mirror of
https://github.com/jackyzha0/quartz.git
synced 2026-03-21 21:45:42 -05:00
Merge 3584ac9e95 into 59b5807601
This commit is contained in:
commit
df449ccb36
22
docs/features/recent changes.md
Normal file
22
docs/features/recent changes.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: Recent Changes
|
||||||
|
tags: component
|
||||||
|
---
|
||||||
|
|
||||||
|
Quartz can generate an activity feed showing recently created and modified notes, with tab-based filtering and pagination. Though this component isn't included in any [[layout]] by default, you can add it by using `Component.RecentChanges` in `quartz.layout.ts`.
|
||||||
|
|
||||||
|
## Customization
|
||||||
|
|
||||||
|
- Changing the title from "Recent Changes": pass in an additional parameter to `Component.RecentChanges({ title: "Latest updates" })`
|
||||||
|
- Changing the number of items shown (without filter tabs): `Component.RecentChanges({ limit: 5 })`
|
||||||
|
- Show only new notes (hide modified): `Component.RecentChanges({ showModified: false })`
|
||||||
|
- Show only modified notes (hide new): `Component.RecentChanges({ showCreated: false })`
|
||||||
|
- Enable tab filter UI (All / New / Updated) with Load More pagination: `Component.RecentChanges({ showFilter: true })`
|
||||||
|
- Set the number of items loaded per tab or per "Load more" click: `Component.RecentChanges({ pageSize: 10 })`
|
||||||
|
- Show note excerpts (requires `detailed: true`): `Component.RecentChanges({ detailed: true, showExcerpt: true })`
|
||||||
|
- Show tags (requires `detailed: true`): `Component.RecentChanges({ detailed: true, showTags: true })`
|
||||||
|
- Restrict to notes under a specific path: `Component.RecentChanges({ filterBy: ["blog/"] })`
|
||||||
|
- Show a 'see all' link: pass in an additional parameter to `Component.RecentChanges({ linkToMore: "recent-changes" })`. This field should be a full slug to a page that exists.
|
||||||
|
- Restrict rendering to specific pages: `Component.RecentChanges({ pages: ["index" as FullSlug] })`. Useful when placing the component in a global layout but only wanting it to appear on certain pages.
|
||||||
|
- Component: `quartz/components/RecentChanges.tsx`
|
||||||
|
- Style: `quartz/components/styles/recentChanges.scss`
|
||||||
262
quartz/components/RecentChanges.tsx
Normal file
262
quartz/components/RecentChanges.tsx
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
|
import { FullSlug, SimpleSlug, resolveRelative } from "../util/path"
|
||||||
|
import { QuartzPluginData } from "../plugins/vfile"
|
||||||
|
import { classNames } from "../util/lang"
|
||||||
|
import { i18n } from "../i18n"
|
||||||
|
import { GlobalConfiguration } from "../cfg"
|
||||||
|
import style from "./styles/recentChanges.scss"
|
||||||
|
import { ChangedItem } from "./utils/recentChanges"
|
||||||
|
// @ts-ignore
|
||||||
|
import script from "./scripts/recentChanges.inline"
|
||||||
|
|
||||||
|
interface Options {
|
||||||
|
title?: string
|
||||||
|
limit: number
|
||||||
|
showCreated: boolean
|
||||||
|
showModified: boolean
|
||||||
|
detailed: boolean
|
||||||
|
filterBy: string[]
|
||||||
|
showExcerpt: boolean
|
||||||
|
showTags: boolean
|
||||||
|
showFilter: boolean
|
||||||
|
pageSize: number
|
||||||
|
linkToMore: SimpleSlug | false
|
||||||
|
pages: FullSlug[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultOptions = (_cfg: GlobalConfiguration): Options => ({
|
||||||
|
limit: 10,
|
||||||
|
showCreated: true,
|
||||||
|
showModified: true,
|
||||||
|
detailed: false,
|
||||||
|
filterBy: [],
|
||||||
|
showExcerpt: false,
|
||||||
|
showTags: false,
|
||||||
|
showFilter: false,
|
||||||
|
pageSize: 20,
|
||||||
|
linkToMore: false,
|
||||||
|
pages: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
function formatRelativeDate(date: Date, locale: string): string {
|
||||||
|
const diffMs = Date.now() - date.getTime()
|
||||||
|
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" })
|
||||||
|
const abs = (n: number) => Math.abs(n)
|
||||||
|
const s = Math.round(diffMs / 1000)
|
||||||
|
const m = Math.round(diffMs / 60000)
|
||||||
|
const h = Math.round(diffMs / 3600000)
|
||||||
|
const d = Math.round(diffMs / 86400000)
|
||||||
|
const w = Math.round(diffMs / 604800000)
|
||||||
|
const mo = Math.round(diffMs / 2592000000)
|
||||||
|
const y = Math.round(diffMs / 31536000000)
|
||||||
|
if (abs(s) < 60) return rtf.format(-s, "second")
|
||||||
|
if (abs(m) < 60) return rtf.format(-m, "minute")
|
||||||
|
if (abs(h) < 24) return rtf.format(-h, "hour")
|
||||||
|
if (abs(d) < 7) return rtf.format(-d, "day")
|
||||||
|
if (abs(w) < 4) return rtf.format(-w, "week")
|
||||||
|
if (abs(mo) < 12) return rtf.format(-mo, "month")
|
||||||
|
return rtf.format(-y, "year")
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ((userOpts?: Partial<Options>) => {
|
||||||
|
const RecentChanges: QuartzComponent = ({
|
||||||
|
allFiles,
|
||||||
|
fileData,
|
||||||
|
displayClass,
|
||||||
|
cfg,
|
||||||
|
}: QuartzComponentProps) => {
|
||||||
|
const opts = { ...defaultOptions(cfg), ...userOpts }
|
||||||
|
const t = i18n(cfg.locale).components.recentChanges
|
||||||
|
|
||||||
|
// Only render on specified pages (empty = all pages)
|
||||||
|
if (opts.pages.length > 0 && !opts.pages.some((p) => fileData.slug === p)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter files with valid dates
|
||||||
|
const validFiles = allFiles.filter(
|
||||||
|
(file: QuartzPluginData) => file.dates && (file.dates.created || file.dates.modified),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sort by most recent date
|
||||||
|
const sortedFiles = validFiles.sort((a: QuartzPluginData, b: QuartzPluginData) => {
|
||||||
|
const dateA = a.dates?.modified || a.dates?.created || new Date(0)
|
||||||
|
const dateB = b.dates?.modified || b.dates?.created || new Date(0)
|
||||||
|
return dateB.getTime() - dateA.getTime()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Convert to ChangedItems
|
||||||
|
const allItems: ChangedItem[] = sortedFiles.map((file: QuartzPluginData) => {
|
||||||
|
const created = file.dates?.created
|
||||||
|
const modified = file.dates?.modified
|
||||||
|
|
||||||
|
// A note is "Updated" only if its creation date predates the last modification by >1h.
|
||||||
|
const isModified =
|
||||||
|
created !== undefined &&
|
||||||
|
modified !== undefined &&
|
||||||
|
modified.getTime() - created.getTime() > 60 * 60 * 1000
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: file.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title,
|
||||||
|
link: file.slug as FullSlug,
|
||||||
|
date: file.dates?.modified || file.dates?.created || new Date(),
|
||||||
|
createdDate: file.dates?.created ?? file.dates?.modified ?? new Date(),
|
||||||
|
type: isModified ? "modified" : "created",
|
||||||
|
id: `${file.slug}-${isModified ? "modified" : "created"}`,
|
||||||
|
excerpt: file.frontmatter?.description,
|
||||||
|
tags: file.frontmatter?.tags,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Apply type filters
|
||||||
|
let filtered = allItems
|
||||||
|
if (!opts.showCreated) {
|
||||||
|
filtered = filtered.filter((item) => item.type !== "created")
|
||||||
|
}
|
||||||
|
if (!opts.showModified) {
|
||||||
|
filtered = filtered.filter((item) => item.type !== "modified")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply path filters
|
||||||
|
if (opts.filterBy.length > 0) {
|
||||||
|
filtered = filtered.filter((item) => opts.filterBy.some((f) => item.link.includes(f)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const remaining = Math.max(0, filtered.length - opts.limit)
|
||||||
|
|
||||||
|
// JSON data island: all items as compact JSON for progressive client-side injection.
|
||||||
|
// Links are pre-resolved server-side since the client cannot call resolveRelative.
|
||||||
|
const allItemsJson = JSON.stringify(
|
||||||
|
filtered.map((item) => ({
|
||||||
|
title: item.title,
|
||||||
|
link: resolveRelative(fileData.slug!, item.link),
|
||||||
|
date: item.date.getTime(),
|
||||||
|
created: item.createdDate.getTime(),
|
||||||
|
type: item.type,
|
||||||
|
...(opts.showExcerpt && item.excerpt ? { excerpt: item.excerpt } : {}),
|
||||||
|
...(opts.showTags && item.tags?.length ? { tags: item.tags } : {}),
|
||||||
|
})),
|
||||||
|
).replace(/<\//g, "<\\/")
|
||||||
|
|
||||||
|
// For showFilter: pre-render pageSize items per type so both "New" and "Updated" tabs
|
||||||
|
// start with visible content. Each item is tagged with its index in filtered (= allData)
|
||||||
|
// via data-idx so the client can correctly initialize its deduplication set.
|
||||||
|
// For !showFilter: flat limit cap, no Load More.
|
||||||
|
const filteredWithIdx = filtered.map((item, idx) => ({ item, idx }))
|
||||||
|
const initialItems = opts.showFilter
|
||||||
|
? [
|
||||||
|
...filteredWithIdx.filter(({ item }) => item.type === "created").slice(0, opts.pageSize),
|
||||||
|
...filteredWithIdx.filter(({ item }) => item.type === "modified").slice(0, opts.pageSize),
|
||||||
|
].sort((a, b) => b.item.date.getTime() - a.item.date.getTime())
|
||||||
|
: filteredWithIdx.slice(0, opts.limit)
|
||||||
|
|
||||||
|
// i18n strings passed to the client-side script via data attributes
|
||||||
|
const i18nData = JSON.stringify({
|
||||||
|
badgeNew: t.badgeNew,
|
||||||
|
badgeUpdated: t.badgeUpdated,
|
||||||
|
noChanges: t.noChanges,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class={classNames(displayClass, "recent-changes")}
|
||||||
|
data-page-size={opts.pageSize}
|
||||||
|
data-detailed={opts.detailed ? "1" : "0"}
|
||||||
|
data-show-excerpt={opts.showExcerpt ? "1" : "0"}
|
||||||
|
data-show-tags={opts.showTags ? "1" : "0"}
|
||||||
|
data-locale={cfg.locale}
|
||||||
|
data-i18n={i18nData}
|
||||||
|
data-load-more-tpl={t.loadMoreTemplate}
|
||||||
|
>
|
||||||
|
<h3>
|
||||||
|
{opts.linkToMore ? (
|
||||||
|
<a href={resolveRelative(fileData.slug!, opts.linkToMore)}>{opts.title ?? t.title}</a>
|
||||||
|
) : (
|
||||||
|
(opts.title ?? t.title)
|
||||||
|
)}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
{opts.showFilter && (
|
||||||
|
<div class="recent-changes-filter" role="group" aria-label="Filter changes">
|
||||||
|
<button data-filter="all" class="active">
|
||||||
|
{t.filterAll}
|
||||||
|
</button>
|
||||||
|
<button data-filter="created">{t.filterNew}</button>
|
||||||
|
<button data-filter="modified">{t.filterUpdated}</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{opts.showFilter && <p class="rc-tab-desc" aria-live="polite"></p>}
|
||||||
|
|
||||||
|
{filtered.length === 0 ? (
|
||||||
|
<p>{t.noChanges}</p>
|
||||||
|
) : (
|
||||||
|
<ul class={`recent-changes-list ${opts.detailed ? "detailed" : "condensed"}`}>
|
||||||
|
{initialItems.map(({ item, idx }) => (
|
||||||
|
<li
|
||||||
|
key={item.id}
|
||||||
|
class={`recent-change-item ${item.type}`}
|
||||||
|
data-type={item.type}
|
||||||
|
data-idx={idx}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href={resolveRelative(fileData.slug!, item.link)}
|
||||||
|
class="recent-change-link internal"
|
||||||
|
>
|
||||||
|
<span class="recent-change-title">{item.title}</span>
|
||||||
|
</a>
|
||||||
|
<div class="recent-change-meta">
|
||||||
|
<span class="recent-change-type">
|
||||||
|
{item.type === "created" ? t.badgeNew : t.badgeUpdated}
|
||||||
|
</span>
|
||||||
|
<span class="recent-change-date" data-timestamp={item.date.getTime().toString()}>
|
||||||
|
{formatRelativeDate(item.date, cfg.locale)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{opts.detailed && opts.showExcerpt && item.excerpt && (
|
||||||
|
<p class="recent-change-excerpt">{item.excerpt}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{opts.detailed && opts.showTags && item.tags && item.tags.length > 0 && (
|
||||||
|
<div class="recent-change-tags">
|
||||||
|
{item.tags.map((tag) => (
|
||||||
|
<span key={tag} class="recent-change-tag">
|
||||||
|
{tag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{opts.showFilter && (
|
||||||
|
// @ts-ignore — dangerouslySetInnerHTML on <script> is valid in Preact
|
||||||
|
<script
|
||||||
|
type="application/json"
|
||||||
|
class="rc-items-data"
|
||||||
|
dangerouslySetInnerHTML={{ __html: allItemsJson }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{opts.showFilter && filtered.length > 0 && (
|
||||||
|
<button class="recent-changes-load-more" style="display:none">
|
||||||
|
Load more
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!opts.showFilter && opts.linkToMore && remaining > 0 && (
|
||||||
|
<div class="recent-changes-more">
|
||||||
|
<a href={resolveRelative(fileData.slug!, opts.linkToMore)}>View all changes →</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
RecentChanges.css = style
|
||||||
|
RecentChanges.afterDOMLoaded = script
|
||||||
|
return RecentChanges
|
||||||
|
}) satisfies QuartzComponentConstructor
|
||||||
@ -23,6 +23,7 @@ import Breadcrumbs from "./Breadcrumbs"
|
|||||||
import Comments from "./Comments"
|
import Comments from "./Comments"
|
||||||
import Flex from "./Flex"
|
import Flex from "./Flex"
|
||||||
import ConditionalRender from "./ConditionalRender"
|
import ConditionalRender from "./ConditionalRender"
|
||||||
|
import RecentChanges from "./RecentChanges"
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ArticleTitle,
|
ArticleTitle,
|
||||||
@ -50,4 +51,5 @@ export {
|
|||||||
Comments,
|
Comments,
|
||||||
Flex,
|
Flex,
|
||||||
ConditionalRender,
|
ConditionalRender,
|
||||||
|
RecentChanges,
|
||||||
}
|
}
|
||||||
|
|||||||
237
quartz/components/scripts/recentChanges.inline.ts
Normal file
237
quartz/components/scripts/recentChanges.inline.ts
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
function formatRelativeDate(date: Date, locale: string): string {
|
||||||
|
const diffMs = Date.now() - date.getTime()
|
||||||
|
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" })
|
||||||
|
const abs = (n: number) => Math.abs(n)
|
||||||
|
const s = Math.round(diffMs / 1000)
|
||||||
|
const m = Math.round(diffMs / 60000)
|
||||||
|
const h = Math.round(diffMs / 3600000)
|
||||||
|
const d = Math.round(diffMs / 86400000)
|
||||||
|
const w = Math.round(diffMs / 604800000)
|
||||||
|
const mo = Math.round(diffMs / 2592000000)
|
||||||
|
const y = Math.round(diffMs / 31536000000)
|
||||||
|
if (abs(s) < 60) return rtf.format(-s, "second")
|
||||||
|
if (abs(m) < 60) return rtf.format(-m, "minute")
|
||||||
|
if (abs(h) < 24) return rtf.format(-h, "hour")
|
||||||
|
if (abs(d) < 7) return rtf.format(-d, "day")
|
||||||
|
if (abs(w) < 4) return rtf.format(-w, "week")
|
||||||
|
if (abs(mo) < 12) return rtf.format(-mo, "month")
|
||||||
|
return rtf.format(-y, "year")
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RcItemJson {
|
||||||
|
title: string
|
||||||
|
link: string
|
||||||
|
date: number // most-recent-activity timestamp (ms)
|
||||||
|
created: number // creation date timestamp (ms)
|
||||||
|
type: "created" | "modified"
|
||||||
|
excerpt?: string
|
||||||
|
tags?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RcI18n {
|
||||||
|
badgeNew: string
|
||||||
|
badgeUpdated: string
|
||||||
|
noChanges: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupRecentChanges() {
|
||||||
|
// Read page locale from <html lang="..."> (set by Quartz from cfg.locale)
|
||||||
|
const pageLocale = document.documentElement.lang || "en-US"
|
||||||
|
|
||||||
|
// Refresh relative dates on all pre-rendered items
|
||||||
|
const dateEls = document.querySelectorAll<HTMLElement>(".recent-change-date[data-timestamp]")
|
||||||
|
dateEls.forEach((el) => {
|
||||||
|
const ts = parseInt(el.dataset.timestamp!, 10)
|
||||||
|
if (!isNaN(ts)) {
|
||||||
|
el.textContent = formatRelativeDate(new Date(ts), pageLocale)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const containers = document.querySelectorAll<HTMLElement>(".recent-changes")
|
||||||
|
containers.forEach((container) => {
|
||||||
|
const filterGroup = container.querySelector<HTMLElement>(".recent-changes-filter")
|
||||||
|
if (!filterGroup) return
|
||||||
|
|
||||||
|
const locale = container.dataset.locale ?? pageLocale
|
||||||
|
const pageSize = parseInt(container.dataset.pageSize ?? "20", 10)
|
||||||
|
const isDetailed = container.dataset.detailed === "1"
|
||||||
|
const showExcerpt = container.dataset.showExcerpt === "1"
|
||||||
|
const showTags = container.dataset.showTags === "1"
|
||||||
|
const loadMoreTpl = container.dataset.loadMoreTpl ?? "Load {count} more · {remaining} remaining"
|
||||||
|
const i18nData: RcI18n = JSON.parse(container.dataset.i18n ?? "{}")
|
||||||
|
|
||||||
|
const list = container.querySelector<HTMLUListElement>(".recent-changes-list")
|
||||||
|
const loadMoreBtn = container.querySelector<HTMLButtonElement>(".recent-changes-load-more")
|
||||||
|
const tabDesc = container.querySelector<HTMLParagraphElement>(".rc-tab-desc")
|
||||||
|
|
||||||
|
if (!list) return
|
||||||
|
const safeList = list
|
||||||
|
|
||||||
|
const dataScript = container.querySelector<HTMLScriptElement>(".rc-items-data")
|
||||||
|
if (!dataScript) return
|
||||||
|
|
||||||
|
let allData: RcItemJson[]
|
||||||
|
try {
|
||||||
|
allData = JSON.parse(dataScript.textContent ?? "[]")
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Three sort views:
|
||||||
|
// "all" → all notes by most recent activity
|
||||||
|
// "created" → ALL notes by creation date (the "New" tab)
|
||||||
|
// "modified" → only modified notes by modification date
|
||||||
|
const sortedArrays: Record<string, RcItemJson[]> = {
|
||||||
|
all: [...allData].sort((a, b) => b.date - a.date),
|
||||||
|
created: [...allData].sort((a, b) => b.created - a.created),
|
||||||
|
modified: allData.filter((x) => x.type === "modified").sort((a, b) => b.date - a.date),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Human-readable descriptions for each tab
|
||||||
|
const tabDescriptions: Record<string, string> = {
|
||||||
|
all: `All ${allData.length} notes · most recent activity first`,
|
||||||
|
created: `All ${allData.length} notes · sorted by when they were added`,
|
||||||
|
modified: `${sortedArrays.modified.length} revised notes · latest changes first`,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-tab injection pointer
|
||||||
|
const injectedCount: Record<string, number> = { all: 0, created: 0, modified: 0 }
|
||||||
|
|
||||||
|
let currentFilter = localStorage.getItem("recent-changes-filter") ?? "all"
|
||||||
|
|
||||||
|
function createItemEl(item: RcItemJson, filter: string): HTMLLIElement {
|
||||||
|
const li = document.createElement("li")
|
||||||
|
li.className = `recent-change-item ${item.type}`
|
||||||
|
li.dataset.type = item.type
|
||||||
|
|
||||||
|
const a = document.createElement("a")
|
||||||
|
a.href = item.link
|
||||||
|
a.className = "recent-change-link internal"
|
||||||
|
const titleSpan = document.createElement("span")
|
||||||
|
titleSpan.className = "recent-change-title"
|
||||||
|
titleSpan.textContent = item.title
|
||||||
|
a.appendChild(titleSpan)
|
||||||
|
li.appendChild(a)
|
||||||
|
|
||||||
|
const meta = document.createElement("div")
|
||||||
|
meta.className = "recent-change-meta"
|
||||||
|
|
||||||
|
const typeSpan = document.createElement("span")
|
||||||
|
typeSpan.className = "recent-change-type"
|
||||||
|
typeSpan.textContent =
|
||||||
|
item.type === "created" ? (i18nData.badgeNew ?? "New") : (i18nData.badgeUpdated ?? "Edited")
|
||||||
|
meta.appendChild(typeSpan)
|
||||||
|
|
||||||
|
// Use creation timestamp for the "New" tab, activity timestamp otherwise
|
||||||
|
const ts = filter === "created" ? item.created : item.date
|
||||||
|
const dateSpan = document.createElement("span")
|
||||||
|
dateSpan.className = "recent-change-date"
|
||||||
|
dateSpan.dataset.timestamp = ts.toString()
|
||||||
|
dateSpan.textContent = formatRelativeDate(new Date(ts), locale)
|
||||||
|
meta.appendChild(dateSpan)
|
||||||
|
|
||||||
|
li.appendChild(meta)
|
||||||
|
|
||||||
|
if (isDetailed && showExcerpt && item.excerpt) {
|
||||||
|
const p = document.createElement("p")
|
||||||
|
p.className = "recent-change-excerpt"
|
||||||
|
p.textContent = item.excerpt
|
||||||
|
li.appendChild(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDetailed && showTags && item.tags?.length) {
|
||||||
|
const tagsDiv = document.createElement("div")
|
||||||
|
tagsDiv.className = "recent-change-tags"
|
||||||
|
item.tags.forEach((tag) => {
|
||||||
|
const tagSpan = document.createElement("span")
|
||||||
|
tagSpan.className = "recent-change-tag"
|
||||||
|
tagSpan.textContent = tag
|
||||||
|
tagsDiv.appendChild(tagSpan)
|
||||||
|
})
|
||||||
|
li.appendChild(tagsDiv)
|
||||||
|
}
|
||||||
|
|
||||||
|
return li
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTabDesc(filter: string) {
|
||||||
|
if (tabDesc) tabDesc.textContent = tabDescriptions[filter] ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLoadMoreBtn(filter: string) {
|
||||||
|
if (!loadMoreBtn) return
|
||||||
|
const arr = sortedArrays[filter] ?? []
|
||||||
|
const loaded = injectedCount[filter]
|
||||||
|
const remaining = arr.length - loaded
|
||||||
|
if (remaining <= 0) {
|
||||||
|
loadMoreBtn.style.display = "none"
|
||||||
|
} else {
|
||||||
|
const count = Math.min(pageSize, remaining)
|
||||||
|
loadMoreBtn.textContent = loadMoreTpl
|
||||||
|
.replace("{count}", String(count))
|
||||||
|
.replace("{remaining}", String(remaining))
|
||||||
|
loadMoreBtn.style.display = "block"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTab(filter: string) {
|
||||||
|
safeList.innerHTML = ""
|
||||||
|
injectedCount[filter] = 0
|
||||||
|
const arr = sortedArrays[filter] ?? []
|
||||||
|
const end = Math.min(pageSize, arr.length)
|
||||||
|
for (let i = 0; i < end; i++) {
|
||||||
|
safeList.appendChild(createItemEl(arr[i], filter))
|
||||||
|
}
|
||||||
|
injectedCount[filter] = end
|
||||||
|
updateTabDesc(filter)
|
||||||
|
updateLoadMoreBtn(filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadMore(filter: string) {
|
||||||
|
const arr = sortedArrays[filter] ?? []
|
||||||
|
const start = injectedCount[filter]
|
||||||
|
const end = Math.min(start + pageSize, arr.length)
|
||||||
|
for (let i = start; i < end; i++) {
|
||||||
|
safeList.appendChild(createItemEl(arr[i], filter))
|
||||||
|
}
|
||||||
|
injectedCount[filter] = end
|
||||||
|
updateLoadMoreBtn(filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttons = filterGroup.querySelectorAll<HTMLButtonElement>("button[data-filter]")
|
||||||
|
buttons.forEach((btn) => {
|
||||||
|
btn.addEventListener("click", () => {
|
||||||
|
currentFilter = btn.dataset.filter ?? "all"
|
||||||
|
localStorage.setItem("recent-changes-filter", currentFilter)
|
||||||
|
buttons.forEach((b) => b.classList.toggle("active", b === btn))
|
||||||
|
renderTab(currentFilter)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (loadMoreBtn) {
|
||||||
|
loadMoreBtn.addEventListener("click", () => loadMore(currentFilter))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore saved filter button active state
|
||||||
|
if (currentFilter !== "all") {
|
||||||
|
const savedBtn = filterGroup.querySelector<HTMLButtonElement>(
|
||||||
|
`button[data-filter="${currentFilter}"]`,
|
||||||
|
)
|
||||||
|
if (savedBtn) {
|
||||||
|
buttons.forEach((b) => b.classList.remove("active"))
|
||||||
|
savedBtn.classList.add("active")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize: keep SSR items if on "all" tab, otherwise rebuild
|
||||||
|
if (currentFilter === "all") {
|
||||||
|
injectedCount.all = safeList.querySelectorAll(".recent-change-item").length
|
||||||
|
updateTabDesc("all")
|
||||||
|
updateLoadMoreBtn("all")
|
||||||
|
} else {
|
||||||
|
renderTab(currentFilter)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("nav", setupRecentChanges)
|
||||||
214
quartz/components/styles/recentChanges.scss
Normal file
214
quartz/components/styles/recentChanges.scss
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
.recent-changes {
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
|
||||||
|
// Badge colors (customizable via CSS custom properties)
|
||||||
|
--rc-created-bg: #b3e6cc;
|
||||||
|
--rc-created-text: #005500;
|
||||||
|
--rc-modified-bg: #d0e0f0;
|
||||||
|
--rc-modified-text: #003366;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
color: var(--secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-changes-filter {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 0.35rem 0.75rem;
|
||||||
|
border: 1px solid var(--lightgray);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--darkgray);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-family: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
transition:
|
||||||
|
background-color 0.15s,
|
||||||
|
color 0.15s;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: var(--secondary);
|
||||||
|
color: var(--light);
|
||||||
|
border-color: var(--secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(.active) {
|
||||||
|
background: var(--lightgray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rc-tab-desc {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
color: var(--gray);
|
||||||
|
margin: -0.5rem 0 0.75rem;
|
||||||
|
min-height: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rc-hidden-filter,
|
||||||
|
.rc-hidden-page {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-changes-load-more {
|
||||||
|
display: block;
|
||||||
|
margin: 1rem auto;
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
border: 1px solid var(--lightgray);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--secondary);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.15s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--lightgray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-changes-list {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
&.detailed {
|
||||||
|
.recent-change-item {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
padding-bottom: 1.2rem;
|
||||||
|
border-bottom: 1px solid var(--lightgray);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-change-excerpt {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--gray);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-change-tags {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.4rem;
|
||||||
|
|
||||||
|
.recent-change-tag {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
padding: 0.2rem 0.5rem;
|
||||||
|
background-color: var(--lightgray);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--darkgray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.condensed {
|
||||||
|
.recent-change-item {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-change-item {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.created {
|
||||||
|
.recent-change-type {
|
||||||
|
background-color: var(--rc-created-bg);
|
||||||
|
color: var(--rc-created-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.modified {
|
||||||
|
.recent-change-type {
|
||||||
|
background-color: var(--rc-modified-bg);
|
||||||
|
color: var(--rc-modified-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-change-link {
|
||||||
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--dark);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: var(--secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-change-meta {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.condensed & {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-change-type {
|
||||||
|
padding: 0.15rem 0.4rem;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-change-date {
|
||||||
|
color: var(--gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-changes-more {
|
||||||
|
margin-top: 1rem;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--secondary);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dark mode adjustments
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.recent-changes {
|
||||||
|
--rc-created-bg: rgba(179, 230, 204, 0.2);
|
||||||
|
--rc-created-text: #b3e6cc;
|
||||||
|
--rc-modified-bg: rgba(208, 224, 240, 0.2);
|
||||||
|
--rc-modified-text: #d0e0f0;
|
||||||
|
|
||||||
|
.recent-changes-list.detailed .recent-change-tags .recent-change-tag {
|
||||||
|
background-color: var(--darkgray);
|
||||||
|
color: var(--light);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
quartz/components/utils/recentChanges.ts
Normal file
12
quartz/components/utils/recentChanges.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { FullSlug } from "../../util/path"
|
||||||
|
|
||||||
|
export interface ChangedItem {
|
||||||
|
title: string
|
||||||
|
link: FullSlug
|
||||||
|
date: Date // most recent activity (used for "All" and "Updated" sort)
|
||||||
|
createdDate: Date // first known date (used for "New" sort)
|
||||||
|
type: "created" | "modified" // display badge only — not a filter
|
||||||
|
excerpt?: string
|
||||||
|
tags?: string[]
|
||||||
|
id: string
|
||||||
|
}
|
||||||
@ -46,6 +46,16 @@ export default {
|
|||||||
title: "آخر الملاحظات",
|
title: "آخر الملاحظات",
|
||||||
seeRemainingMore: ({ remaining }) => `تصفح ${remaining} أكثر →`,
|
seeRemainingMore: ({ remaining }) => `تصفح ${remaining} أكثر →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "التغييرات الأخيرة",
|
||||||
|
filterAll: "الكل",
|
||||||
|
filterNew: "جديد",
|
||||||
|
filterUpdated: "محدَّث",
|
||||||
|
loadMoreTemplate: "تحميل {count} أكثر · {remaining} متبقٍ",
|
||||||
|
noChanges: "لا توجد تغييرات حديثة.",
|
||||||
|
badgeNew: "جديد",
|
||||||
|
badgeUpdated: "تم التعديل",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `مقتبس من ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `مقتبس من ${targetSlug}`,
|
||||||
linkToOriginal: "وصلة للملاحظة الرئيسة",
|
linkToOriginal: "وصلة للملاحظة الرئيسة",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Notes Recents",
|
title: "Notes Recents",
|
||||||
seeRemainingMore: ({ remaining }) => `Vegi ${remaining} més →`,
|
seeRemainingMore: ({ remaining }) => `Vegi ${remaining} més →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Canvis recents",
|
||||||
|
filterAll: "Tots",
|
||||||
|
filterNew: "Nous",
|
||||||
|
filterUpdated: "Actualitzats",
|
||||||
|
loadMoreTemplate: "Carrega {count} més · {remaining} restants",
|
||||||
|
noChanges: "No s'han trobat canvis recents.",
|
||||||
|
badgeNew: "Nou",
|
||||||
|
badgeUpdated: "Editat",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Transcluit de ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transcluit de ${targetSlug}`,
|
||||||
linkToOriginal: "Enllaç a l'original",
|
linkToOriginal: "Enllaç a l'original",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Nejnovější poznámky",
|
title: "Nejnovější poznámky",
|
||||||
seeRemainingMore: ({ remaining }) => `Zobraz ${remaining} dalších →`,
|
seeRemainingMore: ({ remaining }) => `Zobraz ${remaining} dalších →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Nedávné změny",
|
||||||
|
filterAll: "Vše",
|
||||||
|
filterNew: "Nové",
|
||||||
|
filterUpdated: "Aktualizované",
|
||||||
|
loadMoreTemplate: "Načíst {count} dalších · zbývá {remaining}",
|
||||||
|
noChanges: "Nebyly nalezeny žádné nedávné změny.",
|
||||||
|
badgeNew: "Nové",
|
||||||
|
badgeUpdated: "Upraveno",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Zobrazení ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Zobrazení ${targetSlug}`,
|
||||||
linkToOriginal: "Odkaz na původní dokument",
|
linkToOriginal: "Odkaz na původní dokument",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Zuletzt bearbeitete Seiten",
|
title: "Zuletzt bearbeitete Seiten",
|
||||||
seeRemainingMore: ({ remaining }) => `${remaining} weitere ansehen →`,
|
seeRemainingMore: ({ remaining }) => `${remaining} weitere ansehen →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Letzte Änderungen",
|
||||||
|
filterAll: "Alle",
|
||||||
|
filterNew: "Neu",
|
||||||
|
filterUpdated: "Aktualisiert",
|
||||||
|
loadMoreTemplate: "{count} weitere laden · {remaining} verbleibend",
|
||||||
|
noChanges: "Keine aktuellen Änderungen gefunden.",
|
||||||
|
badgeNew: "Neu",
|
||||||
|
badgeUpdated: "Bearbeitet",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Transklusion von ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transklusion von ${targetSlug}`,
|
||||||
linkToOriginal: "Link zum Original",
|
linkToOriginal: "Link zum Original",
|
||||||
|
|||||||
@ -48,6 +48,16 @@ export interface Translation {
|
|||||||
title: string
|
title: string
|
||||||
seeRemainingMore: (variables: { remaining: number }) => string
|
seeRemainingMore: (variables: { remaining: number }) => string
|
||||||
}
|
}
|
||||||
|
recentChanges: {
|
||||||
|
title: string
|
||||||
|
filterAll: string
|
||||||
|
filterNew: string
|
||||||
|
filterUpdated: string
|
||||||
|
loadMoreTemplate: string
|
||||||
|
noChanges: string
|
||||||
|
badgeNew: string
|
||||||
|
badgeUpdated: string
|
||||||
|
}
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: (variables: { targetSlug: FullSlug }) => string
|
transcludeOf: (variables: { targetSlug: FullSlug }) => string
|
||||||
linkToOriginal: string
|
linkToOriginal: string
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Recent Notes",
|
title: "Recent Notes",
|
||||||
seeRemainingMore: ({ remaining }) => `See ${remaining} more →`,
|
seeRemainingMore: ({ remaining }) => `See ${remaining} more →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Recent Changes",
|
||||||
|
filterAll: "All",
|
||||||
|
filterNew: "New",
|
||||||
|
filterUpdated: "Updated",
|
||||||
|
loadMoreTemplate: "Load {count} more · {remaining} remaining",
|
||||||
|
noChanges: "No recent changes found.",
|
||||||
|
badgeNew: "New",
|
||||||
|
badgeUpdated: "Edited",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Transclude of ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transclude of ${targetSlug}`,
|
||||||
linkToOriginal: "Link to original",
|
linkToOriginal: "Link to original",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Recent Notes",
|
title: "Recent Notes",
|
||||||
seeRemainingMore: ({ remaining }) => `See ${remaining} more →`,
|
seeRemainingMore: ({ remaining }) => `See ${remaining} more →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Recent Changes",
|
||||||
|
filterAll: "All",
|
||||||
|
filterNew: "New",
|
||||||
|
filterUpdated: "Updated",
|
||||||
|
loadMoreTemplate: "Load {count} more · {remaining} remaining",
|
||||||
|
noChanges: "No recent changes found.",
|
||||||
|
badgeNew: "New",
|
||||||
|
badgeUpdated: "Edited",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Transclude of ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transclude of ${targetSlug}`,
|
||||||
linkToOriginal: "Link to original",
|
linkToOriginal: "Link to original",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Notas Recientes",
|
title: "Notas Recientes",
|
||||||
seeRemainingMore: ({ remaining }) => `Vea ${remaining} más →`,
|
seeRemainingMore: ({ remaining }) => `Vea ${remaining} más →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Cambios recientes",
|
||||||
|
filterAll: "Todos",
|
||||||
|
filterNew: "Nuevos",
|
||||||
|
filterUpdated: "Actualizados",
|
||||||
|
loadMoreTemplate: "Cargar {count} más · {remaining} restantes",
|
||||||
|
noChanges: "No se encontraron cambios recientes.",
|
||||||
|
badgeNew: "Nuevo",
|
||||||
|
badgeUpdated: "Editado",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Transcluido de ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transcluido de ${targetSlug}`,
|
||||||
linkToOriginal: "Enlace al original",
|
linkToOriginal: "Enlace al original",
|
||||||
|
|||||||
@ -46,6 +46,16 @@ export default {
|
|||||||
title: "یادداشتهای اخیر",
|
title: "یادداشتهای اخیر",
|
||||||
seeRemainingMore: ({ remaining }) => `${remaining} یادداشت دیگر →`,
|
seeRemainingMore: ({ remaining }) => `${remaining} یادداشت دیگر →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "تغییرات اخیر",
|
||||||
|
filterAll: "همه",
|
||||||
|
filterNew: "جدید",
|
||||||
|
filterUpdated: "بهروز شده",
|
||||||
|
loadMoreTemplate: "بارگذاری {count} بیشتر · {remaining} باقیمانده",
|
||||||
|
noChanges: "هیچ تغییر اخیری یافت نشد.",
|
||||||
|
badgeNew: "جدید",
|
||||||
|
badgeUpdated: "ویرایش شده",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `از ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `از ${targetSlug}`,
|
||||||
linkToOriginal: "پیوند به اصلی",
|
linkToOriginal: "پیوند به اصلی",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Viimeisimmät muistiinpanot",
|
title: "Viimeisimmät muistiinpanot",
|
||||||
seeRemainingMore: ({ remaining }) => `Näytä ${remaining} lisää →`,
|
seeRemainingMore: ({ remaining }) => `Näytä ${remaining} lisää →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Viimeisimmät muutokset",
|
||||||
|
filterAll: "Kaikki",
|
||||||
|
filterNew: "Uudet",
|
||||||
|
filterUpdated: "Päivitetyt",
|
||||||
|
loadMoreTemplate: "Lataa {count} lisää · {remaining} jäljellä",
|
||||||
|
noChanges: "Viimeisimpiä muutoksia ei löydy.",
|
||||||
|
badgeNew: "Uusi",
|
||||||
|
badgeUpdated: "Muokattu",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Upote kohteesta ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Upote kohteesta ${targetSlug}`,
|
||||||
linkToOriginal: "Linkki alkuperäiseen",
|
linkToOriginal: "Linkki alkuperäiseen",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Notes Récentes",
|
title: "Notes Récentes",
|
||||||
seeRemainingMore: ({ remaining }) => `Voir ${remaining} de plus →`,
|
seeRemainingMore: ({ remaining }) => `Voir ${remaining} de plus →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Modifications récentes",
|
||||||
|
filterAll: "Tout",
|
||||||
|
filterNew: "Nouveau",
|
||||||
|
filterUpdated: "Mis à jour",
|
||||||
|
loadMoreTemplate: "Charger {count} de plus · {remaining} restants",
|
||||||
|
noChanges: "Aucune modification récente trouvée.",
|
||||||
|
badgeNew: "Nouveau",
|
||||||
|
badgeUpdated: "Modifié",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Transclusion de ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transclusion de ${targetSlug}`,
|
||||||
linkToOriginal: "Lien vers l'original",
|
linkToOriginal: "Lien vers l'original",
|
||||||
|
|||||||
@ -46,6 +46,16 @@ export default {
|
|||||||
title: "הערות אחרונות",
|
title: "הערות אחרונות",
|
||||||
seeRemainingMore: ({ remaining }) => `עיין ב ${remaining} נוספים →`,
|
seeRemainingMore: ({ remaining }) => `עיין ב ${remaining} נוספים →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "שינויים אחרונים",
|
||||||
|
filterAll: "הכל",
|
||||||
|
filterNew: "חדש",
|
||||||
|
filterUpdated: "עודכן",
|
||||||
|
loadMoreTemplate: "טען {count} נוספים · נותרו {remaining}",
|
||||||
|
noChanges: "לא נמצאו שינויים אחרונים.",
|
||||||
|
badgeNew: "חדש",
|
||||||
|
badgeUpdated: "נערך",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `מצוטט מ ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `מצוטט מ ${targetSlug}`,
|
||||||
linkToOriginal: "קישור למקורי",
|
linkToOriginal: "קישור למקורי",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Legutóbbi jegyzetek",
|
title: "Legutóbbi jegyzetek",
|
||||||
seeRemainingMore: ({ remaining }) => `${remaining} további megtekintése →`,
|
seeRemainingMore: ({ remaining }) => `${remaining} további megtekintése →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Legutóbbi változások",
|
||||||
|
filterAll: "Összes",
|
||||||
|
filterNew: "Új",
|
||||||
|
filterUpdated: "Frissített",
|
||||||
|
loadMoreTemplate: "{count} további betöltése · {remaining} maradt",
|
||||||
|
noChanges: "Nem találhatók legutóbbi változások.",
|
||||||
|
badgeNew: "Új",
|
||||||
|
badgeUpdated: "Szerkesztett",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `${targetSlug} áthivatkozása`,
|
transcludeOf: ({ targetSlug }) => `${targetSlug} áthivatkozása`,
|
||||||
linkToOriginal: "Hivatkozás az eredetire",
|
linkToOriginal: "Hivatkozás az eredetire",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Catatan Terbaru",
|
title: "Catatan Terbaru",
|
||||||
seeRemainingMore: ({ remaining }) => `Lihat ${remaining} lagi →`,
|
seeRemainingMore: ({ remaining }) => `Lihat ${remaining} lagi →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Perubahan Terkini",
|
||||||
|
filterAll: "Semua",
|
||||||
|
filterNew: "Baru",
|
||||||
|
filterUpdated: "Diperbarui",
|
||||||
|
loadMoreTemplate: "Muat {count} lagi · {remaining} tersisa",
|
||||||
|
noChanges: "Tidak ada perubahan terkini ditemukan.",
|
||||||
|
badgeNew: "Baru",
|
||||||
|
badgeUpdated: "Diedit",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Transklusi dari ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transklusi dari ${targetSlug}`,
|
||||||
linkToOriginal: "Tautan ke asli",
|
linkToOriginal: "Tautan ke asli",
|
||||||
|
|||||||
@ -46,6 +46,16 @@ export default {
|
|||||||
seeRemainingMore: ({ remaining }) =>
|
seeRemainingMore: ({ remaining }) =>
|
||||||
remaining === 1 ? "Vedi 1 altra →" : `Vedi altre ${remaining} →`,
|
remaining === 1 ? "Vedi 1 altra →" : `Vedi altre ${remaining} →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Modifiche recenti",
|
||||||
|
filterAll: "Tutte",
|
||||||
|
filterNew: "Nuove",
|
||||||
|
filterUpdated: "Aggiornate",
|
||||||
|
loadMoreTemplate: "Carica altri {count} · {remaining} rimanenti",
|
||||||
|
noChanges: "Nessuna modifica recente trovata.",
|
||||||
|
badgeNew: "Nuovo",
|
||||||
|
badgeUpdated: "Modificato",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Inclusione di ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Inclusione di ${targetSlug}`,
|
||||||
linkToOriginal: "Link all'originale",
|
linkToOriginal: "Link all'originale",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "最近の記事",
|
title: "最近の記事",
|
||||||
seeRemainingMore: ({ remaining }) => `さらに${remaining}件 →`,
|
seeRemainingMore: ({ remaining }) => `さらに${remaining}件 →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "最近の変更",
|
||||||
|
filterAll: "すべて",
|
||||||
|
filterNew: "新規",
|
||||||
|
filterUpdated: "更新済み",
|
||||||
|
loadMoreTemplate: "さらに{count}件表示 · 残り{remaining}件",
|
||||||
|
noChanges: "最近の変更はありません。",
|
||||||
|
badgeNew: "新規",
|
||||||
|
badgeUpdated: "編集済み",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `${targetSlug}のまとめ`,
|
transcludeOf: ({ targetSlug }) => `${targetSlug}のまとめ`,
|
||||||
linkToOriginal: "元記事へのリンク",
|
linkToOriginal: "元記事へのリンク",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Соңғы жазбалар",
|
title: "Соңғы жазбалар",
|
||||||
seeRemainingMore: ({ remaining }) => `Тағы ${remaining} жазбаны қарау →`,
|
seeRemainingMore: ({ remaining }) => `Тағы ${remaining} жазбаны қарау →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Соңғы өзгерістер",
|
||||||
|
filterAll: "Барлығы",
|
||||||
|
filterNew: "Жаңа",
|
||||||
|
filterUpdated: "Жаңартылған",
|
||||||
|
loadMoreTemplate: "{count} қосымша жүктеу · {remaining} қалды",
|
||||||
|
noChanges: "Соңғы өзгерістер табылмады.",
|
||||||
|
badgeNew: "Жаңа",
|
||||||
|
badgeUpdated: "Өңделген",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `${targetSlug} кірістіру`,
|
transcludeOf: ({ targetSlug }) => `${targetSlug} кірістіру`,
|
||||||
linkToOriginal: "Бастапқыға сілтеме",
|
linkToOriginal: "Бастапқыға сілтеме",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "최근 게시글",
|
title: "최근 게시글",
|
||||||
seeRemainingMore: ({ remaining }) => `${remaining}건 더보기 →`,
|
seeRemainingMore: ({ remaining }) => `${remaining}건 더보기 →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "최근 변경사항",
|
||||||
|
filterAll: "전체",
|
||||||
|
filterNew: "새로운",
|
||||||
|
filterUpdated: "업데이트됨",
|
||||||
|
loadMoreTemplate: "{count}개 더 보기 · {remaining}개 남음",
|
||||||
|
noChanges: "최근 변경사항이 없습니다.",
|
||||||
|
badgeNew: "새로운",
|
||||||
|
badgeUpdated: "수정됨",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `${targetSlug}의 포함`,
|
transcludeOf: ({ targetSlug }) => `${targetSlug}의 포함`,
|
||||||
linkToOriginal: "원본 링크",
|
linkToOriginal: "원본 링크",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Naujausi Užrašai",
|
title: "Naujausi Užrašai",
|
||||||
seeRemainingMore: ({ remaining }) => `Peržiūrėti dar ${remaining} →`,
|
seeRemainingMore: ({ remaining }) => `Peržiūrėti dar ${remaining} →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Naujausi pakeitimai",
|
||||||
|
filterAll: "Visi",
|
||||||
|
filterNew: "Nauji",
|
||||||
|
filterUpdated: "Atnaujinti",
|
||||||
|
loadMoreTemplate: "Įkelti {count} daugiau · liko {remaining}",
|
||||||
|
noChanges: "Naujausių pakeitimų nerasta.",
|
||||||
|
badgeNew: "Naujas",
|
||||||
|
badgeUpdated: "Redaguota",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Įterpimas iš ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Įterpimas iš ${targetSlug}`,
|
||||||
linkToOriginal: "Nuoroda į originalą",
|
linkToOriginal: "Nuoroda į originalą",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Nylige notater",
|
title: "Nylige notater",
|
||||||
seeRemainingMore: ({ remaining }) => `Se ${remaining} til →`,
|
seeRemainingMore: ({ remaining }) => `Se ${remaining} til →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Siste endringer",
|
||||||
|
filterAll: "Alle",
|
||||||
|
filterNew: "Nye",
|
||||||
|
filterUpdated: "Oppdaterte",
|
||||||
|
loadMoreTemplate: "Last inn {count} til · {remaining} gjenstår",
|
||||||
|
noChanges: "Ingen nylige endringer funnet.",
|
||||||
|
badgeNew: "Ny",
|
||||||
|
badgeUpdated: "Redigert",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Transkludering of ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transkludering of ${targetSlug}`,
|
||||||
linkToOriginal: "Lenke til original",
|
linkToOriginal: "Lenke til original",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Recente notities",
|
title: "Recente notities",
|
||||||
seeRemainingMore: ({ remaining }) => `Zie ${remaining} meer →`,
|
seeRemainingMore: ({ remaining }) => `Zie ${remaining} meer →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Recente wijzigingen",
|
||||||
|
filterAll: "Alle",
|
||||||
|
filterNew: "Nieuw",
|
||||||
|
filterUpdated: "Bijgewerkt",
|
||||||
|
loadMoreTemplate: "Laad {count} meer · {remaining} resterend",
|
||||||
|
noChanges: "Geen recente wijzigingen gevonden.",
|
||||||
|
badgeNew: "Nieuw",
|
||||||
|
badgeUpdated: "Bewerkt",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Invoeging van ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Invoeging van ${targetSlug}`,
|
||||||
linkToOriginal: "Link naar origineel",
|
linkToOriginal: "Link naar origineel",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Najnowsze notatki",
|
title: "Najnowsze notatki",
|
||||||
seeRemainingMore: ({ remaining }) => `Zobacz ${remaining} nastepnych →`,
|
seeRemainingMore: ({ remaining }) => `Zobacz ${remaining} nastepnych →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Ostatnie zmiany",
|
||||||
|
filterAll: "Wszystkie",
|
||||||
|
filterNew: "Nowe",
|
||||||
|
filterUpdated: "Zaktualizowane",
|
||||||
|
loadMoreTemplate: "Załaduj {count} więcej · pozostało {remaining}",
|
||||||
|
noChanges: "Nie znaleziono ostatnich zmian.",
|
||||||
|
badgeNew: "Nowe",
|
||||||
|
badgeUpdated: "Edytowane",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Osadzone ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Osadzone ${targetSlug}`,
|
||||||
linkToOriginal: "Łącze do oryginału",
|
linkToOriginal: "Łącze do oryginału",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Notas recentes",
|
title: "Notas recentes",
|
||||||
seeRemainingMore: ({ remaining }) => `Veja mais ${remaining} →`,
|
seeRemainingMore: ({ remaining }) => `Veja mais ${remaining} →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Mudanças recentes",
|
||||||
|
filterAll: "Todas",
|
||||||
|
filterNew: "Novas",
|
||||||
|
filterUpdated: "Atualizadas",
|
||||||
|
loadMoreTemplate: "Carregar {count} mais · {remaining} restantes",
|
||||||
|
noChanges: "Nenhuma mudança recente encontrada.",
|
||||||
|
badgeNew: "Novo",
|
||||||
|
badgeUpdated: "Editado",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Transcrever de ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transcrever de ${targetSlug}`,
|
||||||
linkToOriginal: "Link ao original",
|
linkToOriginal: "Link ao original",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Notițe recente",
|
title: "Notițe recente",
|
||||||
seeRemainingMore: ({ remaining }) => `Vezi încă ${remaining} →`,
|
seeRemainingMore: ({ remaining }) => `Vezi încă ${remaining} →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Modificări recente",
|
||||||
|
filterAll: "Toate",
|
||||||
|
filterNew: "Noi",
|
||||||
|
filterUpdated: "Actualizate",
|
||||||
|
loadMoreTemplate: "Încarcă {count} mai multe · {remaining} rămase",
|
||||||
|
noChanges: "Nu s-au găsit modificări recente.",
|
||||||
|
badgeNew: "Nou",
|
||||||
|
badgeUpdated: "Editat",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Extras din ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Extras din ${targetSlug}`,
|
||||||
linkToOriginal: "Legătură către original",
|
linkToOriginal: "Legătură către original",
|
||||||
|
|||||||
@ -46,6 +46,16 @@ export default {
|
|||||||
seeRemainingMore: ({ remaining }) =>
|
seeRemainingMore: ({ remaining }) =>
|
||||||
`Посмотреть оставш${getForm(remaining, "уюся", "иеся", "иеся")} ${remaining} →`,
|
`Посмотреть оставш${getForm(remaining, "уюся", "иеся", "иеся")} ${remaining} →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Последние изменения",
|
||||||
|
filterAll: "Все",
|
||||||
|
filterNew: "Новые",
|
||||||
|
filterUpdated: "Обновлённые",
|
||||||
|
loadMoreTemplate: "Загрузить ещё {count} · осталось {remaining}",
|
||||||
|
noChanges: "Последних изменений не найдено.",
|
||||||
|
badgeNew: "Новое",
|
||||||
|
badgeUpdated: "Изменено",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Переход из ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Переход из ${targetSlug}`,
|
||||||
linkToOriginal: "Ссылка на оригинал",
|
linkToOriginal: "Ссылка на оригинал",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "บันทึกล่าสุด",
|
title: "บันทึกล่าสุด",
|
||||||
seeRemainingMore: ({ remaining }) => `ดูเพิ่มอีก ${remaining} รายการ →`,
|
seeRemainingMore: ({ remaining }) => `ดูเพิ่มอีก ${remaining} รายการ →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "การเปลี่ยนแปลงล่าสุด",
|
||||||
|
filterAll: "ทั้งหมด",
|
||||||
|
filterNew: "ใหม่",
|
||||||
|
filterUpdated: "อัปเดตแล้ว",
|
||||||
|
loadMoreTemplate: "โหลดเพิ่ม {count} รายการ · เหลือ {remaining} รายการ",
|
||||||
|
noChanges: "ไม่พบการเปลี่ยนแปลงล่าสุด",
|
||||||
|
badgeNew: "ใหม่",
|
||||||
|
badgeUpdated: "แก้ไขแล้ว",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `รวมข้ามเนื้อหาจาก ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `รวมข้ามเนื้อหาจาก ${targetSlug}`,
|
||||||
linkToOriginal: "ดูหน้าต้นทาง",
|
linkToOriginal: "ดูหน้าต้นทาง",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Son Notlar",
|
title: "Son Notlar",
|
||||||
seeRemainingMore: ({ remaining }) => `${remaining} tane daha gör →`,
|
seeRemainingMore: ({ remaining }) => `${remaining} tane daha gör →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Son değişiklikler",
|
||||||
|
filterAll: "Tümü",
|
||||||
|
filterNew: "Yeni",
|
||||||
|
filterUpdated: "Güncellendi",
|
||||||
|
loadMoreTemplate: "{count} daha yükle · {remaining} kaldı",
|
||||||
|
noChanges: "Son değişiklik bulunamadı.",
|
||||||
|
badgeNew: "Yeni",
|
||||||
|
badgeUpdated: "Düzenlendi",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `${targetSlug} sayfasından alıntı`,
|
transcludeOf: ({ targetSlug }) => `${targetSlug} sayfasından alıntı`,
|
||||||
linkToOriginal: "Orijinal bağlantı",
|
linkToOriginal: "Orijinal bağlantı",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Останні нотатки",
|
title: "Останні нотатки",
|
||||||
seeRemainingMore: ({ remaining }) => `Переглянути ще ${remaining} →`,
|
seeRemainingMore: ({ remaining }) => `Переглянути ще ${remaining} →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Останні зміни",
|
||||||
|
filterAll: "Усі",
|
||||||
|
filterNew: "Нові",
|
||||||
|
filterUpdated: "Оновлені",
|
||||||
|
loadMoreTemplate: "Завантажити ще {count} · залишилось {remaining}",
|
||||||
|
noChanges: "Останніх змін не знайдено.",
|
||||||
|
badgeNew: "Нове",
|
||||||
|
badgeUpdated: "Відредаговано",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Видобуто з ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Видобуто з ${targetSlug}`,
|
||||||
linkToOriginal: "Посилання на оригінал",
|
linkToOriginal: "Посилання на оригінал",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "Ghi chú gần đây",
|
title: "Ghi chú gần đây",
|
||||||
seeRemainingMore: ({ remaining }) => `Xem thêm ${remaining} ghi chú →`,
|
seeRemainingMore: ({ remaining }) => `Xem thêm ${remaining} ghi chú →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "Thay đổi gần đây",
|
||||||
|
filterAll: "Tất cả",
|
||||||
|
filterNew: "Mới",
|
||||||
|
filterUpdated: "Đã cập nhật",
|
||||||
|
loadMoreTemplate: "Tải thêm {count} · còn {remaining}",
|
||||||
|
noChanges: "Không tìm thấy thay đổi gần đây.",
|
||||||
|
badgeNew: "Mới",
|
||||||
|
badgeUpdated: "Đã chỉnh sửa",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `Trích dẫn toàn bộ từ ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Trích dẫn toàn bộ từ ${targetSlug}`,
|
||||||
linkToOriginal: "Xem trang gốc",
|
linkToOriginal: "Xem trang gốc",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "最近的笔记",
|
title: "最近的笔记",
|
||||||
seeRemainingMore: ({ remaining }) => `查看更多${remaining}篇笔记 →`,
|
seeRemainingMore: ({ remaining }) => `查看更多${remaining}篇笔记 →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "最近更改",
|
||||||
|
filterAll: "全部",
|
||||||
|
filterNew: "新增",
|
||||||
|
filterUpdated: "已更新",
|
||||||
|
loadMoreTemplate: "加载更多 {count} 条 · 剩余 {remaining} 条",
|
||||||
|
noChanges: "未找到最近的更改。",
|
||||||
|
badgeNew: "新增",
|
||||||
|
badgeUpdated: "已编辑",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `包含${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `包含${targetSlug}`,
|
||||||
linkToOriginal: "指向原始笔记的链接",
|
linkToOriginal: "指向原始笔记的链接",
|
||||||
|
|||||||
@ -45,6 +45,16 @@ export default {
|
|||||||
title: "最近的筆記",
|
title: "最近的筆記",
|
||||||
seeRemainingMore: ({ remaining }) => `查看更多 ${remaining} 篇筆記 →`,
|
seeRemainingMore: ({ remaining }) => `查看更多 ${remaining} 篇筆記 →`,
|
||||||
},
|
},
|
||||||
|
recentChanges: {
|
||||||
|
title: "最近更改",
|
||||||
|
filterAll: "全部",
|
||||||
|
filterNew: "新增",
|
||||||
|
filterUpdated: "已更新",
|
||||||
|
loadMoreTemplate: "載入更多 {count} 筆 · 剩餘 {remaining} 筆",
|
||||||
|
noChanges: "未找到最近的更改。",
|
||||||
|
badgeNew: "新增",
|
||||||
|
badgeUpdated: "已編輯",
|
||||||
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({ targetSlug }) => `包含 ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `包含 ${targetSlug}`,
|
||||||
linkToOriginal: "指向原始筆記的連結",
|
linkToOriginal: "指向原始筆記的連結",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user