Skip to content

Commit

Permalink
chore(i18n): refactor and cleanup (jackyzha0#805)
Browse files Browse the repository at this point in the history
* checkpoint

* finish

* docs
  • Loading branch information
jackyzha0 authored Feb 5, 2024
1 parent dff4b06 commit 36e4cc4
Show file tree
Hide file tree
Showing 37 changed files with 326 additions and 211 deletions.
2 changes: 1 addition & 1 deletion docs/advanced/making plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ export const ContentPage: QuartzEmitterPlugin = () => {
allFiles,
}

const content = renderPage(slug, componentData, opts, externalResources)
const content = renderPage(cfg, slug, componentData, opts, externalResources)
const fp = await emit({
content,
slug: file.data.slug!,
Expand Down
1 change: 1 addition & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ This part of the configuration concerns anything that can affect the whole site.
- `null`: don't use analytics;
- `{ provider: 'plausible' }`: use [Plausible](https://plausible.io/), a privacy-friendly alternative to Google Analytics; or
- `{ provider: 'google', tagId: <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.
- 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.
Expand Down
18 changes: 18 additions & 0 deletions docs/features/i18n.md
Original file line number Diff line number Diff line change
@@ -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`.
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ If you prefer instructions in a video format you can try following Nicole van de

## 🔧 Features

- [[Obsidian compatibility]], [[full-text search]], [[graph view]], note transclusion, [[wikilinks]], [[backlinks]], [[Latex]], [[syntax highlighting]], [[popover previews]], [[Docker Support]], and [many more](./features) right out of the box
- [[Obsidian compatibility]], [[full-text search]], [[graph view]], note transclusion, [[wikilinks]], [[backlinks]], [[Latex]], [[syntax highlighting]], [[popover previews]], [[Docker Support]], [[i18n|internationalization]] and [many more](./features) right out of the box
- Hot-reload for both configuration and content
- Simple JSX layouts and [[creating components|page components]]
- [[SPA Routing|Ridiculously fast page loads]] and tiny bundle sizes
Expand Down
8 changes: 6 additions & 2 deletions quartz/cfg.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ValidDateType } from "./components/Date"
import { QuartzComponent } from "./components/types"
import { ValidLocale } from "./i18n"
import { PluginTypes } from "./plugins/types"
import { Theme } from "./util/theme"

Expand Down Expand Up @@ -39,9 +40,12 @@ export interface GlobalConfiguration {
/**
* Allow to translate the date in the language of your choice.
* Also used for UI translation (default: en-US)
* Need to be formated following the IETF language tag format (https://en.wikipedia.org/wiki/IETF_language_tag)
* Need to be formated following BCP 47: https://en.wikipedia.org/wiki/IETF_language_tag
* The first part is the language (en) and the second part is the script/region (US)
* Language Codes: https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes
* Region Codes: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
*/
locale?: string
locale: ValidLocale
}

export interface QuartzConfig {
Expand Down
1 change: 1 addition & 0 deletions quartz/components/ArticleTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ function ArticleTitle({ fileData, displayClass }: QuartzComponentProps) {
return null
}
}

ArticleTitle.css = `
.article-title {
margin: 2rem 0 0 0;
Expand Down
6 changes: 3 additions & 3 deletions quartz/components/Backlinks.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import style from "./styles/backlinks.scss"
import { resolveRelative, simplifySlug } from "../util/path"
import { i18n } from "../i18n/i18next"
import { i18n } from "../i18n"
import { classNames } from "../util/lang"

function Backlinks({ fileData, allFiles, displayClass, cfg }: QuartzComponentProps) {
const slug = simplifySlug(fileData.slug!)
const backlinkFiles = allFiles.filter((file) => file.links?.includes(slug))
return (
<div class={classNames(displayClass, "backlinks")}>
<h3>{i18n(cfg.locale, "backlinks.backlinks")}</h3>
<h3>{i18n(cfg.locale).components.backlinks.title}</h3>
<ul class="overflow">
{backlinkFiles.length > 0 ? (
backlinkFiles.map((f) => (
Expand All @@ -20,7 +20,7 @@ function Backlinks({ fileData, allFiles, displayClass, cfg }: QuartzComponentPro
</li>
))
) : (
<li>{i18n(cfg.locale, "backlinks.noBacklinksFound")}</li>
<li>{i18n(cfg.locale).components.backlinks.noBacklinksFound}</li>
)}
</ul>
</div>
Expand Down
6 changes: 3 additions & 3 deletions quartz/components/Darkmode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import darkmodeScript from "./scripts/darkmode.inline"
import styles from "./styles/darkmode.scss"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { i18n } from "../i18n/i18next"
import { i18n } from "../i18n"
import { classNames } from "../util/lang"

function Darkmode({ displayClass, cfg }: QuartzComponentProps) {
Expand All @@ -23,7 +23,7 @@ function Darkmode({ displayClass, cfg }: QuartzComponentProps) {
style="enable-background:new 0 0 35 35"
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>
</svg>
</label>
Expand All @@ -39,7 +39,7 @@ function Darkmode({ displayClass, cfg }: QuartzComponentProps) {
style="enable-background:new 0 0 100 100"
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>
</svg>
</label>
Expand Down
5 changes: 3 additions & 2 deletions quartz/components/Date.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { GlobalConfiguration } from "../cfg"
import { ValidLocale } from "../i18n"
import { QuartzPluginData } from "../plugins/vfile"

interface Props {
date: Date
locale?: string
locale?: ValidLocale
}

export type ValidDateType = keyof Required<QuartzPluginData>["dates"]
Expand All @@ -17,7 +18,7 @@ export function getDate(cfg: GlobalConfiguration, data: QuartzPluginData): Date
return data.dates?.[cfg.defaultDateType]
}

export function formatDate(d: Date, locale = "en-US"): string {
export function formatDate(d: Date, locale: ValidLocale = "en-US"): string {
return d.toLocaleDateString(locale, {
year: "numeric",
month: "short",
Expand Down
6 changes: 3 additions & 3 deletions quartz/components/Explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import script from "./scripts/explorer.inline"
import { ExplorerNode, FileNode, Options } from "./ExplorerNode"
import { QuartzPluginData } from "../plugins/vfile"
import { classNames } from "../util/lang"
import { i18n } from "../i18n"

// Options interface defined in `ExplorerNode` to avoid circular dependency
const defaultOptions = {
title: "Explorer",
folderClickBehavior: "collapse",
folderDefaultState: "collapsed",
useSavedState: true,
Expand Down Expand Up @@ -75,7 +75,7 @@ export default ((userOpts?: Partial<Options>) => {
jsonTree = JSON.stringify(folders)
}

function Explorer({ allFiles, displayClass, fileData }: QuartzComponentProps) {
function Explorer({ cfg, allFiles, displayClass, fileData }: QuartzComponentProps) {
constructFileTree(allFiles)
return (
<div class={classNames(displayClass, "explorer")}>
Expand All @@ -87,7 +87,7 @@ export default ((userOpts?: Partial<Options>) => {
data-savestate={opts.useSavedState}
data-tree={jsonTree}
>
<h1>{opts.title}</h1>
<h1>{opts.title ?? i18n(cfg.locale).components.explorer.title}</h1>
<svg
xmlns="http://www.w3.org/2000/svg"
width="14"
Expand Down
2 changes: 1 addition & 1 deletion quartz/components/ExplorerNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
type OrderEntries = "sort" | "filter" | "map"

export interface Options {
title: string
title?: string
folderDefaultState: "collapsed" | "open"
folderClickBehavior: "collapse" | "link"
useSavedState: boolean
Expand Down
6 changes: 3 additions & 3 deletions quartz/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import style from "./styles/footer.scss"
import { version } from "../../package.json"
import { i18n } from "../i18n/i18next"
import { i18n } from "../i18n"

interface Options {
links: Record<string, string>
Expand All @@ -15,8 +15,8 @@ export default ((opts?: Options) => {
<footer class={`${displayClass ?? ""}`}>
<hr />
<p>
{i18n(cfg.locale, "footer.createdWith")}{" "}
<a href="https://quartz.jzhao.xyz/">Quartz v{version}</a>, © {year}
{i18n(cfg.locale).components.footer.createdWith}{" "}
<a href="https://quartz.jzhao.xyz/">Quartz v{version}</a> © {year}
</p>
<ul>
{Object.entries(links).map(([text, link]) => (
Expand Down
4 changes: 2 additions & 2 deletions quartz/components/Graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
// @ts-ignore
import script from "./scripts/graph.inline"
import style from "./styles/graph.scss"
import { i18n } from "../i18n/i18next"
import { i18n } from "../i18n"
import { classNames } from "../util/lang"

export interface D3Config {
Expand Down Expand Up @@ -59,7 +59,7 @@ export default ((opts?: GraphOptions) => {
const globalGraph = { ...defaultOptions.globalGraph, ...opts?.globalGraph }
return (
<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 id="graph-container" data-cfg={JSON.stringify(localGraph)}></div>
<svg
Expand Down
6 changes: 3 additions & 3 deletions quartz/components/Head.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { i18n } from "../i18n/i18next"
import { i18n } from "../i18n"
import { FullSlug, _stripSlashes, joinSegments, pathToRoot } from "../util/path"
import { JSResourceToScriptElement } from "../util/resources"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"

export default (() => {
function Head({ cfg, fileData, externalResources }: QuartzComponentProps) {
const title = fileData.frontmatter?.title ?? i18n(cfg.locale, "head.untitled")
const title = fileData.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title
const description =
fileData.description?.trim() ?? i18n(cfg.locale, "head.noDescriptionProvided")
fileData.description?.trim() ?? i18n(cfg.locale).propertyDefaults.description
const { css, js } = externalResources

const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`)
Expand Down
3 changes: 2 additions & 1 deletion quartz/components/PageTitle.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { pathToRoot } from "../util/path"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { classNames } from "../util/lang"
import { i18n } from "../i18n"

function PageTitle({ fileData, cfg, displayClass }: QuartzComponentProps) {
const title = cfg?.pageTitle ?? "Untitled Quartz"
const title = cfg?.pageTitle ?? i18n(cfg.locale).propertyDefaults.title
const baseDir = pathToRoot(fileData.slug!)
return (
<h1 class={classNames(displayClass, "page-title")}>
Expand Down
15 changes: 5 additions & 10 deletions quartz/components/RecentNotes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,18 @@ import { byDateAndAlphabetical } from "./PageList"
import style from "./styles/recentNotes.scss"
import { Date, getDate } from "./Date"
import { GlobalConfiguration } from "../cfg"
import { i18n } from "../i18n/i18next"
import { i18n } from "../i18n"
import { classNames } from "../util/lang"

interface Options {
title: string
title?: string
limit: number
linkToMore: SimpleSlug | false
filter: (f: QuartzPluginData) => boolean
sort: (f1: QuartzPluginData, f2: QuartzPluginData) => number
}

const defaultOptions = (cfg: GlobalConfiguration): Options => ({
title: "Recent Notes",
limit: 3,
linkToMore: false,
filter: () => true,
Expand All @@ -31,10 +30,10 @@ export default ((userOpts?: Partial<Options>) => {
const remaining = Math.max(0, pages.length - opts.limit)
return (
<div class={classNames(displayClass, "recent-notes")}>
<h3>{opts.title}</h3>
<h3>{opts.title ?? i18n(cfg.locale).components.recentNotes.title}</h3>
<ul class="recent-ul">
{pages.slice(0, opts.limit).map((page) => {
const title = page.frontmatter?.title
const title = page.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title
const tags = page.frontmatter?.tags ?? []

return (
Expand Down Expand Up @@ -72,11 +71,7 @@ export default ((userOpts?: Partial<Options>) => {
{opts.linkToMore && remaining > 0 && (
<p>
<a href={resolveRelative(fileData.slug!, opts.linkToMore)}>
{" "}
{i18n(cfg.locale, "recentNotes.seeRemainingMore", {
remaining: remaining.toString(),
})}{" "}
{i18n(cfg.locale).components.recentNotes.seeRemainingMore({ remaining })}
</a>
</p>
)}
Expand Down
10 changes: 5 additions & 5 deletions quartz/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import style from "./styles/search.scss"
// @ts-ignore
import script from "./scripts/search.inline"
import { classNames } from "../util/lang"
import { i18n } from "../i18n/i18next"
import { i18n } from "../i18n"

export interface SearchOptions {
enablePreview: boolean
Expand All @@ -16,11 +16,11 @@ const defaultOptions: SearchOptions = {
export default ((userOpts?: Partial<SearchOptions>) => {
function Search({ displayClass, cfg }: QuartzComponentProps) {
const opts = { ...defaultOptions, ...userOpts }

const searchPlaceholder = i18n(cfg.locale).components.search.searchBarPlaceholder
return (
<div class={classNames(displayClass, "search")}>
<div id="search-icon">
<p>{i18n(cfg.locale, "search")}</p>
<p>{i18n(cfg.locale).components.search.title}</p>
<div></div>
<svg
tabIndex={0}
Expand All @@ -44,8 +44,8 @@ export default ((userOpts?: Partial<SearchOptions>) => {
id="search-bar"
name="search"
type="text"
aria-label="Search for something"
placeholder="Search for something"
aria-label={searchPlaceholder}
placeholder={searchPlaceholder}
/>
<div id="search-layout" data-preview={opts.enablePreview}></div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions quartz/components/TableOfContents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { classNames } from "../util/lang"

// @ts-ignore
import script from "./scripts/toc.inline"
import { i18n } from "../i18n/i18next"
import { i18n } from "../i18n"

interface Options {
layout: "modern" | "legacy"
Expand All @@ -23,7 +23,7 @@ function TableOfContents({ fileData, displayClass, cfg }: QuartzComponentProps)
return (
<div class={classNames(displayClass, "toc")}>
<button type="button" id="toc" class={fileData.collapseToc ? "collapsed" : ""}>
<h3>{i18n(cfg.locale, "tableOfContent")}</h3>
<h3>{i18n(cfg.locale).components.tableOfContents.title}</h3>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
Expand Down Expand Up @@ -63,7 +63,7 @@ function LegacyTableOfContents({ fileData, cfg }: QuartzComponentProps) {
return (
<details id="toc" open={!fileData.collapseToc}>
<summary>
<h3>{i18n(cfg.locale, "tableOfContent")}</h3>
<h3>{i18n(cfg.locale).components.tableOfContents.title}</h3>
</summary>
<ul>
{fileData.toc.map((tocEntry) => (
Expand Down
4 changes: 2 additions & 2 deletions quartz/components/pages/404.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { i18n } from "../../i18n/i18next"
import { i18n } from "../../i18n"
import { QuartzComponentConstructor, QuartzComponentProps } from "../types"

function NotFound({ cfg }: QuartzComponentProps) {
return (
<article class="popover-hint">
<h1>404</h1>
<p>{i18n(cfg.locale, "404")}</p>
<p>{i18n(cfg.locale).pages.error.notFound}</p>
</article>
)
}
Expand Down
Loading

0 comments on commit 36e4cc4

Please sign in to comment.