diff --git a/docs/spaces-agents.md b/docs/spaces-agents.md index 8d151b43..b507615b 100644 --- a/docs/spaces-agents.md +++ b/docs/spaces-agents.md @@ -77,6 +77,7 @@ unique ids the first time. | `divider` | — | `
` | | `image` | `src` (see below), `alt`, `caption`, `width` (10–100 **%**), `w`/`h` (intrinsic px) | `
` | | `pagelink` | `page` | a card linking to another page | +| `embed` | `page`, `anchor`, `html` | a live view of another page, or one section of it — see **Embeds** | | `link` | `url`, `title`, `desc`, `site`, `icon`, `image`, `html` | a card linking OUT of the space — see **Link cards** | | `prop` | `key`, `value`, `html` | one field value — see **The issue tracker** | | `view` | `layout`, `groupBy`, `html` | a board or list of this space's issues | @@ -108,6 +109,38 @@ as it is for `prop`. A `javascript:` or `data:` url renders as a dead card that keeps its title rather than as a link — `validate()` reports both that and a remote `image`. Use `pagelink`, not this, for a page inside the space. +### Embeds + +An `embed` block shows another page **live**. It stores a reference and never a +copy: the source page is the truth, and editing it changes every embed of it. + +```jsonc +{ "id": "b9", "type": "embed", + "page": "p-design", // the target page id, exactly as pagelink means it + "anchor": "Rollout", // OPTIONAL — one heading on that page, matched by NAME + "html": "Design notes" } +``` + +Write `html` too, for the reason `link` and `prop` do: it is what a build that +predates this type renders, so an older shell shows a link to the source page +instead of a blank. + +`anchor` names an `h1`/`h2`/`h3` on the target, case- and +whitespace-insensitively, and the section runs to the next heading of the same +or higher rank — so an h2 takes its h3s with it. Leave it out for the whole +page; **a name that matches nothing is reported by `validate()`, never quietly +widened back to the whole page.** + +Two limits, both deliberate and both visible to the reader rather than silent: +a page cannot embed itself or anything that leads back to it (the loop renders +as a named placeholder), and an embed chain is followed at most **three** pages +deep. `validate()` reports both, plus a target that is not a page. + +An embed produces a **backlink** on its target, exactly as a `pagelink` does — +it is the strongest reference in the model, since the page it names is being +shown somewhere else. In Markdown it is Obsidian's `![[Page]]` / `![[Page#Section]]`, +in both directions: a vault's embeds import as embeds and export as themselves. + ### Callouts `tone` is one of `note` `tip` `important` `warning` `caution` — GitHub's alert @@ -277,6 +310,7 @@ notifications, automation. The file is the team boundary and the capability. | a topic that belongs *under* another | `parent` on the page | the tree is the navigation | | a reference to another page | an inline `#p/` link | it produces a backlink on the target automatically, at no cost | | a list of sub-pages | one `pagelink` block each | a visible card beats a bare link for a hub page | +| one page's material that belongs on another too | an `embed`, narrowed with `anchor` | the source stays the single copy — a pasted duplicate is what goes stale | | steps someone will tick off | `todo` | state lives in the document, so it survives sharing | | an aside, or detail most readers skip | `toggle` with its body as `parent` children | folds away, and always PRINTS expanded | | a warning the reader must not miss | `callout` with the `tone` that fits | it is boxed, named and legible in print and without colour vision — but three per page and none of them registers | @@ -352,7 +386,9 @@ findings.filter(f => f.severity === 'error') Each finding is `{code, severity, message, fix, page?, block?, path?}`. It reports duplicate and missing ids, a page inside its own subtree (which is the one way a page becomes unreachable), parents naming nothing, `#p/` links and -`pagelink` cards pointing at pages that do not exist, unknown block types, block +`pagelink` cards and `embed` blocks pointing at pages that do not exist +(`broken-embed`), embeds that loop back to their own page (`embed-cycle`) and +embed anchors that name no heading (`no-section`), unknown block types, block markup inside inline `html` (and markup that is dropped whole), hrefs outside the allowlist, images with no `alt`, no size, a missing `asset:` or a remote `src`, a `home` naming nothing, pages with no blocks, and assets nothing references. diff --git a/scripts/test-spaces-model.ts b/scripts/test-spaces-model.ts index a61883d3..6c415dca 100644 --- a/scripts/test-spaces-model.ts +++ b/scripts/test-spaces-model.ts @@ -49,7 +49,11 @@ import { CLASS_OK, keepClasses, } from '../spaces/src/marks.ts' import { extractSpace, planGraft, subtreeIds } from '../spaces/src/portable.ts' -import { planUpdatePage } from '../spaces/src/agent.ts' +import { + EMBED_MAX_DEPTH, isPageRef, anchorOf, sectionOf, headingsOf, viewEmbed, embedReaches, + parseEmbedLine, embedToMd, linkEmbeds, +} from '../spaces/src/embed.ts' +import { planUpdatePage, validateDoc } from '../spaces/src/agent.ts' import { tokenize, normLang, langLabel, CODE_LANGS } from '../spaces/src/highlight.ts' import { escText, externalHref } from '../spaces/src/sanitize.ts' import { @@ -3594,5 +3598,292 @@ function fsTable(f: string): string { } +// ---- TRANSCLUSION ---------------------------------------------------------- +// +// An `embed` block shows another page LIVE. Everything asserted here is the +// part that fails silently: a loop that would hang the renderer, a target that +// is gone, a section name that matches nothing, and the two halves of the +// Obsidian round trip — `![[Page]]` in, `![[Page]]` out. markdown.ts carried a +// comment saying "there is no transclusion in the model" and quietly demoted +// every embed in an imported vault to a plain link; this is what replaced it. +{ + const space = (): SpacesDoc => JSON.parse(JSON.stringify({ + format: FORMAT, version: 1, docId: 'doc-embed', title: 'Space', home: 'A', theme: {}, + pages: [ + { id: 'A', title: 'Alpha', blocks: [ + { id: 'a1', type: 'p', html: 'top of alpha' }, + { id: 'a2', type: 'embed', page: 'B', html: 'Beta' }, + ] }, + { id: 'B', title: 'Beta', blocks: [ + { id: 'b1', type: 'p', html: 'intro' }, + { id: 'b2', type: 'h2', html: 'Rollout' }, + { id: 'b3', type: 'p', html: 'first step' }, + { id: 'b4', type: 'h3', html: 'Details' }, + { id: 'b5', type: 'p', html: 'the detail' }, + { id: 'b6', type: 'h2', html: 'Risks' }, + { id: 'b7', type: 'p', html: 'the risk' }, + ] }, + ], + })) + + // ---- the section slice --------------------------------------------------- + const doc0 = space() + const beta = doc0.pages[1] + const roll = sectionOf(beta, 'rollout') + ok(roll !== null && roll.map((b) => b.id).join(',') === 'b2,b3,b4,b5', + 'a section is its heading, its prose and its SUBheadings, stopping at the next h2') + ok(sectionOf(beta, 'Details')?.map((b) => b.id).join(',') === 'b4,b5', + 'an h3 section stops at the next heading of the same or higher rank') + ok(sectionOf(beta, 'ROLL OUT') === null && sectionOf(beta, 'rollout') !== null, + 'heading names match on their TEXT, tags stripped — and not on a name nobody wrote') + ok(sectionOf(beta, 'Nowhere') === null, + 'a name that matches nothing returns null, never the whole page') + + // A HEADING NAME OUT OF A MAILED FILE. `HEADINGS` is a plain object keyed on + // b.type, so `'toString' in HEADINGS` is true and would hand back a native + // function — the bug this app has shipped twice. Object.hasOwn is why these + // are misses rather than a rank of NaN. + for (const evil of ['toString', 'constructor', '__proto__', 'valueOf', 'hasOwnProperty']) { + ok(sectionOf(beta, evil) === null, `anchor ${JSON.stringify(evil)} finds no section`) + const poisoned: Page = { id: 'X', title: 'X', blocks: [{ id: 'x1', type: evil, html: 'Rollout' }] } + ok(sectionOf(poisoned, 'Rollout') === null, + `a block of type ${JSON.stringify(evil)} is not treated as a heading`) + ok(headingsOf(poisoned).length === 0, + `…and the picker does not offer it as one either`) + } + + // THE PICKER AND THE RESOLVER AGREE. Every name the editor can offer is a + // name sectionOf finds — otherwise a section you chose reports as missing. + ok(headingsOf(beta).map((h) => h.text).join('|') === 'Rollout|Details|Risks', + 'headingsOf lists every heading, in page order, as plain text') + ok(headingsOf(beta).every((h) => sectionOf(beta, h.text) !== null), + 'and every one of them resolves — the picker cannot offer a dead section') + + // ---- what one embed shows ------------------------------------------------ + const whole = viewEmbed(doc0.pages[0].blocks[1], doc0, ['A']) + ok(whole.ok && whole.page.id === 'B' && whole.blocks.length === beta.blocks.length, + 'an embed with no anchor shows the whole target page') + const narrowed = viewEmbed({ id: 'e', type: 'embed', page: 'B', anchor: 'Risks' }, doc0, ['A']) + ok(narrowed.ok && narrowed.blocks.map((b) => b.id).join(',') === 'b6,b7', + 'an embed with an anchor shows that section and nothing else') + const noSec = viewEmbed({ id: 'e', type: 'embed', page: 'B', anchor: 'Ghost' }, doc0, ['A']) + ok(!noSec.ok && noSec.why === 'no-section', + 'an anchor that matches nothing is REPORTED, not quietly widened to the whole page') + + // ---- a dangling ref ------------------------------------------------------ + const gone = viewEmbed({ id: 'e', type: 'embed', page: 'nope' }, doc0, ['A']) + ok(!gone.ok && gone.why === 'missing', 'a target that is not a page is a named miss') + ok(!viewEmbed({ id: 'e', type: 'embed' }, doc0, ['A']).ok, + 'and an embed with no target at all does not throw') + for (const evil of ['toString', '__proto__', 'constructor', 'valueOf']) { + const v = viewEmbed({ id: 'e', type: 'embed', page: evil }, doc0, ['A']) + ok(!v.ok && v.why === 'missing' && v.page === undefined, + `page ${JSON.stringify(evil)} resolves to nothing, never to a native function`) + } + + // ---- cycles -------------------------------------------------------------- + // + // The renderer walks a chain of open pages; a target already on that chain + // is the loop, and it stops with a NAMED placeholder (the page is still + // returned, so the reader is told which one repeats) rather than a blank. + const loop = viewEmbed(doc0.pages[0].blocks[1], doc0, ['X', 'B', 'A']) + ok(!loop.ok && loop.why === 'cycle' && loop.page?.title === 'Beta', + 'a target already open above this block is a cycle, and the placeholder can name it') + ok(!viewEmbed({ id: 'e', type: 'embed', page: 'A' }, doc0, ['A']).ok, + 'a page embedding ITSELF is a cycle at depth zero') + + // …and the shape a cycle check cannot see: a chain of DISTINCT pages. + const deep = viewEmbed(doc0.pages[0].blocks[1], doc0, ['P0', 'P1', 'P2', 'P3']) + ok(!deep.ok && deep.why === 'depth', + `an embed chain is not followed deeper than ${EMBED_MAX_DEPTH} pages`) + ok(viewEmbed(doc0.pages[0].blocks[1], doc0, ['P0', 'P1', 'P2']).ok, + '…and exactly at the cap it still renders, so the limit is off-by-none') + + // The validator's question, which one render path cannot answer. + const cyc: SpacesDoc = space() + cyc.pages[1].blocks.push({ id: 'b8', type: 'embed', page: 'A' }) + ok(embedReaches(cyc, 'A', 'B'), 'A embeds B embeds A is reported as a loop') + ok(!embedReaches(doc0, 'A', 'B'), 'and a one-way embed is not') + ok(embedReaches(cyc, 'A', 'A'), 'a page reaches itself trivially') + // TERMINATION on a document that already cycles: this runs on hand-edited + // files, and a loop check that hangs on a looping file is worse than none. + ok(!embedReaches(cyc, 'nobody', 'B'), 'the walk terminates on a cycling document') + + // ---- backlinks ----------------------------------------------------------- + // NO `html` ON THIS ONE, deliberately. Every embed the editor writes carries + // a fallback link, and the index's inline-link sweep would find THAT — so an + // embed with html cannot tell you whether the index understands embeds at + // all. An agent-written block is the one that can, and it is also the one + // that would silently have no backlink if it did not. + const bare: SpacesDoc = space() + bare.pages[0].blocks.push({ id: 'a9', type: 'embed', page: 'B' }) + const ix = buildIndex(bare) + ok(ix.backlinks.get('B')?.some((s2) => s2.blockId === 'a9'), + 'an embed with no fallback html still appears in "Linked from", as a pagelink does') + ok(!String(bare.pages[0].blocks.find((b) => b.id === 'a9')?.html ?? '').includes('#p/'), + '…and it really had no link in its html for the inline sweep to find') + ok(isPageRef({ id: 'x', type: 'embed', page: 'B' }) && + isPageRef({ id: 'x', type: 'pagelink', page: 'B' }) && + !isPageRef({ id: 'x', type: 'embed' }) && !isPageRef({ id: 'x', type: 'p', page: 'B' }), + 'isPageRef is exactly "a block that names a page id"') + + // ---- the default is never stored ---------------------------------------- + ok(anchorOf({ id: 'x', type: 'embed', page: 'B' }) === undefined && + anchorOf({ id: 'x', type: 'embed', page: 'B', anchor: ' ' }) === undefined && + anchorOf({ id: 'x', type: 'embed', page: 'B', anchor: ' Risks ' }) === 'Risks', + 'no anchor and a blank anchor are the same absent default; a real one is trimmed') + + // ---- markdown IN --------------------------------------------------------- + ok(parseEmbedLine('![[Design notes]]')?.target === 'Design notes', + 'a whole line of ![[Note]] is an embed') + const withSec = parseEmbedLine('![[Design notes#Rollout]]') + ok(withSec?.target === 'Design notes' && withSec?.anchor === 'Rollout', + '…and ![[Note#Section]] carries the section') + ok(parseEmbedLine('![[Note|shown]]')?.target === 'Note' && + parseEmbedLine('![[Note|shown]]')?.anchor === undefined, + 'an |alias has nothing to be the text of, so it is dropped rather than stored') + ok(parseEmbedLine('![[Note#^abc123]]')?.anchor === undefined, + 'a ^block anchor lands on the page — this model has no block anchors') + ok(parseEmbedLine('see ![[Note]] here') === null && parseEmbedLine('[[Note]]') === null, + 'an embed inside a sentence, and a plain wikilink, are not blocks') + + const note = parseNote('# Plan\n\nsome prose\n\n![[Design notes#Rollout]]\n\n![[pic.png]]\n\nsee ![[Other]] inline\n', 'Plan') + const kinds = note.blocks.map((b) => b.type).join(',') + ok(kinds === 'p,embed,image,p', `a standalone embed line becomes an embed block (got ${kinds})`) + ok(note.blocks[1].anchor === 'Rollout' && note.blocks[1].page === undefined, + 'the parser carries the section and NO page — no page ids exist at parse time') + ok(String(note.blocks[1].html).includes('#w/Design%20notes'), + '…it carries the same #w/ placeholder link every other block carries') + ok(note.blocks[2].type === 'image' && note.blocks[2].src === 'pic.png', + '![[picture.png]] is still an IMAGE — imageOf owns the extension list and runs first') + ok(String(note.blocks[3].html).includes('#w/Other'), + 'and an inline ![[…]] is still a link inside its sentence') + + // ---- markdown IN, resolved ---------------------------------------------- + const plan = planImport([ + { path: 'Vault/Plan.md', text: '# Plan\n\n![[Design notes#Rollout]]\n\n![[Missing note]]\n' }, + { path: 'Vault/Design notes.md', text: '# Design notes\n\n## Rollout\n\nship it\n' }, + ], { rootTitle: 'Vault' }) + const planPage = plan.pages.find((p) => p.title === 'Plan')! + const design = plan.pages.find((p) => p.title === 'Design notes')! + const emb = planPage.blocks.find((b) => b.type === 'embed') + ok(emb !== undefined && emb.page === design.id, + 'an imported embed points at the page its wikilink named') + ok(emb?.anchor === 'Rollout' && sectionOf(design, String(emb?.anchor)) !== null, + '…and its section resolves against the page that arrived') + ok(planPage.blocks.every((b) => b.type !== 'embed' || b.page !== undefined), + 'no imported embed is left with a placeholder for a target') + const dead = planPage.blocks.find((b) => String(b.html ?? '').includes('[[Missing note]]')) + ok(dead !== undefined && dead.type === 'p', + 'an embed of a note that was not in the import becomes the literal text the author typed') + + // linkEmbeds on its own, since planImport is the only caller. + const pending: Page[] = [{ id: 'P', title: 'P', blocks: [ + { id: 'e1', type: 'embed', html: 'Beta', anchor: 'Risks' }, + { id: 'e2', type: 'embed', html: '[[Nowhere]]', anchor: 'Risks' }, + ] }] + const res = linkEmbeds(pending) + ok(res.linked === 1 && res.dropped === 1, 'linkEmbeds counts both outcomes') + ok(pending[0].blocks[0].page === 'B' && pending[0].blocks[0].anchor === 'Risks', + 'a resolved embed takes its target from the html the wikilink sweep rewrote') + ok(pending[0].blocks[1].type === 'p' && pending[0].blocks[1].page === undefined && + pending[0].blocks[1].anchor === undefined, + 'an unresolved one becomes a paragraph and keeps no half-set fields') + + // ---- markdown OUT, and the round trip ------------------------------------ + const spec = SPEC.get('embed')! + const ctx = { titleOf: (id: string) => (id === 'B' ? 'Beta' : undefined), rowsOf: () => [], inline: (h: string) => h } + ok(spec.toMd!({ id: 'e', type: 'embed', page: 'B' }, '', '', ctx).join('') === '![[Beta]]', + 'an embed exports as the ![[Page]] it was imported from') + ok(spec.toMd!({ id: 'e', type: 'embed', page: 'B', anchor: 'Risks' }, '', '', ctx).join('') === '![[Beta#Risks]]', + '…and carries its section with it') + ok(embedToMd(undefined, undefined) === '![[?]]', + 'an embed whose target is gone exports as a visible ?, never as an empty ![[]]') + + // THE FULL LOOP: export → parse → resolve → the same target and section. + const md = spec.toMd!({ id: 'e', type: 'embed', page: 'B', anchor: 'Risks' }, '', '', ctx).join('\n') + const back = planImport([ + { path: 'V/Alpha.md', text: `# Alpha\n\n${md}\n` }, + { path: 'V/Beta.md', text: '# Beta\n\n## Risks\n\nthe risk\n' }, + ], { rootTitle: 'V' }) + const alphaBack = back.pages.find((p) => p.title === 'Alpha')! + const betaBack = back.pages.find((p) => p.title === 'Beta')! + const round = alphaBack.blocks.find((b) => b.type === 'embed') + ok(round?.page === betaBack.id && round?.anchor === 'Risks', + 'an embed survives export to Markdown and back with its target and its section') + + // ---- extract and graft --------------------------------------------------- + const wide: SpacesDoc = JSON.parse(JSON.stringify({ + format: FORMAT, version: 1, docId: 'doc-wide', title: 'Wide', home: 'R', theme: {}, + pages: [ + { id: 'R', title: 'Root', blocks: [ + { id: 'r1', type: 'embed', page: 'K', anchor: 'Risks', html: 'Kid' }, + { id: 'r2', type: 'embed', page: 'Z', anchor: 'Risks', html: 'Zed' }, + ] }, + { id: 'K', title: 'Kid', parent: 'R', blocks: [{ id: 'k1', type: 'h2', html: 'Risks' }] }, + { id: 'Z', title: 'Zed', blocks: [{ id: 'z1', type: 'p', html: 'away' }] }, + ], + })) + const cutOut = extractSpace(wide, 'R', { docId: 'doc-cut', now: '2026-09-09T00:00:00.000Z' }) + const cutRoot = cutOut.doc.pages[0] + ok(cutRoot.blocks[0].type === 'embed' && cutRoot.blocks[0].page === 'K', + 'an embed whose target travelled still points at it') + ok(cutRoot.blocks[1].type === 'p' && cutRoot.blocks[1].page === undefined && + cutRoot.blocks[1].anchor === undefined && + String(cutRoot.blocks[1].html).includes('[[Zed]]'), + 'an embed whose target stayed behind becomes the same honest text a pagelink becomes') + + const host: SpacesDoc = JSON.parse(JSON.stringify({ + format: FORMAT, version: 1, docId: 'doc-host', title: 'Host', home: 'H', theme: {}, + pages: [ + { id: 'H', title: 'Host home', blocks: [{ id: 'h1', type: 'p', html: '' }] }, + // the host already owns 'V2', so the visitor's page of that id must be + // renumbered — which is the only way this exercises the remap at all + { id: 'V2', title: 'Host two', blocks: [{ id: 'h2', type: 'p', html: '' }] }, + ], + })) + // an id COLLISION with the host, so the graft has to renumber and the embed + // has to follow it — the case a copied `page` would silently get wrong + const visitor: SpacesDoc = JSON.parse(JSON.stringify({ + format: FORMAT, version: 1, docId: 'doc-vis', title: 'Visitor', home: 'H', theme: {}, + pages: [ + { id: 'H', title: 'Visitor home', blocks: [ + { id: 'v1', type: 'embed', page: 'V2', anchor: 'Risks', html: 'Two' }, + ] }, + { id: 'V2', title: 'Two', parent: 'H', blocks: [{ id: 'v2', type: 'h2', html: 'Risks' }] }, + ], + })) + const graft = planGraft(host, visitor, {}) + const landed = graft.pages[0].blocks[0] + ok(graft.pages[0].id !== 'H', 'the grafted root was renumbered around the host id collision') + ok(landed.type === 'embed' && landed.page === graft.pages[1].id && landed.page !== 'V2', + 'and its embed followed the renumbering instead of pointing at the visitor’s old id') + ok(landed.anchor === 'Risks', 'the section came with it') + const grafted: SpacesDoc = { ...host, pages: [...host.pages, ...graft.pages] } + ok(viewEmbed(landed, grafted, [graft.pages[0].id]).ok, + 'the grafted embed RESOLVES in the document it landed in — the whole point of the remap') + + // ---- validate ------------------------------------------------------------ + const sick: SpacesDoc = space() + sick.pages[0].blocks.push({ id: 'a3', type: 'embed', page: 'nope' }) + sick.pages[0].blocks.push({ id: 'a4', type: 'embed', page: 'B', anchor: 'Ghost' }) + const sickCodes = validateDoc(sick).findings.map((i) => i.code) + ok(sickCodes.includes('broken-embed'), 'validate names an embed whose target is not a page') + ok(sickCodes.includes('no-section'), '…an anchor that matches no heading on the target') + ok(validateDoc(sick).findings.filter((i) => i.code === 'broken-embed')[0].severity === 'error', + 'a dead embed is an error, not a note') + + // The loop gets its OWN document, because a page that is on a cycle is + // reported as a cycle FIRST: a section name on a block whose whole embed is + // cut short is not the thing to tell the author about. + const looped: SpacesDoc = space() + looped.pages[1].blocks.push({ id: 'b9', type: 'embed', page: 'A', html: 'Alpha' }) + ok(validateDoc(looped).findings.some((i) => i.code === 'embed-cycle'), + '…and a loop, which renders as a stub nobody would notice') + ok(validateDoc(space()).findings.every((i) => !String(i.code).startsWith('embed') && i.code !== 'broken-embed'), + 'and a healthy embed raises nothing at all') +} + + console.log(`\n${checks - failures}/${checks} checks passed`) if (failures) process.exit(1) diff --git a/spaces/CHANGELOG.md b/spaces/CHANGELOG.md index 820b8829..53a51a23 100644 --- a/spaces/CHANGELOG.md +++ b/spaces/CHANGELOG.md @@ -477,6 +477,31 @@ Versions follow `0.MINOR.PATCH` while pre-1.0. 2026, from the same file. `bento.journal()` opens today's for an agent, and `bento.journal('2026-08-06')` any day's. +- **A page can show another page: transclusion.** The new `embed` block draws a + live view of another page — or of one heading's section of it, chosen by name + — attributed to its source and clickable through to it. It stores a + REFERENCE and never a copy, so the source page stays the single copy of the + words and every embed of it changes when it does. + + This closes a hole in the Obsidian import that nothing reported. `markdown.ts` + had parsed `![[Page]]` since the importer was written and carried a comment + saying "an embed of a note is just a link to it, because there is no + transclusion in the model" — so a vault arrived with every embed silently + demoted to a plain link. A whole line of `![[Page]]` or `![[Page#Section]]` + is now an embed, and exports back as itself; an `![[…]]` inside a sentence is + still a link, because a block cannot live in the middle of one. + + What the reader is never shown is a blank box. A loop (A embeds B embeds A) + renders as a named placeholder that says which page repeats, an embed chain + is followed at most three pages deep, a target that has been deleted says so, + and an `anchor` that matches no heading says THAT rather than quietly falling + back to the whole page. `validate()` names all four (`broken-embed`, + `embed-cycle`, `no-section`). An embed also appears in "Linked from" like a + page link does — it is the strongest reference in the model, and the one you + most want to be warned about before rewriting a page — and it survives being + extracted or grafted into another space, where a target that did not travel + becomes the same honest `[[Name]]` text a page link becomes. + ## [0.1.0] — 2026-08-03 First release. diff --git a/spaces/src/agent.ts b/spaces/src/agent.ts index e2b412a7..fe3f6571 100644 --- a/spaces/src/agent.ts +++ b/spaces/src/agent.ts @@ -34,6 +34,7 @@ import { type SpacesDoc, type Page, type Block, buildIndex, isRemote, newBlock, import { SPECS, SPEC } from './blocks.ts' import { sanitizeInline, textOf, inertBody, esc, UNWRAP } from './sanitize.ts' import { orphanAssets, humanBytes } from './assets.ts' +import { isPageRef, anchorOf, sectionOf, embedReaches } from './embed.ts' import { type FieldSpec, ISSUE_FIELDS, fieldsOf, fieldByKey, optionOf, propBlock, propHtml, valuesOf, isIssue, headerLength, } from './fields.ts' @@ -305,7 +306,7 @@ export function validateDoc(doc: SpacesDoc): ValidateResult { add({ page: p.id, code: 'no-blocks', severity: 'error', path: 'blocks', message: `Page "${p.title}" has no blocks, so there is nothing in it to put a caret in — it cannot be typed into.`, fix: 'Give it at least one block, e.g. { "type": "p", "html": "" }.' }) - } else if (!blocks.some((b) => textOf(b.html).trim() || b.type === 'image' || b.type === 'media' || b.type === 'pagelink' || b.type === 'link' || b.type === 'divider')) { + } else if (!blocks.some((b) => textOf(b.html).trim() || b.type === 'image' || b.type === 'media' || b.type === 'pagelink' || b.type === 'embed' || b.type === 'link' || b.type === 'divider')) { add({ page: p.id, code: 'empty-page', severity: 'info', message: `Page "${p.title}" has blocks but no content.`, fix: 'Write something, or remove the page. A deliberately blank page (an inbox, a stub) is fine — this is only a note.' }) @@ -393,6 +394,31 @@ export function validateDoc(doc: SpacesDoc): ValidateResult { } } + // AN EMBED IS A PAGELINK THAT SHOWS ITS TARGET, so it can go wrong in + // three ways instead of one — and every one of them is silent to a + // reader who never saw the page it was supposed to be showing. + if (b.type === 'embed') { + const target = typeof b.page === 'string' ? b.page : '' + const anchor = anchorOf(b) + if (!target || !pageIx.has(target)) { + add({ ...at, code: 'broken-embed', severity: 'error', path: 'page', + message: `An embed points at "${target || '(nothing)'}", which is not a page — it renders as a note saying so instead of the content.`, + fix: 'Set page to a real page id, or remove the block.' }) + } else if (embedReaches(doc, p.id, target)) { + // NAMED, not merely counted: the renderer stops the loop safely (a + // placeholder where the repeat would be), so this is not a crash + // waiting to happen — it is content the author believes is on the + // page and that nobody will ever see. + add({ ...at, code: 'embed-cycle', severity: 'error', path: 'page', + message: `This embed of "${pageIx.get(target)?.title ?? target}" leads back to this page, so the loop is cut short and the rest of the embed is not shown.`, + fix: 'Point the embed at a page that does not embed this one, or narrow it to a section with anchor.' }) + } else if (anchor && !sectionOf(pageIx.get(target)!, anchor)) { + add({ ...at, code: 'no-section', severity: 'warning', path: 'anchor', + message: `No heading named "${anchor}" on "${pageIx.get(target)?.title ?? target}" — the embed shows a note instead of that section.`, + fix: 'Match anchor to a heading on that page, or remove anchor to embed the whole page.' }) + } + } + if (b.type === 'link') { // A CARD IS NOT A LINK UNTIL ITS URL IS ONE. `linkCard` returns '' for // an empty url and for anything outside https:/http:/mailto: — and a @@ -600,7 +626,9 @@ export function outlineDoc(doc: SpacesDoc): OutlineResult { if (b.type === 'h1' || b.type === 'h2' || b.type === 'h3') { headings.push({ id: b.id, level: Number(b.type.slice(1)) as 1 | 2 | 3, text }) } - if (b.type === 'pagelink' && typeof b.page === 'string' && !links.includes(b.page)) links.push(b.page) + // pagelink AND embed: both name a page, and an outline that listed only + // the first would under-report exactly the dependency that hurts most. + if (isPageRef(b) && !links.includes(String(b.page))) links.push(String(b.page)) for (const m of (b.html ?? '').matchAll(/href\s*=\s*["']#p\/([^"']+)["']/g)) { if (!links.includes(m[1])) links.push(m[1]) } diff --git a/spaces/src/blocks.ts b/spaces/src/blocks.ts index 28ec22be..4e1a2ad7 100644 --- a/spaces/src/blocks.ts +++ b/spaces/src/blocks.ts @@ -22,6 +22,7 @@ import type { Block } from './model' import { effectiveParents, tableOf, writeTable, linkCard } from './model.ts' +import { anchorOf, embedToMd } from './embed.ts' import type { IconName } from './icons' export interface BlockSpec { @@ -243,6 +244,24 @@ export const SPECS: BlockSpec[] = [ tag: 'div', custom: true, toMd: (b, _text, _indent, ctx) => [`→ [[${ctx.titleOf(String(b.page)) ?? '?'}]]`], }, + { + // TRANSCLUSION — the pagelink's other half. A pagelink says where + // something is; an embed shows it, live, from the source. + // + // `text: false` for the reason a pagelink has none: the block's content is + // somewhere else, and an editable line beside it would be a second place + // to type that nothing displays. Its `html` is written by the editor as a + // plain link to the target, which is what a build that predates this type + // renders (render.ts default case) — the additivity fallback, not a copy. + type: 'embed', label: 'Embed a page', hint: 'A live view of another page', icon: 'page', + tag: 'div', custom: true, + // BACK TO THE SYNTAX IT CAME FROM. markdown.ts parses Obsidian's + // `![[Page]]` and, before this type existed, silently turned every one + // into a plain link — so a vault lost every embed on the way in and the + // export had nothing to write back. The two halves are one round trip and + // scripts/test-spaces-model.ts asserts it in both directions. + toMd: (b, _text, _indent, ctx) => [embedToMd(ctx.titleOf(String(b.page)), anchorOf(b))], + }, { // A LINK TO SOMEWHERE ON THE WEB — the outward-facing sibling of pagelink. // diff --git a/spaces/src/editor.ts b/spaces/src/editor.ts index e682b5b1..992359f9 100644 --- a/spaces/src/editor.ts +++ b/spaces/src/editor.ts @@ -32,6 +32,7 @@ import { } from './fields' import { planImport, type SourceFile } from './markdown' import { extractSpace, planGraft } from './portable' +import { headingsOf } from './embed.ts' import { countOutsideTags, replaceOutsideTags } from './findreplace' import { asksForAnswer, evaluate, format, pageContext } from './calc' import { t, locale } from './i18n' @@ -281,11 +282,16 @@ export class Editor { close() const page = this.store.page if (!page) return - const fresh = newBlock(item.type === 'pagelink' ? 'p' : item.type) + // pagelink and embed both start life as a paragraph and become + // themselves only when a page has been chosen — dismissing the + // picker must leave a block you can type in, never a card pointing + // at nothing. + const fresh = newBlock(item.type === 'pagelink' || item.type === 'embed' ? 'p' : item.type) SPEC.get(fresh.type)?.init?.(fresh) this.store.commit(() => { page.blocks.push(fresh) }) this.paintPage() if (item.type === 'pagelink') this.insertPageCard(fresh.id) + else if (item.type === 'embed') this.insertEmbed(fresh.id) // the block is already a `link` — dismissing the dialog leaves an // empty card with its own way back in, never a half-made block else if (item.type === 'link') this.openLinkCard(fresh.id) @@ -2259,8 +2265,35 @@ export class Editor { return true } - /** Attach behaviour to a freshly painted page. */ + /** + * Attach behaviour to a freshly painted page. + * + * EMBEDDED CONTENT IS PARKED FOR THE DURATION, and that is not tidiness. + * Everything below sweeps the painted page by class or attribute — every + * `.sp-check`, every `.sp-b-code`, every table cell — and hangs a handler + * that commits through `store.block(id)`, which resolves ANY id in the + * document. An embed draws ANOTHER page's blocks inside this one, so without + * this a tick in an embedded checklist would commit to a page the editor is + * not showing, and a language chip would be appended into somebody else's + * paragraph. The renderer already strips `data-block-id` from that subtree; + * this closes the half that keys on classes instead. + * + * Detached and restored rather than filtered at each of the fifteen sweeps: + * one guarantee in one place cannot be forgotten by the sixteenth. + */ private wire(view: HTMLElement): void { + const parked: Array<[HTMLElement, Comment]> = [] + for (const body of view.querySelectorAll('.sp-embed-body')) { + const mark = document.createComment('embed') + body.replaceWith(mark) + parked.push([body, mark]) + } + try { this.wireOwn(view) } finally { + for (const [body, mark] of parked) mark.replaceWith(body) + } + } + + private wireOwn(view: HTMLElement): void { const s = this.store const title = view.querySelector('[data-page-title]') @@ -3657,6 +3690,7 @@ export class Editor { // the "/" that opened the menu is a command, not content if (blk && (blk.html ?? '').trim() === '/') blk.html = '' if (item.type === 'pagelink') this.insertPageCard(blockId) + else if (item.type === 'embed') this.insertEmbed(blockId) else if (item.type === 'link') { this.setType(blockId, 'link'); this.openLinkCard(blockId) } else this.setType(blockId, item.type) } @@ -3716,6 +3750,92 @@ export class Editor { }) } + /** + * THE ONE WRITER for an embed's target — the same rule as a link card's + * fields (applyLinkCard below), for the same reason. + * + * `html` is written alongside `page`, always, and it is a LINK to the target + * rather than a copy of anything: a build that has never heard of `embed` + * renders an unknown type's html (render.ts default case), so an older shell + * opening this file shows a link to the source page instead of a blank box. + * An embed written without it is a block that vanishes in last year's shell. + * + * A section returned to "the whole page" DELETES `anchor` rather than + * storing an empty one — a default is never bytes in the file (PLATFORM §3). + */ + private applyEmbed(blockId: string, pageId: string, anchor?: string): void { + const s = this.store + s.commit(() => { + const b = s.block(blockId) + const target = s.index.page.get(pageId) + if (!b) return + b.type = 'embed' + b.page = pageId + if (anchor) b.anchor = anchor + else delete b.anchor + b.html = `${escapeHtml(target?.title || t('Untitled'))}` + }) + this.paintPage() + } + + /** + * Choose what an embed shows: a page, or one section of it. + * + * ITS OWN PICKER rather than openPagePicker with a flag, because the list is + * a different list — every page AND every heading on it, so "show me the + * Rollout section of the plan" is one gesture instead of choose-then-hunt. + * The heading names come from embed.ts `headingsOf`, which is the same list + * `sectionOf` matches against, so a section you can pick here is a section + * that resolves. + */ + private insertEmbed(blockId: string): void { + const s = this.store + if (s.readOnly || this.reading) return + this.openOverlay(t('Embed a page'), (card, close) => { + const input = document.createElement('input') + input.className = 'sp-find' + input.placeholder = t('Find a page or a section…') + const list = el('ul', 'sp-results') + const row = (label: string, sub: string, then: () => void) => { + const li = document.createElement('li') + const b = document.createElement('button') + b.className = 'sp-result' + b.type = 'button' + b.innerHTML = + `${ICONS.page}` + + `${escapeHtml(label)}` + + (sub ? `${escapeHtml(sub)}` : '') + '' + b.addEventListener('click', () => { close(); then() }) + li.append(b) + list.append(li) + } + const run = () => { + const q = input.value.trim().toLowerCase() + list.innerHTML = '' + for (const p of s.doc.pages) { + // A PAGE CANNOT EMBED ITSELF, so it is not offered. The renderer + // stops that loop safely either way; offering it would be offering a + // placeholder. + if (p.id === s.pageId) continue + const title = p.title || t('Untitled') + const heads = headingsOf(p).filter((h) => !q || h.text.toLowerCase().includes(q)) + const hit = !q || title.toLowerCase().includes(q) + if (hit) row(title, t('The whole page'), () => this.applyEmbed(blockId, p.id)) + for (const h of (hit ? headingsOf(p) : heads)) { + row(`${title} › ${h.text}`, t('That section only'), + () => this.applyEmbed(blockId, p.id, h.text)) + } + if (list.childElementCount > 40) break + } + if (!list.childElementCount) list.append(el('li', 'sp-noresult', t('No page matches'))) + } + input.addEventListener('input', run) + card.append(input, list) + run() + setTimeout(() => input.focus(), 0) + }) + } + /** * THE ONE WRITER for a link card's fields. * diff --git a/spaces/src/embed.ts b/spaces/src/embed.ts new file mode 100644 index 00000000..b1e7859f --- /dev/null +++ b/spaces/src/embed.ts @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2026 The Bento authors +// TRANSCLUSION: a block that shows a LIVE view of another page. +// +// The whole of this file is PURE — no DOM, no imports that touch one — so +// scripts/test-spaces-model.ts can import it directly and assert the parts that +// actually break: the cycle guard, a dangling target, the section slice and the +// markdown round trip. The DOM is 40 lines in render.ts and nothing else. +// +// WHAT AN EMBED IS, in the format: +// +// { id, type: 'embed', page: '', anchor?: '', html: '' } +// +// `page` is the SAME field a pagelink carries and means the same thing — the +// target page id — so every sweep that already understands "this block is a +// reference to a page" (backlinks, extract, graft, validate) extends by one +// name in one condition rather than growing a second concept. That is what +// `isPageRef` below is for. +// +// THE SOURCE IS THE TRUTH. An embed stores a reference and never a copy: there +// is no cached content on the block, nothing to invalidate, and editing the +// source page changes every embed of it on the next paint. The one thing the +// block DOES carry is `html` — a plain link to the target — and that is not a +// copy either, it is the ADDITIVITY fallback: a build that has never heard of +// `embed` renders an unknown type's `html` (render.ts default case), so an +// older shell opening this file shows a link to the source page instead of a +// blank. Absent key = old behaviour; a default is never stored. + +import type { Block, Page, SpacesDoc } from './model.ts' + +/** + * How many pages deep an embed chain is followed. + * + * A CAP AS WELL AS the cycle check below, not instead of it. The cycle check + * catches A→B→A exactly; the cap catches the shape it cannot see — a hundred + * distinct pages each embedding the next, which is not a cycle and would still + * render a hundred pages into one. Three is the number at which an embed is + * still recognisably a quotation rather than a merge. + */ +export const EMBED_MAX_DEPTH = 3 + +/** + * Does this block REFERENCE a page by id? + * + * The one predicate for both types, because the alternative is what the + * codebase had before an embed existed: `b.type === 'pagelink' && typeof + * b.page === 'string'` written out in five files, four of which would have + * gone on quietly ignoring embeds. A grafted page whose embed pointed at + * nothing is exactly the failure that would not have been noticed until + * someone opened the export. + */ +export function isPageRef(b: Block): boolean { + return (b.type === 'pagelink' || b.type === 'embed') && typeof b.page === 'string' +} + +/** The heading an embed narrows to, or undefined for the whole page. Trimmed, + * and an empty string is ABSENT — a default is never stored (PLATFORM §3). */ +export function anchorOf(b: Block): string | undefined { + const raw = typeof b.anchor === 'string' ? b.anchor.trim() : '' + return raw || undefined +} + +/** + * Inline html → comparable plain text, with no DOM. + * + * render.ts parses INERT for this and is right to: it is handling markup that + * will be displayed. Nothing here is displayed — the output is compared to a + * heading name and thrown away — so a tag strip plus the five entities `esc` + * writes is exactly the job, and it keeps this module importable by node. + */ +function plain(html: unknown): string { + // A TAG CONTRIBUTES NOTHING, not a space — `## Rollout` is the + // heading "Rollout", which is what `textContent` says and what the author + // typed as `## Roll**out**`. Substituting a space instead would make a + // heading with any inline markup in it unmatchable by the name it exports as. + return decodeEntities(String(html ?? '').replace(/<[^>]*>/g, '')) + .replace(/\s+/g, ' ') + .trim() +} + +/** The five entities `esc` writes, back again. `&` LAST, or `&lt;` + * would decode twice and turn stored text into a tag. */ +const decodeEntities = (s: string): string => + s.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"') + .replace(/�*39;/g, "'").replace(/'/g, "'").replace(/&/g, '&') + +/** Heading names match the way a wikilink target does: case- and + * whitespace-insensitive. `## Getting started` is `[[Page#getting started]]`. */ +const headingKey = (s: unknown): string => plain(s).toLowerCase() + +const HEADINGS: Record = { h1: 1, h2: 2, h3: 3 } + +/** + * A block's heading rank, or 0 for anything that is not a heading. + * + * `Object.hasOwn`, not `in`: `b.type` comes out of a file someone mailed you, + * `HEADINGS` is an object literal, and `'toString' in HEADINGS` is TRUE — the + * lookup would hand back a native function. This app has shipped that exact + * bug twice. + * + * ONE copy, read by `sectionOf` and by `headingsOf`, because a guard written + * twice is a guard that gets fixed once. It is also the only shape in which a + * test can prove the guard: through `sectionOf` alone a native function fails + * `> 0` and the miss looks identical either way, so the second reader is where + * the sabotage is visible. + */ +const rankOf = (b: Block): number => (Object.hasOwn(HEADINGS, b.type) ? HEADINGS[b.type] : 0) + +/** + * The blocks under one heading, INCLUDING the heading itself. + * + * The section ends at the next heading of the SAME OR HIGHER rank, which is + * what a reader means by "that section": an h2 takes its h3s with it and stops + * at the next h2 or at an h1. Returns null when no heading matches — never the + * whole page, because silently showing four pages where one section was asked + * for is worse than saying the section is gone. + * + * `HEADINGS` is a plain object keyed on `b.type`, which comes out of a file + * someone mailed you, so the lookup is `Object.hasOwn`: `'toString' in + * HEADINGS` is true and would hand back a native function, and this app has + * shipped that exact bug twice. + */ +export function sectionOf(page: Page, anchor: string): Block[] | null { + const want = headingKey(anchor) + if (!want) return null + const at = page.blocks.findIndex((b) => rankOf(b) > 0 && headingKey(b.html) === want) + if (at < 0) return null + const level = rankOf(page.blocks[at]) + const out: Block[] = [page.blocks[at]] + for (let i = at + 1; i < page.blocks.length; i++) { + const r = rankOf(page.blocks[i]) + if (r > 0 && r <= level) break + out.push(page.blocks[i]) + } + return out +} + +/** + * The headings an embed can be narrowed to, in page order. + * + * THE PICKER AND THE RESOLVER READ THE SAME LIST. The editor offers these + * names and `sectionOf` matches against them by the same `headingKey`, so a + * section you can choose is a section that resolves — the alternative is a + * picker that can offer a name the resolver then reports as missing, which is + * a bug nobody would find until someone used a heading with a `&` in it. + */ +export function headingsOf(page: Page): Array<{ id: string; level: number; text: string }> { + const out: Array<{ id: string; level: number; text: string }> = [] + for (const b of page.blocks) { + const level = rankOf(b) + if (!level) continue + const text = plain(b.html) + if (text) out.push({ id: b.id, level, text }) + } + return out +} + +/** Why an embed has nothing to show. Each one RENDERS as a named placeholder — + * a blank box tells the reader nothing and looks like a bug in the app. */ +export type EmbedProblem = 'no-target' | 'missing' | 'cycle' | 'depth' | 'no-section' + +export type EmbedView = + | { ok: true; page: Page; blocks: Block[]; anchor?: string } + | { ok: false; why: EmbedProblem; target: string; anchor?: string; page?: Page } + +/** + * What one embed block shows, given where the renderer already is. + * + * `chain` is the pages currently open above this block, host page first. It is + * the cycle guard and the depth guard at once, and it is a PARAMETER rather + * than module state because two surfaces render at the same time (the editor + * canvas and the still preview) and a shared counter between them would be a + * race that only shows up in a saved thumbnail. + */ +export function viewEmbed(b: Block, doc: SpacesDoc, chain: readonly string[] = []): EmbedView { + const target = typeof b.page === 'string' ? b.page : '' + const anchor = anchorOf(b) + if (!target) return { ok: false, why: 'no-target', target: '', ...(anchor ? { anchor } : {}) } + // A LINEAR SCAN over an array, deliberately, not a lookup in an object keyed + // by page id: `target` is document data and an object would answer + // `__proto__` and `toString` with something that is not a page. + const page = doc.pages.find((p) => p.id === target) + if (!page) return { ok: false, why: 'missing', target, ...(anchor ? { anchor } : {}) } + if (chain.includes(target)) return { ok: false, why: 'cycle', target, page, ...(anchor ? { anchor } : {}) } + if (chain.length > EMBED_MAX_DEPTH) return { ok: false, why: 'depth', target, page, ...(anchor ? { anchor } : {}) } + if (anchor) { + const cut = sectionOf(page, anchor) + if (!cut) return { ok: false, why: 'no-section', target, anchor, page } + return { ok: true, page, blocks: cut, anchor } + } + return { ok: true, page, blocks: page.blocks } +} + +/** + * Can `from` be reached from `to` by following embeds? — the validator's + * question, which the renderer's chain cannot answer because the renderer only + * ever sees one path at a time. + * + * Breadth-first over a visited set, so it terminates on a document that + * already cycles. That matters: this runs on files people hand-edit, and a + * "does this cycle" check that hangs on a cycling document is worse than none. + */ +export function embedReaches(doc: SpacesDoc, from: string, to: string): boolean { + const byId = new Map(doc.pages.map((p) => [p.id, p])) + const seen = new Set() + const queue: string[] = [to] + while (queue.length) { + const at = queue.shift()! + if (at === from) return true + if (seen.has(at)) continue + seen.add(at) + for (const b of byId.get(at)?.blocks ?? []) { + if (b.type === 'embed' && typeof b.page === 'string' && !seen.has(b.page)) queue.push(b.page) + } + } + return false +} + +// ---- markdown -------------------------------------------------------------- + +/** + * `![[Page]]` / `![[Page#Section]]` on a line of its OWN. + * + * Obsidian's embed syntax, and the reason this feature exists: markdown.ts + * parsed it already and downgraded every one to a plain link, so importing a + * vault lost every embed with no warning. + * + * INLINE `![[x]]` IS STILL A LINK and that is not a compromise — a block + * cannot live inside a sentence, and markdown.ts's inline pass is where a + * sentence is built. Only a whole line becomes a block. + * + * An `![[picture.png]]` line is an IMAGE, and this function does not know + * that: markdown.ts tests `imageOf` first, which is the one place that already + * owns the image-extension list. Adding a second copy of that list here is how + * the two would come to disagree. + */ +export function parseEmbedLine(line: string): { target: string; anchor?: string } | null { + const m = /^!\[\[([^\]]+)\]\]$/.exec(line.trim()) + if (!m) return null + // `|alias` is Obsidian's display text. An embed shows the page, so there is + // nothing for an alias to be the text OF; it is dropped, not stored. + const bar = m[1].indexOf('|') + const whole = (bar < 0 ? m[1] : m[1].slice(0, bar)).trim() + const hash = whole.indexOf('#') + const target = (hash < 0 ? whole : whole.slice(0, hash)).trim() + // `^block-id` is Obsidian's block anchor. This model has no block anchors, so + // the embed lands on the page — the same rule linkKey already applies to + // links, rather than a second, quieter one. + const anchor = hash < 0 ? '' : whole.slice(hash + 1).replace(/^\^.*$/, '').trim() + if (!target) return null + return { target, ...(anchor ? { anchor } : {}) } +} + +/** An embed as the markdown it was imported from. The exporter's half of the + * round trip: `![[Title]]`, or `![[Title#Section]]`. */ +export function embedToMd(title: string | undefined, anchor: string | undefined): string { + const name = (title ?? '?').trim() || '?' + return `![[${name}${anchor ? `#${anchor}` : ''}]]` +} + +/** + * Turn the importer's resolved links into real embed targets. + * + * The parser cannot set `page`: at parse time a wikilink names a FILE and the + * pages do not exist yet. So an embed block arrives carrying only its `html` — + * the `#w/` placeholder link every other block carries — and planImport's + * existing sweep resolves that html to `#p/` exactly as it resolves a link + * in a paragraph. This reads the answer back off the html. + * + * A TARGET THAT WAS NOT IN THE IMPORT BECOMES A PARAGRAPH, and this is the + * deliberate half: `resolveWikilinks` has already rewritten the html to the + * literal `[[Name]]` the author typed, which is a true statement about a note + * that is not here. An embed with no target would be a permanent error card in + * a document that never had one — the vault said "show me that note", the note + * did not come, and the honest result is the text saying so. + */ +export function linkEmbeds(pages: Page[]): { linked: number; dropped: number } { + let linked = 0 + let dropped = 0 + for (const p of pages) { + for (const b of p.blocks) { + if (b.type !== 'embed') continue + const m = /href="#p\/([^"]+)"/.exec(String(b.html ?? '')) + if (m) { b.page = decodeEntities(m[1]); linked++; continue } + dropped++ + b.type = 'p' + delete b.page + delete b.anchor + } + } + return { linked, dropped } +} diff --git a/spaces/src/i18n/de.ts b/spaces/src/i18n/de.ts index 29921266..f63e1553 100644 --- a/spaces/src/i18n/de.ts +++ b/spaces/src/i18n/de.ts @@ -18,7 +18,9 @@ export const de: Catalog = { "A folder of notes, or another space": "Ein Ordner mit Notizen oder ein anderer Space", "A link with nothing in it yet": "Ein Link, in dem noch nichts steht", "A list of every page, in order": "Eine Liste aller Seiten, der Reihe nach", + "A live view of another page": "Eine Live-Ansicht einer anderen Seite", "A live viewer: follows every edit as it happens but can never change this space — the relay enforces it.": "Ein Live-Viewer: folgt jeder Änderung, kann diesen Space aber nie verändern — vom Relay erzwungen.", + "A new block": "Ein neuer Block", "A note, tip or warning": "Eine Notiz, ein Tipp oder eine Warnung", "A page cannot contain itself": "Eine Seite kann sich nicht selbst enthalten", "A password encrypts the document inside the file. There is no recovery — lose it and the space is gone.": "Ein Passwort verschlüsselt das Dokument in der Datei. Es gibt keine Wiederherstellung — verlierst du es, ist der Space weg.", @@ -32,6 +34,7 @@ export const de: Catalog = { "About bento/spaces — version, updates, language, password": "Über bento/spaces — Version, Updates, Sprache, Passwort", "About this space": "Über diesen Space", "Access reset — only copies saved from now on can join": "Zugriff zurückgesetzt — nur ab jetzt gespeicherte Kopien können beitreten", + "Add": "Hinzufügen", "Add a block below": "Block darunter hinzufügen", "Add a card": "Karte hinzufügen", "Add a card that opens a page": "Karte hinzufügen, die eine Seite öffnet", @@ -39,9 +42,12 @@ export const de: Catalog = { "Add a link": "Link hinzufügen", "Add a page": "Seite hinzufügen", "Add a picture": "Bild hinzufügen", + "Add a property": "Eine Eigenschaft hinzufügen", "Add a row below": "Zeile darunter einfügen", "Add below": "Darunter einfügen", "Add pages under": "Seiten einfügen unter", + "Add property…": "Eigenschaft hinzufügen…", + "Added {name}": "{name} hinzugefügt", "Address of a video or audio file": "Adresse einer Video- oder Audiodatei", "Adds status, priority, assignee, estimate": "Fügt Status, Priorität, Bearbeiter:in und Schätzung hinzu", "All text. Nothing is embedded, so this file is as small as a space gets.": "Nur Text. Nichts ist eingebettet, also ist diese Datei so klein, wie ein Space nur sein kann.", @@ -100,6 +106,7 @@ export const de: Catalog = { "Choose a space…": "Space auswählen…", "Choose the field the columns come from": "Feld wählen, aus dem die Spalten kommen", "Choose the order": "Reihenfolge wählen", + "Choose which pages this view holds": "Wählen, welche Seiten diese Ansicht enthält", "Choose…": "Auswählen…", "Clear filter": "Filter zurücksetzen", "Clear formatting": "Formatierung entfernen", @@ -135,6 +142,7 @@ export const de: Catalog = { "Cover": "Titelbild", "Create “{name}”": "„{name}“ erstellen", "Dark": "Dunkel", + "Date": "Datum", "Default": "Standard", "Delete": "Löschen", "Delete “{name}”?": "„{name}“ löschen?", @@ -142,6 +150,7 @@ export const de: Catalog = { "Descending": "Absteigend", "Description": "Beschreibung", "Discard": "Verwerfen", + "Dismiss": "Schließen", "Divider": "Trennlinie", "Document": "Dokument", "Document JSON copied": "Dokument-JSON kopiert", @@ -166,10 +175,14 @@ export const de: Catalog = { "Editing": "Bearbeiten", "Editor": "Bearbeiter", "Editor copy saved — recipients join live with edit access": "Bearbeiter-Kopie gespeichert — Empfänger treten live mit Schreibzugriff bei", + "Embed a page": "Seite einbetten", + "Embedded from {page}": "Eingebettet aus {page}", "Embedded in the file": "In der Datei eingebettet", + "Embeds are not followed deeper than this.": "Einbettungen werden nicht tiefer verfolgt.", "Encrypt the document inside this file": "Das Dokument in dieser Datei verschlüsseln", "Enter the password to open it.": "Gib das Passwort ein, um ihn zu öffnen.", "Every page opens wide on this screen from now on": "Auf diesem Bildschirm öffnen ab jetzt alle Seiten breit", + "Every page with a status": "Jede Seite mit einem Status", "Every page, and what links to what": "Alle Seiten, und was worauf verweist", "Every page, as one .md file": "Jede Seite, in einer .md-Datei", "Everything in this space is replaced by what you paste. ⌘Z undoes it, but only while this window stays open.": "Alles in diesem Space wird durch das ersetzt, was Sie einfügen. ⌘Z macht das rückgängig, aber nur solange dieses Fenster geöffnet bleibt.", @@ -185,11 +198,16 @@ export const de: Catalog = { "Filter": "Filtern", "Filter blocks…": "Blöcke filtern…", "Find": "Suchen", + "Find a page or a section…": "Seite oder Abschnitt suchen…", + "Find and replace": "Suchen und ersetzen", "Find in this space…": "In diesem Space suchen…", "Find or create a page…": "Seite finden oder erstellen…", "Fit": "Einpassen", "Following — view only": "Folgt mit — nur Ansicht", + "Formatting": "Formatierung", "Full width": "Volle Breite", + "Gallery": "Galerie", + "Getting around": "Navigieren", "Graph": "Graph", "Gray": "Grau", "Green": "Grün", @@ -203,6 +221,7 @@ export const de: Catalog = { "Hide the page list ([)": "Seitenliste ausblenden ([)", "Highlight": "Hervorhebung", "Highlight — ⇧⌘H": "Hervorhebung — ⇧⌘H", + "History": "Verlauf", "Icon": "Symbol", "Image": "Bild", "Image added ({size})": "Bild hinzugefügt ({size})", @@ -219,6 +238,7 @@ export const de: Catalog = { "Include archived pages": "Archivierte Seiten einbeziehen", "Include the image files and they are embedded too. An image this browser cannot open is kept as its path rather than as a broken picture.": "Nimm die Bilddateien mit, dann werden sie gleich eingebettet. Ein Bild, das dieser Browser nicht öffnen kann, bleibt als Pfad stehen statt als kaputtes Bild.", "Include the pages nested under it": "Die darunter verschachtelten Seiten einschließen", + "Indent, or move back out": "Einrücken, oder wieder heraus", "Inline code — ⌘E": "Inline-Code — ⌘E", "Insert": "Einfügen", "Insert a block — text, headings, lists, code, images": "Block einfügen — Text, Überschriften, Listen, Code, Bilder", @@ -232,16 +252,21 @@ export const de: Catalog = { "Italic — ⌘I": "Kursiv — ⌘I", "Journal date": "Journaldatum", "Key-verified identity": "Schlüssel-verifizierte Identität", + "Keyboard shortcuts": "Tastaturkürzel", + "Labels": "Labels", "Language": "Sprache", "Language follows whoever opens the file. It is never written into the document.": "Die Sprache richtet sich danach, wer die Datei öffnet. Sie wird nie ins Dokument geschrieben.", "Language — what this block is highlighted as": "Sprache — wie dieser Block hervorgehoben wird", "Last saved": "Zuletzt gespeichert", "Launch check couldn't reach the release server ({m}). Check manually below.": "Startprüfung konnte den Release-Server nicht erreichen ({m}). Unten manuell prüfen.", "Leave it empty to use the tone mark": "Leer lassen, um das Symbol der Art zu verwenden", + "Leave the reading view": "Leseansicht verlassen", "Light": "Hell", "Link": "Link", "Link address": "Linkadresse", "Link card": "Link-Karte", + "Link the selected words": "Die markierten Wörter verlinken", + "Link to another page": "Auf eine andere Seite verlinken", "Link to page": "Seite verlinken", "Link to the web": "Link ins Web", "Link — ⌘K": "Link — ⌘K", @@ -265,11 +290,14 @@ export const de: Catalog = { "Move down": "Nach unten", "Move up": "Nach oben", "Muted": "Stumm", + "Name": "Name", "Name this canvas": "Diese Leinwand benennen", + "Nested under": "Verschachtelt unter", "New issue": "Neues Issue", "New page": "Neue Seite", "New page (⌘⌥N)": "Neue Seite (⌘⌥N)", "New page inside": "Neue Unterseite", + "New property": "Neue Eigenschaft", "New to Bento? Find templates, the gallery and the AI editing guide at {home} — or ⭐ it on {gh}.": "Neu bei Bento? Vorlagen, die Galerie und den KI-Bearbeitungsleitfaden findest du auf {home} — oder gib ⭐ auf {gh}.", "Next (⏎)": "Weiter (⏎)", "No Markdown files in that selection": "Keine Markdown-Dateien in dieser Auswahl", @@ -277,7 +305,10 @@ export const de: Catalog = { "No field here has options to group by": "Kein Feld hier hat Optionen zum Gruppieren", "No issues match this filter.": "Keine Issues entsprechen diesem Filter.", "No issues yet. Add a status field to any page and it appears here.": "Noch keine Issues. Gib einer Seite ein Status-Feld, dann erscheint sie hier.", + "No page matches": "Keine Seite passt", "No pages yet": "Noch keine Seiten", + "No section named {name} on this page.": "Kein Abschnitt namens {name} auf dieser Seite.", + "No versions yet — they build up as you write and save.": "Noch keine Versionen — sie sammeln sich beim Schreiben und Speichern an.", "Nobody else is here yet": "Noch niemand sonst da", "Not in this file: it needs the network, and the site is told when someone opens the page": "Nicht in dieser Datei: das Abspielen braucht das Netz, und die Website erfährt, dass jemand diese Seite geöffnet hat", "Not live — turns on when you share": "Nicht live — startet beim Teilen", @@ -290,6 +321,7 @@ export const de: Catalog = { "Nothing on this canvas yet.": "Auf dieser Leinwand ist noch nichts.", "Nothing to draw yet — link two pages with [[ and they will appear here.": "Noch nichts zu zeichnen — verknüpfe zwei Seiten mit [[ und sie erscheinen hier.", "Now an issue": "Jetzt ein Issue", + "Number": "Zahl", "Numbered list": "Nummerierte Liste", "Off by default — they were archived for a reason": "Standardmäßig aus — sie wurden aus einem Grund archiviert", "Offline mode is on — nothing leaves this computer.": "Der Offline-Modus ist aktiv — nichts verlässt diesen Computer.", @@ -310,7 +342,9 @@ export const de: Catalog = { "Page": "Seite", "Page options": "Seiten-Optionen", "Pages": "Seiten", + "Pages nested under this one": "Seiten unterhalb dieser Seite", "Pages open at their normal width again": "Seiten öffnen wieder in normaler Breite", + "Pages that have this property": "Seiten mit dieser Eigenschaft", "Pages — show or hide the page list": "Seiten — Seitenliste ein- oder ausblenden", "Password": "Passwort", "Password removed. Save to write the space unencrypted.": "Passwort entfernt. Speichere, um den Space unverschlüsselt zu schreiben.", @@ -319,6 +353,7 @@ export const de: Catalog = { "Paste document JSON here…": "Dokument-JSON hier einfügen…", "Paste or type a link": "Link einfügen oder eingeben", "People in this space": "Personen in diesem Space", + "Person": "Person", "Picture": "Bild", "Picture…": "Bild…", "Pink": "Rosa", @@ -381,6 +416,7 @@ export const de: Catalog = { "Resolve": "Erledigen", "Restore": "Wiederherstellen", "Restore to the page list": "Zurück in die Seitenliste", + "Restored the version from {when} — ⌘Z undoes it": "Version vom {when} wiederhergestellt — ⌘Z macht das rückgängig.", "Room for a board or a table": "Platz für ein Board oder eine Tabelle", "Rows": "Zeilen", "Rows and columns": "Zeilen und Spalten", @@ -395,12 +431,18 @@ export const de: Catalog = { "Saving…": "Speichere…", "Search": "Suche", "Search all pages (⌘K)": "Alle Seiten durchsuchen (⌘K)", + "Search all pages, with nothing selected": "Alle Seiten durchsuchen, wenn nichts markiert ist", "Search all pages…": "Alle Seiten durchsuchen…", "Search this space": "Diesen Space durchsuchen", + "Select": "Auswahl", "Set a password…": "Passwort festlegen…", "Share this space": "Diesen Space teilen", "Show as a board": "Als Board anzeigen", + "Show as a gallery": "Als Galerie anzeigen", "Show as a list": "Als Liste anzeigen", + "Show as a table": "Als Tabelle anzeigen", + "Show or hide properties": "Eigenschaften ein- oder ausblenden", + "Show or hide the page list": "Seitenliste ein- oder ausblenden", "Show playback controls to the reader": "Wiedergabesteuerung für Lesende anzeigen", "Show properties (])": "Eigenschaften einblenden (])", "Show the page list ([)": "Seitenliste einblenden ([)", @@ -432,7 +474,10 @@ export const de: Catalog = { "That is not a bento/spaces document": "Das ist kein bento/spaces-Dokument", "That is {n} files — importing them all may take a moment. Continue?": "Das sind {n} Dateien — der Import kann einen Moment dauern. Fortfahren?", "That needs to be an http or https address": "Das muss eine http- oder https-Adresse sein", + "That section only": "Nur dieser Abschnitt", "That space is password-protected. Open it, then export the pages you want.": "Dieser Space ist passwortgeschützt. Öffnen Sie ihn und exportieren Sie dort die gewünschten Seiten.", + "That version could not be read": "Diese Version konnte nicht gelesen werden", + "The block menu, on an empty line": "Das Blockmenü, in einer leeren Zeile", "The counterpart of Copy document JSON: edit a space in another tool, then bring it back.": "Das Gegenstück zu „Dokument-JSON kopieren“: Bearbeiten Sie einen Space in einem anderen Werkzeug und bringen Sie ihn zurück.", "The day after": "Der Tag danach", "The day before": "Der Tag davor", @@ -446,8 +491,10 @@ export const de: Catalog = { "The pages without the editing tools": "Die Seiten ohne die Bearbeitungswerkzeuge", "The rest name notes that were not in the selection, and are left as text.": "Der Rest nennt Notizen, die nicht in der Auswahl waren, und bleibt als Text stehen.", "The theme follows whoever opens the file. It is never written into the document.": "Das Design richtet sich danach, wer die Datei öffnet. Es wird nie ins Dokument geschrieben.", + "The whole page": "Die ganze Seite", "The whole space": "Der ganze Space", "The whole space, or just this page": "Der ganze Space oder nur diese Seite", + "The workspace": "Der Arbeitsbereich", "Then send someone the file": "Schick die Datei danach jemandem", "These windows are on this computer. Start a session to work with someone elsewhere.": "Diese Fenster sind auf diesem Computer. Starte eine Sitzung, um mit jemand anderem zu arbeiten.", "This block has no settings of its own — its content is all of it.": "Dieser Block hat keine eigenen Einstellungen — sein Inhalt ist alles.", @@ -455,6 +502,8 @@ export const de: Catalog = { "This browser cannot write back to the file, so every save makes a new copy. Chrome and Edge on a computer can save in place.": "Dieser Browser kann nicht in die Datei zurückschreiben, deshalb entsteht bei jedem Speichern eine neue Kopie. Chrome und Edge auf dem Computer können direkt speichern.", "This clip is {size} and travels inside the file, making it that much bigger for everyone you send it to. Embed it anyway?": "Dieser Clip ist {size} groß und reist in der Datei mit — die Datei wird für jeden, dem du sie schickst, entsprechend größer. Trotzdem einbetten?", "This copy goes offline; the others carry on": "Diese Kopie geht offline; die anderen machen weiter", + "This embed is inside itself — the loop stops here.": "Diese Einbettung enthält sich selbst – die Schleife endet hier.", + "This embed points at a page that is not here.": "Diese Einbettung zeigt auf eine Seite, die nicht hier ist.", "This file": "Diese Datei", "This file carries its own app — it works offline, forever, as is.": "Diese Datei trägt ihre eigene App — sie funktioniert offline, für immer, so wie sie ist.", "This file could not be opened": "Diese Datei konnte nicht geöffnet werden", @@ -466,10 +515,12 @@ export const de: Catalog = { "This is a reading copy. It opens for reading; nothing you do here changes the file.": "Dies ist eine Lesekopie. Sie öffnet sich zum Lesen; nichts, was du hier tust, ändert die Datei.", "This is a view-only copy — it follows the live session but can’t change this space.": "Dies ist eine Nur-Lesen-Kopie — sie folgt der Live-Sitzung, kann diesen Space aber nicht ändern.", "This is not a bento/spaces document — {detail}.": "Dies ist kein bento/spaces-Dokument — {detail}.", + "This list": "Diese Liste", "This live session has run out of room. Your change is saved in your copy, but collaborators won’t see it.": "Diese Live-Sitzung hat keinen Platz mehr. Deine Änderung ist in deiner Kopie gespeichert, aber Mitarbeitende sehen sie nicht.", "This page only": "Nur diese Seite", "This space has no live session to follow": "Dieser Space hat keine Live-Sitzung zum Folgen", "This space has no pages.": "Dieser Space hat keine Seiten.", + "This space is encrypted, so no versions are kept.": "Dieser Space ist verschlüsselt, deshalb werden keine Versionen aufbewahrt.", "This space is encrypted. Saves stay encrypted.": "Dieser Space ist verschlüsselt. Speichern bleibt verschlüsselt.", "This space is locked": "Dieser Space ist gesperrt", "This window is still running v{v} — reload to finish. A v{v} backup was downloaded.": "Dieses Fenster läuft noch mit v{v} — zum Abschließen neu laden. Ein v{v}-Backup wurde heruntergeladen.", @@ -483,6 +534,7 @@ export const de: Catalog = { "Today": "Heute", "Today's journal": "Heutiges Journal", "Toggle": "Klappblock", + "Toggle section": "Abschnitt auf- oder zuklappen", "Tone": "Art", "Too many changes at once — live sync is catching up.": "Zu viele Änderungen auf einmal — die Live-Synchronisierung holt auf.", "Top level": "Oberste Ebene", @@ -491,6 +543,7 @@ export const de: Catalog = { "Underline": "Unterstrichen", "Underline — ⌘U": "Unterstrichen — ⌘U", "Undo (⌘Z)": "Rückgängig (⌘Z)", + "Undo, redo": "Rückgängig, wiederherstellen", "Unlock": "Entsperren", "Unlocked, but the document inside could not be read.": "Entsperrt, aber das Dokument darin konnte nicht gelesen werden.", "Unsaved changes from a previous session were found.": "Nicht gespeicherte Änderungen aus einer früheren Sitzung gefunden.", @@ -508,6 +561,7 @@ export const de: Catalog = { "Verifying…": "Verifiziere…", "Version {v} is available.": "Version {v} ist verfügbar.", "Version, language, password, exports": "Version, Sprache, Passwort, Export", + "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "Versionen liegen nur in diesem Browser — nie in der Datei, nie online. Das Wiederherstellen lässt sich rückgängig machen.", "Video": "Video", "Video from {host}": "Video von {host}", "Video or audio": "Video oder Audio", @@ -521,12 +575,14 @@ export const de: Catalog = { "What is in the picture": "Was auf dem Bild zu sehen ist", "What this is": "Worum es sich handelt", "What’s new →": "Neuerungen →", + "Which pages": "Welche Seiten", "Wide": "Breit", "Width": "Breite", "Width %": "Breite %", "Width in the text column": "Breite in der Textspalte", "Windows on this computer still sync; turn offline mode off in About to work with someone elsewhere.": "Fenster auf diesem Computer synchronisieren sich weiterhin; deaktiviere den Offline-Modus unter „Über“, um mit jemandem anderswo zu arbeiten.", "Words": "Wörter", + "Writing": "Schreiben", "Wrong password — try again": "Falsches Passwort — bitte erneut versuchen", "Yellow": "Gelb", "You have the newest version ({v}).": "Du hast die neueste Version ({v}).", @@ -539,6 +595,7 @@ export const de: Catalog = { "bento/spaces {version} · {pages} page(s), {blocks} block(s). The document, the editor and the search are all in this one file.": "bento/spaces {version} · {pages} Seiten, {blocks} Blöcke. Das Dokument, der Editor und die Suche stecken alle in dieser einen Datei.", "format v{v}": "Format v{v}", "just now": "gerade eben", + "most recent": "neueste", "none": "keine", "you": "du", "you: {name} ✎": "Sie: {name} ✎", @@ -547,6 +604,8 @@ export const de: Catalog = { "{name} joined": "{name} ist beigetreten", "{name} left": "{name} hat verlassen", "{name} was removed": "{name} wurde entfernt", + "{n} block": "{n} Block", + "{n} blocks": "{n} Blöcke", "{n} connected": "{n} verbunden", "{n} duplicate or missing id(s) were repaired so links and pages resolve.": "{n} doppelte oder fehlende IDs wurden repariert, damit Links und Seiten wieder gefunden werden.", "{n} embedded image(s) and clip(s) account for {size} of that — {pct}%. Everything else is text.": "{n} eingebettete Bild(er) und Clip(s) machen davon {size} aus — {pct}%. Alles andere ist Text.", @@ -558,9 +617,11 @@ export const de: Catalog = { "{n} image(s) go with them; the rest stay here.": "{n} Bild(er) gehen mit; der Rest bleibt hier.", "{n} image(s) point at the web. Nothing loads until a reader asks.": "{n} Bild(er) zeigen ins Web. Nichts wird geladen, bevor ein Leser darum bittet.", "{n} image(s) were left as paths because embedding stopped there.": "{n} Bild(er) blieben als Pfad stehen, weil das Einbetten dort endete.", + "{n} link to this page": "{n} Link auf diese Seite", "{n} link(s) named pages that were not in that file, and are kept as text.": "{n} Link(s) nannten Seiten, die nicht in dieser Datei waren, und bleiben als Text erhalten.", "{n} link(s) point outside them and are kept as text naming the page they meant.": "{n} Link(s) zeigen nach außen und bleiben als Text erhalten, der die gemeinte Seite nennt.", "{n} link(s) to it will stop working.": "{n} Links darauf funktionieren nicht mehr.", + "{n} links to this page": "{n} Links auf diese Seite", "{n} note name(s) appear more than once, so links naming them all went to the first.": "{n} Notiznamen kommen mehrfach vor — Links mit diesem Namen führen alle zur ersten.", "{n} of {total} wikilink(s) resolved.": "{n} von {total} Wikilink(s) aufgelöst.", "{n} page(s) had frontmatter, kept verbatim in a folded block.": "{n} Seite(n) hatten Frontmatter — wortgetreu in einem zugeklappten Block erhalten.", @@ -569,6 +630,8 @@ export const de: Catalog = { "{n} table(s) imported, with their column alignment.": "{n} Tabelle(n) importiert, samt Spaltenausrichtung.", "{n} table(s) kept as text: there is no table block in this format yet.": "{n} Tabelle(n) als Text übernommen: Dieses Format hat noch keinen Tabellenblock.", "{n} unresolved comment(s)": "{n} offene(r) Kommentar(e)", + "{n} word": "{n} Wort", + "{n} words": "{n} Wörter", "{n}d ago": "vor {n} T", "{n}h ago": "vor {n} Std", "{n}m ago": "vor {n} Min", @@ -577,56 +640,4 @@ export const de: Catalog = { "{pages} page(s) and {blocks} block(s) will travel.": "{pages} Seite(n) und {blocks} Block/Blöcke kommen mit.", "{pages} pages · {links} links": "{pages} Seiten · {links} Verknüpfungen", "⌘Z removes the imported pages again.": "Mit ⌘Z verschwinden die importierten Seiten wieder.", - "History": "Verlauf", - "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "Versionen liegen nur in diesem Browser — nie in der Datei, nie online. Das Wiederherstellen lässt sich rückgängig machen.", - "This space is encrypted, so no versions are kept.": "Dieser Space ist verschlüsselt, deshalb werden keine Versionen aufbewahrt.", - "No versions yet — they build up as you write and save.": "Noch keine Versionen — sie sammeln sich beim Schreiben und Speichern an.", - "most recent": "neueste", - "That version could not be read": "Diese Version konnte nicht gelesen werden", - "Restored the version from {when} — ⌘Z undoes it": "Version vom {when} wiederhergestellt — ⌘Z macht das rückgängig.", - "Dismiss": "Schließen", - "Toggle section": "Abschnitt auf- oder zuklappen", - "{n} block": "{n} Block", - "{n} blocks": "{n} Blöcke", - "{n} word": "{n} Wort", - "{n} words": "{n} Wörter", - "{n} link to this page": "{n} Link auf diese Seite", - "{n} links to this page": "{n} Links auf diese Seite", - "A new block": "Ein neuer Block", - "Find and replace": "Suchen und ersetzen", - "Formatting": "Formatierung", - "Getting around": "Navigieren", - "Indent, or move back out": "Einrücken, oder wieder heraus", - "Keyboard shortcuts": "Tastaturkürzel", - "Leave the reading view": "Leseansicht verlassen", - "Link the selected words": "Die markierten Wörter verlinken", - "Link to another page": "Auf eine andere Seite verlinken", - "Search all pages, with nothing selected": "Alle Seiten durchsuchen, wenn nichts markiert ist", - "Show or hide properties": "Eigenschaften ein- oder ausblenden", - "Show or hide the page list": "Seitenliste ein- oder ausblenden", - "The block menu, on an empty line": "Das Blockmenü, in einer leeren Zeile", - "The workspace": "Der Arbeitsbereich", - "This list": "Diese Liste", - "Undo, redo": "Rückgängig, wiederherstellen", - "Writing": "Schreiben", - "Add": "Hinzufügen", - "Add a property": "Eine Eigenschaft hinzufügen", - "Add property…": "Eigenschaft hinzufügen…", - "Added {name}": "{name} hinzugefügt", - "Name": "Name", - "New property": "Neue Eigenschaft", - "Choose which pages this view holds": "Wählen, welche Seiten diese Ansicht enthält", - "Every page with a status": "Jede Seite mit einem Status", - "Nested under": "Verschachtelt unter", - "Pages nested under this one": "Seiten unterhalb dieser Seite", - "Pages that have this property": "Seiten mit dieser Eigenschaft", - "Which pages": "Welche Seiten", - "Gallery": "Galerie", - "Show as a gallery": "Als Galerie anzeigen", - "Show as a table": "Als Tabelle anzeigen", - "Select": "Auswahl", - "Number": "Zahl", - "Date": "Datum", - "Person": "Person", - "Labels": "Labels", } diff --git a/spaces/src/i18n/es.ts b/spaces/src/i18n/es.ts index 772b824f..fc560460 100644 --- a/spaces/src/i18n/es.ts +++ b/spaces/src/i18n/es.ts @@ -18,7 +18,9 @@ export const es: Catalog = { "A folder of notes, or another space": "Una carpeta de notas u otro espacio", "A link with nothing in it yet": "Un enlace todavía sin nada", "A list of every page, in order": "Una lista de todas las páginas, en orden", + "A live view of another page": "Una vista en vivo de otra página", "A live viewer: follows every edit as it happens but can never change this space — the relay enforces it.": "Un visor en vivo: sigue cada edición al instante pero nunca puede modificar este Space — el relé lo garantiza.", + "A new block": "Un bloque nuevo", "A note, tip or warning": "Una nota, un consejo o una advertencia", "A page cannot contain itself": "Una página no puede contenerse a sí misma", "A password encrypts the document inside the file. There is no recovery — lose it and the space is gone.": "Una contraseña cifra el documento dentro del archivo. No hay forma de recuperarla — si la pierdes, el espacio desaparece.", @@ -32,6 +34,7 @@ export const es: Catalog = { "About bento/spaces — version, updates, language, password": "Acerca de bento/spaces: versión, actualizaciones, idioma, contraseña", "About this space": "Acerca de este espacio", "Access reset — only copies saved from now on can join": "Acceso restablecido — solo las copias guardadas a partir de ahora podrán unirse", + "Add": "Añadir", "Add a block below": "Añadir un bloque debajo", "Add a card": "Añadir una tarjeta", "Add a card that opens a page": "Añadir una tarjeta que abre una página", @@ -39,9 +42,12 @@ export const es: Catalog = { "Add a link": "Añadir un enlace", "Add a page": "Añadir una página", "Add a picture": "Añadir una imagen", + "Add a property": "Añadir una propiedad", "Add a row below": "Añadir una fila debajo", "Add below": "Añadir debajo", "Add pages under": "Añadir las páginas debajo de", + "Add property…": "Añadir propiedad…", + "Added {name}": "Se añadió {name}", "Address of a video or audio file": "Dirección de un archivo de vídeo o audio", "Adds status, priority, assignee, estimate": "Añade estado, prioridad, responsable y estimación", "All text. Nothing is embedded, so this file is as small as a space gets.": "Todo es texto. No hay nada incrustado, así que este archivo es lo más pequeño que puede ser un espacio.", @@ -100,6 +106,7 @@ export const es: Catalog = { "Choose a space…": "Elegir un espacio…", "Choose the field the columns come from": "Elige el campo del que salen las columnas", "Choose the order": "Elige el orden", + "Choose which pages this view holds": "Elige qué páginas contiene esta vista", "Choose…": "Elegir…", "Clear filter": "Borrar el filtro", "Clear formatting": "Borrar formato", @@ -135,6 +142,7 @@ export const es: Catalog = { "Cover": "Portada", "Create “{name}”": "Crear «{name}»", "Dark": "Oscuro", + "Date": "Fecha", "Default": "Predeterminado", "Delete": "Eliminar", "Delete “{name}”?": "¿Eliminar «{name}»?", @@ -142,6 +150,7 @@ export const es: Catalog = { "Descending": "Descendente", "Description": "Descripción", "Discard": "Descartar", + "Dismiss": "Descartar", "Divider": "Separador", "Document": "Documento", "Document JSON copied": "JSON del documento copiado", @@ -166,10 +175,14 @@ export const es: Catalog = { "Editing": "Edición", "Editor": "Editor", "Editor copy saved — recipients join live with edit access": "Copia de editor guardada — quien la reciba se une en vivo con acceso de edición", + "Embed a page": "Insertar una página", + "Embedded from {page}": "Insertado desde {page}", "Embedded in the file": "Incrustado en el archivo", + "Embeds are not followed deeper than this.": "Las inserciones no se siguen más allá de aquí.", "Encrypt the document inside this file": "Cifrar el documento dentro de este archivo", "Enter the password to open it.": "Introduce la contraseña para abrirlo.", "Every page opens wide on this screen from now on": "A partir de ahora todas las páginas se abren anchas en esta pantalla", + "Every page with a status": "Todas las páginas con estado", "Every page, and what links to what": "Todas las páginas, y qué enlaza con qué", "Every page, as one .md file": "Todas las páginas, en un solo archivo .md", "Everything in this space is replaced by what you paste. ⌘Z undoes it, but only while this window stays open.": "Todo lo que hay en este espacio se sustituye por lo que pegues. ⌘Z lo deshace, pero solo mientras esta ventana siga abierta.", @@ -185,11 +198,16 @@ export const es: Catalog = { "Filter": "Filtrar", "Filter blocks…": "Filtrar bloques…", "Find": "Buscar", + "Find a page or a section…": "Buscar una página o una sección…", + "Find and replace": "Buscar y reemplazar", "Find in this space…": "Buscar en este espacio…", "Find or create a page…": "Buscar o crear una página…", "Fit": "Ajustar", "Following — view only": "Siguiendo — solo lectura", + "Formatting": "Formato", "Full width": "Ancho completo", + "Gallery": "Galería", + "Getting around": "Moverse", "Graph": "Grafo", "Gray": "Gris", "Green": "Verde", @@ -203,6 +221,7 @@ export const es: Catalog = { "Hide the page list ([)": "Ocultar la lista de páginas ([)", "Highlight": "Resaltado", "Highlight — ⇧⌘H": "Resaltado — ⇧⌘H", + "History": "Historial", "Icon": "Icono", "Image": "Imagen", "Image added ({size})": "Imagen añadida ({size})", @@ -219,6 +238,7 @@ export const es: Catalog = { "Include archived pages": "Incluir páginas archivadas", "Include the image files and they are embedded too. An image this browser cannot open is kept as its path rather than as a broken picture.": "Incluye los archivos de imagen y también se incrustan. Una imagen que este navegador no puede abrir se conserva como su ruta, no como una imagen rota.", "Include the pages nested under it": "Incluir las páginas anidadas debajo", + "Indent, or move back out": "Sangrar, o volver a salir", "Inline code — ⌘E": "Código en línea — ⌘E", "Insert": "Insertar", "Insert a block — text, headings, lists, code, images": "Insertar un bloque — texto, encabezados, listas, código, imágenes", @@ -232,16 +252,21 @@ export const es: Catalog = { "Italic — ⌘I": "Cursiva — ⌘I", "Journal date": "Fecha del diario", "Key-verified identity": "Identidad verificada por clave", + "Keyboard shortcuts": "Atajos de teclado", + "Labels": "Etiquetas", "Language": "Idioma", "Language follows whoever opens the file. It is never written into the document.": "El idioma sigue a quien abre el archivo. Nunca se escribe en el documento.", "Language — what this block is highlighted as": "Lenguaje — cómo se colorea este bloque", "Last saved": "Última vez guardado", "Launch check couldn't reach the release server ({m}). Check manually below.": "La comprobación inicial no alcanzó el servidor ({m}). Comprueba manualmente abajo.", "Leave it empty to use the tone mark": "Déjalo vacío para usar el símbolo del tipo", + "Leave the reading view": "Salir de la vista de lectura", "Light": "Claro", "Link": "Enlace", "Link address": "Dirección del enlace", "Link card": "Tarjeta de enlace", + "Link the selected words": "Enlazar las palabras seleccionadas", + "Link to another page": "Enlazar a otra página", "Link to page": "Enlazar a una página", "Link to the web": "Enlace a la web", "Link — ⌘K": "Enlace — ⌘K", @@ -265,11 +290,14 @@ export const es: Catalog = { "Move down": "Bajar", "Move up": "Subir", "Muted": "Silenciado", + "Name": "Nombre", "Name this canvas": "Nombra este lienzo", + "Nested under": "Anidadas en", "New issue": "Nueva incidencia", "New page": "Nueva página", "New page (⌘⌥N)": "Nueva página (⌘⌥N)", "New page inside": "Nueva página dentro", + "New property": "Nueva propiedad", "New to Bento? Find templates, the gallery and the AI editing guide at {home} — or ⭐ it on {gh}.": "¿Nuevo en Bento? Encuentra plantillas, la galería y la guía de edición con IA en {home} — o dale ⭐ en {gh}.", "Next (⏎)": "Siguiente (⏎)", "No Markdown files in that selection": "No hay archivos Markdown en esa selección", @@ -277,7 +305,10 @@ export const es: Catalog = { "No field here has options to group by": "Ningún campo de aquí tiene opciones para agrupar", "No issues match this filter.": "Ninguna incidencia coincide con este filtro.", "No issues yet. Add a status field to any page and it appears here.": "Aún no hay incidencias. Añade un campo de estado a cualquier página y aparecerá aquí.", + "No page matches": "Ninguna página coincide", "No pages yet": "Aún no hay páginas", + "No section named {name} on this page.": "No hay ninguna sección llamada {name} en esta página.", + "No versions yet — they build up as you write and save.": "Aún no hay versiones: se acumulan a medida que escribes y guardas.", "Nobody else is here yet": "Todavía no hay nadie más", "Not in this file: it needs the network, and the site is told when someone opens the page": "No está en este archivo: necesita la red, y ese sitio sabrá que alguien ha abierto esta página", "Not live — turns on when you share": "Sin conexión — se activa al compartir", @@ -290,6 +321,7 @@ export const es: Catalog = { "Nothing on this canvas yet.": "Aún no hay nada en este lienzo.", "Nothing to draw yet — link two pages with [[ and they will appear here.": "Todavía no hay nada que dibujar — enlaza dos páginas con [[ y aparecerán aquí.", "Now an issue": "Ahora es una incidencia", + "Number": "Número", "Numbered list": "Lista numerada", "Off by default — they were archived for a reason": "Desactivado por defecto — por algo se archivaron", "Offline mode is on — nothing leaves this computer.": "El modo sin conexión está activo: nada sale de este equipo.", @@ -310,7 +342,9 @@ export const es: Catalog = { "Page": "Página", "Page options": "Opciones de la página", "Pages": "Páginas", + "Pages nested under this one": "Páginas anidadas bajo esta", "Pages open at their normal width again": "Las páginas vuelven a su anchura normal", + "Pages that have this property": "Páginas que tienen esta propiedad", "Pages — show or hide the page list": "Páginas — mostrar u ocultar la lista de páginas", "Password": "Contraseña", "Password removed. Save to write the space unencrypted.": "Contraseña eliminada. Guarda para escribir el espacio sin cifrar.", @@ -319,6 +353,7 @@ export const es: Catalog = { "Paste document JSON here…": "Pega aquí el JSON del documento…", "Paste or type a link": "Pega o escribe un enlace", "People in this space": "Personas en este espacio", + "Person": "Persona", "Picture": "Imagen", "Picture…": "Imagen…", "Pink": "Rosa", @@ -381,6 +416,7 @@ export const es: Catalog = { "Resolve": "Resolver", "Restore": "Restaurar", "Restore to the page list": "Restaurar a la lista de páginas", + "Restored the version from {when} — ⌘Z undoes it": "Se restauró la versión del {when}: ⌘Z lo deshace.", "Room for a board or a table": "Espacio para un tablero o una tabla", "Rows": "Filas", "Rows and columns": "Filas y columnas", @@ -395,12 +431,18 @@ export const es: Catalog = { "Saving…": "Guardando…", "Search": "Buscar", "Search all pages (⌘K)": "Buscar en todas las páginas (⌘K)", + "Search all pages, with nothing selected": "Buscar en todas las páginas, sin nada seleccionado", "Search all pages…": "Buscar en todas las páginas…", "Search this space": "Buscar en este espacio", + "Select": "Selección", "Set a password…": "Establecer una contraseña…", "Share this space": "Compartir este espacio", "Show as a board": "Mostrar como tablero", + "Show as a gallery": "Mostrar como galería", "Show as a list": "Mostrar como lista", + "Show as a table": "Mostrar como tabla", + "Show or hide properties": "Mostrar u ocultar las propiedades", + "Show or hide the page list": "Mostrar u ocultar la lista de páginas", "Show playback controls to the reader": "Mostrar los controles de reproducción a quien lee", "Show properties (])": "Mostrar propiedades (])", "Show the page list ([)": "Mostrar la lista de páginas ([)", @@ -432,7 +474,10 @@ export const es: Catalog = { "That is not a bento/spaces document": "Eso no es un documento de bento/spaces", "That is {n} files — importing them all may take a moment. Continue?": "Son {n} archivos: importarlos todos puede tardar un momento. ¿Continuar?", "That needs to be an http or https address": "Tiene que ser una dirección http o https", + "That section only": "Solo esa sección", "That space is password-protected. Open it, then export the pages you want.": "Ese espacio está protegido con contraseña. Ábrelo y exporta desde allí las páginas que quieras.", + "That version could not be read": "No se pudo leer esa versión", + "The block menu, on an empty line": "El menú de bloques, en una línea vacía", "The counterpart of Copy document JSON: edit a space in another tool, then bring it back.": "La contraparte de Copiar el JSON del documento: edita un espacio en otra herramienta y vuelve a traerlo.", "The day after": "El día siguiente", "The day before": "El día anterior", @@ -446,8 +491,10 @@ export const es: Catalog = { "The pages without the editing tools": "Las páginas sin las herramientas de edición", "The rest name notes that were not in the selection, and are left as text.": "El resto nombra notas que no estaban en la selección y se dejan como texto.", "The theme follows whoever opens the file. It is never written into the document.": "El tema sigue a quien abre el archivo. Nunca se escribe en el documento.", + "The whole page": "La página entera", "The whole space": "Todo el espacio", "The whole space, or just this page": "Todo el espacio, o solo esta página", + "The workspace": "El espacio de trabajo", "Then send someone the file": "Después envía el archivo a alguien", "These windows are on this computer. Start a session to work with someone elsewhere.": "Estas ventanas están en este ordenador. Inicia una sesión para trabajar con alguien en otro sitio.", "This block has no settings of its own — its content is all of it.": "Este bloque no tiene ajustes propios: su contenido es todo lo que hay.", @@ -455,6 +502,8 @@ export const es: Catalog = { "This browser cannot write back to the file, so every save makes a new copy. Chrome and Edge on a computer can save in place.": "Este navegador no puede reescribir el archivo, así que cada guardado crea una copia nueva. Chrome y Edge en un ordenador sí pueden guardar en el sitio.", "This clip is {size} and travels inside the file, making it that much bigger for everyone you send it to. Embed it anyway?": "Este clip pesa {size} y viaja dentro del archivo, que crecerá otro tanto para todos a quienes se lo envíes. ¿Incrustarlo de todos modos?", "This copy goes offline; the others carry on": "Esta copia se desconecta; las demás siguen", + "This embed is inside itself — the loop stops here.": "Esta inserción se contiene a sí misma: el bucle se detiene aquí.", + "This embed points at a page that is not here.": "Esta inserción apunta a una página que no está aquí.", "This file": "Este archivo", "This file carries its own app — it works offline, forever, as is.": "Este archivo lleva su propia aplicación — funciona sin conexión, para siempre, tal cual.", "This file could not be opened": "No se pudo abrir este archivo", @@ -466,10 +515,12 @@ export const es: Catalog = { "This is a reading copy. It opens for reading; nothing you do here changes the file.": "Esta es una copia de lectura. Se abre para leer; nada de lo que hagas aquí cambia el archivo.", "This is a view-only copy — it follows the live session but can’t change this space.": "Esta es una copia de solo lectura — sigue la sesión en vivo pero no puede modificar este Space.", "This is not a bento/spaces document — {detail}.": "Esto no es un documento de bento/spaces — {detail}.", + "This list": "Esta lista", "This live session has run out of room. Your change is saved in your copy, but collaborators won’t see it.": "Esta sesión en vivo se ha quedado sin espacio. Tu cambio se guarda en tu copia, pero los colaboradores no lo verán.", "This page only": "Solo esta página", "This space has no live session to follow": "Este Space no tiene sesión en vivo que seguir", "This space has no pages.": "Este espacio no tiene páginas.", + "This space is encrypted, so no versions are kept.": "Este espacio está cifrado, así que no se guarda ninguna versión.", "This space is encrypted. Saves stay encrypted.": "Este espacio está cifrado. Los guardados siguen cifrados.", "This space is locked": "Este espacio está bloqueado", "This window is still running v{v} — reload to finish. A v{v} backup was downloaded.": "Esta ventana sigue en v{v} — recarga para terminar. Se descargó una copia de seguridad v{v}.", @@ -483,6 +534,7 @@ export const es: Catalog = { "Today": "Hoy", "Today's journal": "Diario de hoy", "Toggle": "Bloque plegable", + "Toggle section": "Mostrar u ocultar la sección", "Tone": "Tono", "Too many changes at once — live sync is catching up.": "Demasiados cambios a la vez — la sincronización en vivo se está poniendo al día.", "Top level": "Nivel superior", @@ -491,6 +543,7 @@ export const es: Catalog = { "Underline": "Subrayado", "Underline — ⌘U": "Subrayado — ⌘U", "Undo (⌘Z)": "Deshacer (⌘Z)", + "Undo, redo": "Deshacer, rehacer", "Unlock": "Desbloquear", "Unlocked, but the document inside could not be read.": "Desbloqueado, pero no se pudo leer el documento de dentro.", "Unsaved changes from a previous session were found.": "Se encontraron cambios sin guardar de una sesión anterior.", @@ -508,6 +561,7 @@ export const es: Catalog = { "Verifying…": "Verificando…", "Version {v} is available.": "Versión {v} disponible.", "Version, language, password, exports": "Versión, idioma, contraseña, exportaciones", + "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "Las versiones se guardan solo en este navegador: nunca en el archivo ni en línea. Restaurar se puede deshacer.", "Video": "Vídeo", "Video from {host}": "Vídeo de {host}", "Video or audio": "Vídeo o audio", @@ -521,12 +575,14 @@ export const es: Catalog = { "What is in the picture": "Qué hay en la imagen", "What this is": "Qué es esto", "What’s new →": "Novedades →", + "Which pages": "Qué páginas", "Wide": "Ancho", "Width": "Anchura", "Width %": "Ancho %", "Width in the text column": "Ancho en la columna de texto", "Windows on this computer still sync; turn offline mode off in About to work with someone elsewhere.": "Las ventanas de este equipo siguen sincronizándose; desactiva el modo sin conexión en «Acerca de» para trabajar con alguien en otro lugar.", "Words": "Palabras", + "Writing": "Escribir", "Wrong password — try again": "Contraseña incorrecta: inténtalo de nuevo", "Yellow": "Amarillo", "You have the newest version ({v}).": "Tienes la versión más reciente ({v}).", @@ -539,6 +595,7 @@ export const es: Catalog = { "bento/spaces {version} · {pages} page(s), {blocks} block(s). The document, the editor and the search are all in this one file.": "bento/spaces {version} · {pages} página(s), {blocks} bloque(s). El documento, el editor y la búsqueda están todos en este único archivo.", "format v{v}": "formato v{v}", "just now": "ahora mismo", + "most recent": "más reciente", "none": "ninguna", "you": "tú", "you: {name} ✎": "tú: {name} ✎", @@ -547,6 +604,8 @@ export const es: Catalog = { "{name} joined": "{name} se ha unido", "{name} left": "{name} se ha ido", "{name} was removed": "{name} fue eliminado", + "{n} block": "{n} bloque", + "{n} blocks": "{n} bloques", "{n} connected": "{n} conectados", "{n} duplicate or missing id(s) were repaired so links and pages resolve.": "Se repararon {n} id(s) duplicados o ausentes para que los enlaces y las páginas funcionen.", "{n} embedded image(s) and clip(s) account for {size} of that — {pct}%. Everything else is text.": "{n} imagen(es) y clip(s) incrustados suman {size} de ese total: el {pct}%. Todo lo demás es texto.", @@ -558,9 +617,11 @@ export const es: Catalog = { "{n} image(s) go with them; the rest stay here.": "{n} imagen(es) van con ellas; el resto se queda aquí.", "{n} image(s) point at the web. Nothing loads until a reader asks.": "{n} imagen(es) apuntan a la web. No se carga nada hasta que un lector lo pida.", "{n} image(s) were left as paths because embedding stopped there.": "{n} imagen(es) quedaron como rutas porque la incrustación se detuvo ahí.", + "{n} link to this page": "{n} enlace a esta página", "{n} link(s) named pages that were not in that file, and are kept as text.": "{n} enlace(s) nombraban páginas que no estaban en ese archivo y se conservan como texto.", "{n} link(s) point outside them and are kept as text naming the page they meant.": "{n} enlace(s) apuntan fuera y se conservan como texto que nombra la página a la que se referían.", "{n} link(s) to it will stop working.": "{n} enlace(s) a ella dejarán de funcionar.", + "{n} links to this page": "{n} enlaces a esta página", "{n} note name(s) appear more than once, so links naming them all went to the first.": "{n} nombre(s) de nota se repiten, así que los enlaces con ese nombre fueron todos al primero.", "{n} of {total} wikilink(s) resolved.": "{n} de {total} wikilink(s) resueltos.", "{n} page(s) had frontmatter, kept verbatim in a folded block.": "{n} página(s) tenían frontmatter, conservado tal cual en un bloque plegado.", @@ -569,6 +630,8 @@ export const es: Catalog = { "{n} table(s) imported, with their column alignment.": "{n} tabla(s) importada(s), con la alineación de sus columnas.", "{n} table(s) kept as text: there is no table block in this format yet.": "{n} tabla(s) conservadas como texto: este formato aún no tiene bloque de tabla.", "{n} unresolved comment(s)": "{n} comentario(s) sin resolver", + "{n} word": "{n} palabra", + "{n} words": "{n} palabras", "{n}d ago": "hace {n} d", "{n}h ago": "hace {n} h", "{n}m ago": "hace {n} min", @@ -577,56 +640,4 @@ export const es: Catalog = { "{pages} page(s) and {blocks} block(s) will travel.": "Viajarán {pages} página(s) y {blocks} bloque(s).", "{pages} pages · {links} links": "{pages} páginas · {links} enlaces", "⌘Z removes the imported pages again.": "⌘Z vuelve a quitar las páginas importadas.", - "History": "Historial", - "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "Las versiones se guardan solo en este navegador: nunca en el archivo ni en línea. Restaurar se puede deshacer.", - "This space is encrypted, so no versions are kept.": "Este espacio está cifrado, así que no se guarda ninguna versión.", - "No versions yet — they build up as you write and save.": "Aún no hay versiones: se acumulan a medida que escribes y guardas.", - "most recent": "más reciente", - "That version could not be read": "No se pudo leer esa versión", - "Restored the version from {when} — ⌘Z undoes it": "Se restauró la versión del {when}: ⌘Z lo deshace.", - "Dismiss": "Descartar", - "Toggle section": "Mostrar u ocultar la sección", - "{n} block": "{n} bloque", - "{n} blocks": "{n} bloques", - "{n} word": "{n} palabra", - "{n} words": "{n} palabras", - "{n} link to this page": "{n} enlace a esta página", - "{n} links to this page": "{n} enlaces a esta página", - "A new block": "Un bloque nuevo", - "Find and replace": "Buscar y reemplazar", - "Formatting": "Formato", - "Getting around": "Moverse", - "Indent, or move back out": "Sangrar, o volver a salir", - "Keyboard shortcuts": "Atajos de teclado", - "Leave the reading view": "Salir de la vista de lectura", - "Link the selected words": "Enlazar las palabras seleccionadas", - "Link to another page": "Enlazar a otra página", - "Search all pages, with nothing selected": "Buscar en todas las páginas, sin nada seleccionado", - "Show or hide properties": "Mostrar u ocultar las propiedades", - "Show or hide the page list": "Mostrar u ocultar la lista de páginas", - "The block menu, on an empty line": "El menú de bloques, en una línea vacía", - "The workspace": "El espacio de trabajo", - "This list": "Esta lista", - "Undo, redo": "Deshacer, rehacer", - "Writing": "Escribir", - "Add": "Añadir", - "Add a property": "Añadir una propiedad", - "Add property…": "Añadir propiedad…", - "Added {name}": "Se añadió {name}", - "Name": "Nombre", - "New property": "Nueva propiedad", - "Choose which pages this view holds": "Elige qué páginas contiene esta vista", - "Every page with a status": "Todas las páginas con estado", - "Nested under": "Anidadas en", - "Pages nested under this one": "Páginas anidadas bajo esta", - "Pages that have this property": "Páginas que tienen esta propiedad", - "Which pages": "Qué páginas", - "Gallery": "Galería", - "Show as a gallery": "Mostrar como galería", - "Show as a table": "Mostrar como tabla", - "Select": "Selección", - "Number": "Número", - "Date": "Fecha", - "Person": "Persona", - "Labels": "Etiquetas", } diff --git a/spaces/src/i18n/fr.ts b/spaces/src/i18n/fr.ts index a7042f28..2cf6ecb7 100644 --- a/spaces/src/i18n/fr.ts +++ b/spaces/src/i18n/fr.ts @@ -18,7 +18,9 @@ export const fr: Catalog = { "A folder of notes, or another space": "Un dossier de notes, ou un autre espace", "A link with nothing in it yet": "Un lien encore vide", "A list of every page, in order": "La liste de toutes les pages, dans l’ordre", + "A live view of another page": "Une vue en direct d’une autre page", "A live viewer: follows every edit as it happens but can never change this space — the relay enforces it.": "Un visualiseur en direct : suit chaque modification mais ne peut jamais changer cet espace — le relais l’applique.", + "A new block": "Un nouveau bloc", "A note, tip or warning": "Une note, un conseil ou un avertissement", "A page cannot contain itself": "Une page ne peut pas se contenir elle-même", "A password encrypts the document inside the file. There is no recovery — lose it and the space is gone.": "Un mot de passe chiffre le document à l’intérieur du fichier. Il n’y a aucune récupération — perdez-le et l’espace est perdu.", @@ -32,6 +34,7 @@ export const fr: Catalog = { "About bento/spaces — version, updates, language, password": "À propos de bento/spaces — version, mises à jour, langue, mot de passe", "About this space": "À propos de cet espace", "Access reset — only copies saved from now on can join": "Accès réinitialisé — seules les copies enregistrées désormais pourront rejoindre", + "Add": "Ajouter", "Add a block below": "Ajouter un bloc en dessous", "Add a card": "Ajouter une carte", "Add a card that opens a page": "Ajouter une carte qui ouvre une page", @@ -39,9 +42,12 @@ export const fr: Catalog = { "Add a link": "Ajouter un lien", "Add a page": "Ajouter une page", "Add a picture": "Ajouter une image", + "Add a property": "Ajouter une propriété", "Add a row below": "Ajouter une ligne en dessous", "Add below": "Ajouter en dessous", "Add pages under": "Ajouter les pages sous", + "Add property…": "Ajouter une propriété…", + "Added {name}": "{name} ajoutée", "Address of a video or audio file": "Adresse d’un fichier vidéo ou audio", "Adds status, priority, assignee, estimate": "Ajoute statut, priorité, responsable et estimation", "All text. Nothing is embedded, so this file is as small as a space gets.": "Que du texte. Rien n’est intégré, donc ce fichier est aussi léger qu’un espace peut l’être.", @@ -100,6 +106,7 @@ export const fr: Catalog = { "Choose a space…": "Choisir un espace…", "Choose the field the columns come from": "Choisir le champ dont viennent les colonnes", "Choose the order": "Choisir l’ordre", + "Choose which pages this view holds": "Choisir les pages que contient cette vue", "Choose…": "Choisir…", "Clear filter": "Effacer le filtre", "Clear formatting": "Effacer la mise en forme", @@ -135,6 +142,7 @@ export const fr: Catalog = { "Cover": "Couverture", "Create “{name}”": "Créer « {name} »", "Dark": "Sombre", + "Date": "Date", "Default": "Par défaut", "Delete": "Supprimer", "Delete “{name}”?": "Supprimer « {name} » ?", @@ -142,6 +150,7 @@ export const fr: Catalog = { "Descending": "Décroissant", "Description": "Description", "Discard": "Ignorer", + "Dismiss": "Ignorer", "Divider": "Séparateur", "Document": "Document", "Document JSON copied": "JSON du document copié", @@ -166,10 +175,14 @@ export const fr: Catalog = { "Editing": "Édition", "Editor": "Éditeur", "Editor copy saved — recipients join live with edit access": "Copie éditeur enregistrée — les destinataires rejoignent la session en direct avec accès en écriture", + "Embed a page": "Insérer une page", + "Embedded from {page}": "Inséré depuis {page}", "Embedded in the file": "Intégré au fichier", + "Embeds are not followed deeper than this.": "Les insertions ne sont pas suivies plus loin.", "Encrypt the document inside this file": "Chiffrer le document à l’intérieur de ce fichier", "Enter the password to open it.": "Saisissez le mot de passe pour l’ouvrir.", "Every page opens wide on this screen from now on": "Désormais toutes les pages s’ouvrent en large sur cet écran", + "Every page with a status": "Toutes les pages ayant un statut", "Every page, and what links to what": "Toutes les pages, et ce qui pointe vers quoi", "Every page, as one .md file": "Toutes les pages, en un seul fichier .md", "Everything in this space is replaced by what you paste. ⌘Z undoes it, but only while this window stays open.": "Tout ce que contient cet espace est remplacé par ce que vous collez. ⌘Z annule l’opération, mais seulement tant que cette fenêtre reste ouverte.", @@ -185,11 +198,16 @@ export const fr: Catalog = { "Filter": "Filtrer", "Filter blocks…": "Filtrer les blocs…", "Find": "Rechercher", + "Find a page or a section…": "Rechercher une page ou une section…", + "Find and replace": "Rechercher et remplacer", "Find in this space…": "Rechercher dans cet espace…", "Find or create a page…": "Trouver ou créer une page…", "Fit": "Ajuster", "Following — view only": "Suivi en cours — lecture seule", + "Formatting": "Mise en forme", "Full width": "Pleine largeur", + "Gallery": "Galerie", + "Getting around": "Se déplacer", "Graph": "Graphe", "Gray": "Gris", "Green": "Vert", @@ -203,6 +221,7 @@ export const fr: Catalog = { "Hide the page list ([)": "Masquer la liste des pages ([)", "Highlight": "Surlignage", "Highlight — ⇧⌘H": "Surlignage — ⇧⌘H", + "History": "Historique", "Icon": "Icône", "Image": "Image", "Image added ({size})": "Image ajoutée ({size})", @@ -219,6 +238,7 @@ export const fr: Catalog = { "Include archived pages": "Inclure les pages archivées", "Include the image files and they are embedded too. An image this browser cannot open is kept as its path rather than as a broken picture.": "Ajoutez les fichiers image et ils sont intégrés eux aussi. Une image que ce navigateur ne peut pas ouvrir reste sous forme de chemin, plutôt qu’en image cassée.", "Include the pages nested under it": "Inclure les pages imbriquées en dessous", + "Indent, or move back out": "Indenter, ou revenir en arrière", "Inline code — ⌘E": "Code en ligne — ⌘E", "Insert": "Insérer", "Insert a block — text, headings, lists, code, images": "Insérer un bloc — texte, titres, listes, code, images", @@ -232,16 +252,21 @@ export const fr: Catalog = { "Italic — ⌘I": "Italique — ⌘I", "Journal date": "Date du journal", "Key-verified identity": "Identité vérifiée par clé", + "Keyboard shortcuts": "Raccourcis clavier", + "Labels": "Étiquettes", "Language": "Langue", "Language follows whoever opens the file. It is never written into the document.": "La langue suit la personne qui ouvre le fichier. Elle n’est jamais écrite dans le document.", "Language — what this block is highlighted as": "Langage — la coloration de ce bloc", "Last saved": "Dernier enregistrement", "Launch check couldn't reach the release server ({m}). Check manually below.": "La vérification au démarrage n'a pas atteint le serveur ({m}). Vérifiez manuellement ci-dessous.", "Leave it empty to use the tone mark": "Laissez vide pour utiliser le symbole du type", + "Leave the reading view": "Quitter la vue lecture", "Light": "Clair", "Link": "Lien", "Link address": "Adresse du lien", "Link card": "Carte de lien", + "Link the selected words": "Lier les mots sélectionnés", + "Link to another page": "Lier vers une autre page", "Link to page": "Lier à une page", "Link to the web": "Lien vers le web", "Link — ⌘K": "Lien — ⌘K", @@ -265,11 +290,14 @@ export const fr: Catalog = { "Move down": "Descendre", "Move up": "Monter", "Muted": "Muet", + "Name": "Nom", "Name this canvas": "Nommez ce canevas", + "Nested under": "Imbriquées sous", "New issue": "Nouveau ticket", "New page": "Nouvelle page", "New page (⌘⌥N)": "Nouvelle page (⌘⌥N)", "New page inside": "Nouvelle page à l’intérieur", + "New property": "Nouvelle propriété", "New to Bento? Find templates, the gallery and the AI editing guide at {home} — or ⭐ it on {gh}.": "Nouveau sur Bento ? Trouvez des modèles, la galerie et le guide d’édition par IA sur {home} — ou mettez une ⭐ sur {gh}.", "Next (⏎)": "Suivant (⏎)", "No Markdown files in that selection": "Aucun fichier Markdown dans cette sélection", @@ -277,7 +305,10 @@ export const fr: Catalog = { "No field here has options to group by": "Aucun champ ici n’a d’options pour grouper", "No issues match this filter.": "Aucun ticket ne correspond à ce filtre.", "No issues yet. Add a status field to any page and it appears here.": "Aucun ticket pour l’instant. Ajoutez un champ statut à une page et elle apparaîtra ici.", + "No page matches": "Aucune page ne correspond", "No pages yet": "Aucune page pour l’instant", + "No section named {name} on this page.": "Aucune section nommée {name} sur cette page.", + "No versions yet — they build up as you write and save.": "Pas encore de versions — elles s’accumulent à mesure que vous écrivez et enregistrez.", "Nobody else is here yet": "Personne d’autre pour l’instant", "Not in this file: it needs the network, and the site is told when someone opens the page": "N’est pas dans ce fichier : la lecture nécessite le réseau, et ce site saura que quelqu’un a ouvert cette page", "Not live — turns on when you share": "Hors ligne — s’active quand vous partagez", @@ -290,6 +321,7 @@ export const fr: Catalog = { "Nothing on this canvas yet.": "Rien sur ce canevas pour l’instant.", "Nothing to draw yet — link two pages with [[ and they will appear here.": "Rien à dessiner pour l’instant — reliez deux pages avec [[ et elles apparaîtront ici.", "Now an issue": "C’est désormais un ticket", + "Number": "Nombre", "Numbered list": "Liste numérotée", "Off by default — they were archived for a reason": "Désactivé par défaut — elles ont été archivées pour une raison", "Offline mode is on — nothing leaves this computer.": "Le mode hors ligne est activé — rien ne quitte cet ordinateur.", @@ -310,7 +342,9 @@ export const fr: Catalog = { "Page": "Page", "Page options": "Options de la page", "Pages": "Pages", + "Pages nested under this one": "Pages imbriquées sous celle-ci", "Pages open at their normal width again": "Les pages retrouvent leur largeur normale", + "Pages that have this property": "Pages qui ont cette propriété", "Pages — show or hide the page list": "Pages — afficher ou masquer la liste", "Password": "Mot de passe", "Password removed. Save to write the space unencrypted.": "Mot de passe supprimé. Enregistrez pour écrire l’espace non chiffré.", @@ -319,6 +353,7 @@ export const fr: Catalog = { "Paste document JSON here…": "Collez le JSON du document ici…", "Paste or type a link": "Collez ou saisissez un lien", "People in this space": "Personnes dans cet espace", + "Person": "Personne", "Picture": "Image", "Picture…": "Image…", "Pink": "Rose", @@ -381,6 +416,7 @@ export const fr: Catalog = { "Resolve": "Résoudre", "Restore": "Restaurer", "Restore to the page list": "Restaurer dans la liste des pages", + "Restored the version from {when} — ⌘Z undoes it": "Version du {when} restaurée — ⌘Z annule.", "Room for a board or a table": "De la place pour un tableau", "Rows": "Lignes", "Rows and columns": "Lignes et colonnes", @@ -395,12 +431,18 @@ export const fr: Catalog = { "Saving…": "Enregistrement…", "Search": "Rechercher", "Search all pages (⌘K)": "Rechercher dans toutes les pages (⌘K)", + "Search all pages, with nothing selected": "Rechercher dans toutes les pages, sans sélection", "Search all pages…": "Rechercher dans toutes les pages…", "Search this space": "Rechercher dans cet espace", + "Select": "Sélection", "Set a password…": "Définir un mot de passe…", "Share this space": "Partager cet espace", "Show as a board": "Afficher en tableau", + "Show as a gallery": "Afficher en galerie", "Show as a list": "Afficher en liste", + "Show as a table": "Afficher en tableau", + "Show or hide properties": "Afficher ou masquer les propriétés", + "Show or hide the page list": "Afficher ou masquer la liste des pages", "Show playback controls to the reader": "Afficher les contrôles de lecture au lecteur", "Show properties (])": "Afficher les propriétés (])", "Show the page list ([)": "Afficher la liste des pages ([)", @@ -432,7 +474,10 @@ export const fr: Catalog = { "That is not a bento/spaces document": "Ce n’est pas un document bento/spaces", "That is {n} files — importing them all may take a moment. Continue?": "Cela fait {n} fichiers — tout importer peut prendre un moment. Continuer ?", "That needs to be an http or https address": "Il faut une adresse http ou https", + "That section only": "Cette section uniquement", "That space is password-protected. Open it, then export the pages you want.": "Cet espace est protégé par un mot de passe. Ouvrez-le, puis exportez-en les pages voulues.", + "That version could not be read": "Cette version n’a pas pu être lue", + "The block menu, on an empty line": "Le menu de blocs, sur une ligne vide", "The counterpart of Copy document JSON: edit a space in another tool, then bring it back.": "Le pendant de Copier le JSON du document : modifiez un espace dans un autre outil, puis ramenez-le ici.", "The day after": "Le lendemain", "The day before": "La veille", @@ -446,8 +491,10 @@ export const fr: Catalog = { "The pages without the editing tools": "Les pages sans les outils d'édition", "The rest name notes that were not in the selection, and are left as text.": "Les autres désignent des notes absentes de la sélection et restent en texte.", "The theme follows whoever opens the file. It is never written into the document.": "Le thème suit la personne qui ouvre le fichier. Il n’est jamais écrit dans le document.", + "The whole page": "La page entière", "The whole space": "Tout l’espace", "The whole space, or just this page": "Tout l’espace, ou seulement cette page", + "The workspace": "L’espace de travail", "Then send someone the file": "Envoyez ensuite le fichier à quelqu’un", "These windows are on this computer. Start a session to work with someone elsewhere.": "Ces fenêtres sont sur cet ordinateur. Démarrez une session pour travailler avec quelqu’un ailleurs.", "This block has no settings of its own — its content is all of it.": "Ce bloc n’a pas de réglages à lui — son contenu, c’est tout.", @@ -455,6 +502,8 @@ export const fr: Catalog = { "This browser cannot write back to the file, so every save makes a new copy. Chrome and Edge on a computer can save in place.": "Ce navigateur ne peut pas réécrire le fichier : chaque enregistrement crée donc une nouvelle copie. Chrome et Edge sur ordinateur enregistrent sur place.", "This clip is {size} and travels inside the file, making it that much bigger for everyone you send it to. Embed it anyway?": "Ce clip fait {size} et voyage dans le fichier, ce qui l’alourdit d’autant pour toutes les personnes à qui vous l’envoyez. L’intégrer quand même ?", "This copy goes offline; the others carry on": "Cette copie passe hors ligne ; les autres continuent", + "This embed is inside itself — the loop stops here.": "Cette insertion se contient elle-même — la boucle s’arrête ici.", + "This embed points at a page that is not here.": "Cette insertion pointe vers une page qui n’est pas là.", "This file": "Ce fichier", "This file carries its own app — it works offline, forever, as is.": "Ce fichier embarque sa propre application — il fonctionne hors ligne, pour toujours, tel quel.", "This file could not be opened": "Ce fichier n’a pas pu être ouvert", @@ -466,10 +515,12 @@ export const fr: Catalog = { "This is a reading copy. It opens for reading; nothing you do here changes the file.": "Ceci est une copie de lecture. Elle s'ouvre en lecture ; rien de ce que vous faites ici ne modifie le fichier.", "This is a view-only copy — it follows the live session but can’t change this space.": "Ceci est une copie en lecture seule — elle suit la session live mais ne peut pas modifier cet espace.", "This is not a bento/spaces document — {detail}.": "Ce n’est pas un document bento/spaces — {detail}.", + "This list": "Cette liste", "This live session has run out of room. Your change is saved in your copy, but collaborators won’t see it.": "Cette session en direct n’a plus de place. Votre modification est enregistrée dans votre copie, mais les collaborateurs ne la verront pas.", "This page only": "Cette page seulement", "This space has no live session to follow": "Cet espace n’a pas de session live à suivre", "This space has no pages.": "Cet espace n’a aucune page.", + "This space is encrypted, so no versions are kept.": "Cet espace est chiffré : aucune version n’est conservée.", "This space is encrypted. Saves stay encrypted.": "Cet espace est chiffré. Les enregistrements restent chiffrés.", "This space is locked": "Cet espace est verrouillé", "This window is still running v{v} — reload to finish. A v{v} backup was downloaded.": "Cette fenêtre tourne encore en v{v} — rechargez pour terminer. Une sauvegarde v{v} a été téléchargée.", @@ -483,6 +534,7 @@ export const fr: Catalog = { "Today": "Aujourd’hui", "Today's journal": "Journal du jour", "Toggle": "Bloc dépliant", + "Toggle section": "Afficher ou masquer la section", "Tone": "Ton", "Too many changes at once — live sync is catching up.": "Trop de modifications à la fois — la synchronisation en direct rattrape son retard.", "Top level": "Premier niveau", @@ -491,6 +543,7 @@ export const fr: Catalog = { "Underline": "Souligné", "Underline — ⌘U": "Souligné — ⌘U", "Undo (⌘Z)": "Annuler (⌘Z)", + "Undo, redo": "Annuler, rétablir", "Unlock": "Déverrouiller", "Unlocked, but the document inside could not be read.": "Déverrouillé, mais le document à l’intérieur n’a pas pu être lu.", "Unsaved changes from a previous session were found.": "Des modifications non enregistrées d’une session précédente ont été trouvées.", @@ -508,6 +561,7 @@ export const fr: Catalog = { "Verifying…": "Vérification…", "Version {v} is available.": "La version {v} est disponible.", "Version, language, password, exports": "Version, langue, mot de passe, exports", + "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "Les versions sont conservées uniquement dans ce navigateur — jamais dans le fichier, jamais en ligne. La restauration est annulable.", "Video": "Vidéo", "Video from {host}": "Vidéo provenant de {host}", "Video or audio": "Vidéo ou audio", @@ -521,12 +575,14 @@ export const fr: Catalog = { "What is in the picture": "Ce que montre l’image", "What this is": "Ce que c’est", "What’s new →": "Nouveautés →", + "Which pages": "Quelles pages", "Wide": "Large", "Width": "Largeur", "Width %": "Largeur %", "Width in the text column": "Largeur dans la colonne de texte", "Windows on this computer still sync; turn offline mode off in About to work with someone elsewhere.": "Les fenêtres de cet ordinateur restent synchronisées ; désactivez le mode hors ligne dans « À propos » pour travailler avec quelqu’un ailleurs.", "Words": "Mots", + "Writing": "Écrire", "Wrong password — try again": "Mot de passe incorrect — réessayez", "Yellow": "Jaune", "You have the newest version ({v}).": "Vous avez la dernière version ({v}).", @@ -539,6 +595,7 @@ export const fr: Catalog = { "bento/spaces {version} · {pages} page(s), {blocks} block(s). The document, the editor and the search are all in this one file.": "bento/spaces {version} · {pages} page(s), {blocks} bloc(s). Le document, l’éditeur et la recherche tiennent tous dans ce seul fichier.", "format v{v}": "format v{v}", "just now": "à l'instant", + "most recent": "la plus récente", "none": "aucun", "you": "vous", "you: {name} ✎": "vous : {name} ✎", @@ -547,6 +604,8 @@ export const fr: Catalog = { "{name} joined": "{name} a rejoint", "{name} left": "{name} est parti", "{name} was removed": "{name} a été retiré", + "{n} block": "{n} bloc", + "{n} blocks": "{n} blocs", "{n} connected": "{n} connectés", "{n} duplicate or missing id(s) were repaired so links and pages resolve.": "{n} id(s) en double ou manquant(s) ont été réparés pour que les liens et les pages se résolvent.", "{n} embedded image(s) and clip(s) account for {size} of that — {pct}%. Everything else is text.": "{n} image(s) et clip(s) intégrés représentent {size}, soit {pct}%. Tout le reste est du texte.", @@ -558,9 +617,11 @@ export const fr: Catalog = { "{n} image(s) go with them; the rest stay here.": "{n} image(s) partent avec elles ; les autres restent ici.", "{n} image(s) point at the web. Nothing loads until a reader asks.": "{n} image(s) pointent vers le web. Rien ne se charge tant qu’un lecteur ne le demande pas.", "{n} image(s) were left as paths because embedding stopped there.": "{n} image(s) restent sous forme de chemin, l’intégration s’étant arrêtée là.", + "{n} link to this page": "{n} lien vers cette page", "{n} link(s) named pages that were not in that file, and are kept as text.": "{n} lien(s) nommaient des pages absentes de ce fichier et sont conservés en texte.", "{n} link(s) point outside them and are kept as text naming the page they meant.": "{n} lien(s) pointent en dehors et sont conservés en texte, avec le nom de la page visée.", "{n} link(s) to it will stop working.": "{n} lien(s) vers elle ne fonctionneront plus.", + "{n} links to this page": "{n} liens vers cette page", "{n} note name(s) appear more than once, so links naming them all went to the first.": "{n} nom(s) de note apparaissent plusieurs fois : les liens portant ce nom pointent tous vers le premier.", "{n} of {total} wikilink(s) resolved.": "{n} wikilien(s) sur {total} résolus.", "{n} page(s) had frontmatter, kept verbatim in a folded block.": "{n} page(s) avaient un frontmatter, conservé tel quel dans un bloc replié.", @@ -569,6 +630,8 @@ export const fr: Catalog = { "{n} table(s) imported, with their column alignment.": "{n} tableau(x) importé(s), avec l’alignement de leurs colonnes.", "{n} table(s) kept as text: there is no table block in this format yet.": "{n} tableau(x) conservés en texte : ce format n’a pas encore de bloc tableau.", "{n} unresolved comment(s)": "{n} commentaire(s) non résolu(s)", + "{n} word": "{n} mot", + "{n} words": "{n} mots", "{n}d ago": "il y a {n} j", "{n}h ago": "il y a {n} h", "{n}m ago": "il y a {n} min", @@ -577,56 +640,4 @@ export const fr: Catalog = { "{pages} page(s) and {blocks} block(s) will travel.": "{pages} page(s) et {blocks} bloc(s) feront le voyage.", "{pages} pages · {links} links": "{pages} pages · {links} liens", "⌘Z removes the imported pages again.": "⌘Z supprime à nouveau les pages importées.", - "History": "Historique", - "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "Les versions sont conservées uniquement dans ce navigateur — jamais dans le fichier, jamais en ligne. La restauration est annulable.", - "This space is encrypted, so no versions are kept.": "Cet espace est chiffré : aucune version n’est conservée.", - "No versions yet — they build up as you write and save.": "Pas encore de versions — elles s’accumulent à mesure que vous écrivez et enregistrez.", - "most recent": "la plus récente", - "That version could not be read": "Cette version n’a pas pu être lue", - "Restored the version from {when} — ⌘Z undoes it": "Version du {when} restaurée — ⌘Z annule.", - "Dismiss": "Ignorer", - "Toggle section": "Afficher ou masquer la section", - "{n} block": "{n} bloc", - "{n} blocks": "{n} blocs", - "{n} word": "{n} mot", - "{n} words": "{n} mots", - "{n} link to this page": "{n} lien vers cette page", - "{n} links to this page": "{n} liens vers cette page", - "A new block": "Un nouveau bloc", - "Find and replace": "Rechercher et remplacer", - "Formatting": "Mise en forme", - "Getting around": "Se déplacer", - "Indent, or move back out": "Indenter, ou revenir en arrière", - "Keyboard shortcuts": "Raccourcis clavier", - "Leave the reading view": "Quitter la vue lecture", - "Link the selected words": "Lier les mots sélectionnés", - "Link to another page": "Lier vers une autre page", - "Search all pages, with nothing selected": "Rechercher dans toutes les pages, sans sélection", - "Show or hide properties": "Afficher ou masquer les propriétés", - "Show or hide the page list": "Afficher ou masquer la liste des pages", - "The block menu, on an empty line": "Le menu de blocs, sur une ligne vide", - "The workspace": "L’espace de travail", - "This list": "Cette liste", - "Undo, redo": "Annuler, rétablir", - "Writing": "Écrire", - "Add": "Ajouter", - "Add a property": "Ajouter une propriété", - "Add property…": "Ajouter une propriété…", - "Added {name}": "{name} ajoutée", - "Name": "Nom", - "New property": "Nouvelle propriété", - "Choose which pages this view holds": "Choisir les pages que contient cette vue", - "Every page with a status": "Toutes les pages ayant un statut", - "Nested under": "Imbriquées sous", - "Pages nested under this one": "Pages imbriquées sous celle-ci", - "Pages that have this property": "Pages qui ont cette propriété", - "Which pages": "Quelles pages", - "Gallery": "Galerie", - "Show as a gallery": "Afficher en galerie", - "Show as a table": "Afficher en tableau", - "Select": "Sélection", - "Number": "Nombre", - "Date": "Date", - "Person": "Personne", - "Labels": "Étiquettes", } diff --git a/spaces/src/i18n/it.ts b/spaces/src/i18n/it.ts index a4f1c6af..41fa97ef 100644 --- a/spaces/src/i18n/it.ts +++ b/spaces/src/i18n/it.ts @@ -18,7 +18,9 @@ export const it: Catalog = { "A folder of notes, or another space": "Una cartella di note o un altro spazio", "A link with nothing in it yet": "Un collegamento ancora vuoto", "A list of every page, in order": "Un elenco di tutte le pagine, in ordine", + "A live view of another page": "Una vista dal vivo di un’altra pagina", "A live viewer: follows every edit as it happens but can never change this space — the relay enforces it.": "Un visualizzatore live: segue ogni modifica ma non può mai cambiare questo Space — imposto dal relay.", + "A new block": "Un nuovo blocco", "A note, tip or warning": "Una nota, un consiglio o un avviso", "A page cannot contain itself": "Una pagina non può contenere sé stessa", "A password encrypts the document inside the file. There is no recovery — lose it and the space is gone.": "Una password cifra il documento dentro il file. Non c’è modo di recuperarla — se la perdi, lo spazio è perso.", @@ -32,6 +34,7 @@ export const it: Catalog = { "About bento/spaces — version, updates, language, password": "Informazioni su bento/spaces — versione, aggiornamenti, lingua, password", "About this space": "Informazioni su questo spazio", "Access reset — only copies saved from now on can join": "Accesso reimpostato — solo le copie salvate d’ora in poi potranno unirsi", + "Add": "Aggiungi", "Add a block below": "Aggiungi un blocco sotto", "Add a card": "Aggiungi una scheda", "Add a card that opens a page": "Aggiungi una scheda che apre una pagina", @@ -39,9 +42,12 @@ export const it: Catalog = { "Add a link": "Aggiungi un collegamento", "Add a page": "Aggiungi una pagina", "Add a picture": "Aggiungi un'immagine", + "Add a property": "Aggiungi una proprietà", "Add a row below": "Aggiungi una riga sotto", "Add below": "Aggiungi sotto", "Add pages under": "Aggiungi le pagine sotto", + "Add property…": "Aggiungi proprietà…", + "Added {name}": "{name} aggiunta", "Address of a video or audio file": "Indirizzo di un file video o audio", "Adds status, priority, assignee, estimate": "Aggiunge stato, priorità, assegnatario e stima", "All text. Nothing is embedded, so this file is as small as a space gets.": "Solo testo. Non c’è nulla di incorporato, quindi questo file è il più piccolo possibile per uno spazio.", @@ -100,6 +106,7 @@ export const it: Catalog = { "Choose a space…": "Scegli uno spazio…", "Choose the field the columns come from": "Scegli il campo da cui nascono le colonne", "Choose the order": "Scegli l’ordine", + "Choose which pages this view holds": "Scegli quali pagine contiene questa vista", "Choose…": "Scegli…", "Clear filter": "Azzera il filtro", "Clear formatting": "Cancella formattazione", @@ -135,6 +142,7 @@ export const it: Catalog = { "Cover": "Copertina", "Create “{name}”": "Crea «{name}»", "Dark": "Scuro", + "Date": "Data", "Default": "Predefinito", "Delete": "Elimina", "Delete “{name}”?": "Eliminare «{name}»?", @@ -142,6 +150,7 @@ export const it: Catalog = { "Descending": "Decrescente", "Description": "Descrizione", "Discard": "Ignora", + "Dismiss": "Chiudi", "Divider": "Separatore", "Document": "Documento", "Document JSON copied": "JSON del documento copiato", @@ -166,10 +175,14 @@ export const it: Catalog = { "Editing": "In modifica", "Editor": "Editor", "Editor copy saved — recipients join live with edit access": "Copia editor salvata — chi la riceve entra live con accesso in modifica", + "Embed a page": "Incorpora una pagina", + "Embedded from {page}": "Incorporato da {page}", "Embedded in the file": "Incorporato nel file", + "Embeds are not followed deeper than this.": "Le incorporazioni non vengono seguite più in profondità.", "Encrypt the document inside this file": "Cifra il documento dentro questo file", "Enter the password to open it.": "Inserisci la password per aprirlo.", "Every page opens wide on this screen from now on": "Da ora tutte le pagine si aprono larghe su questo schermo", + "Every page with a status": "Ogni pagina con uno stato", "Every page, and what links to what": "Tutte le pagine, e cosa collega a cosa", "Every page, as one .md file": "Tutte le pagine, in un unico file .md", "Everything in this space is replaced by what you paste. ⌘Z undoes it, but only while this window stays open.": "Tutto ciò che si trova in questo spazio viene sostituito da quello che incolli. ⌘Z annulla l’operazione, ma solo finché questa finestra resta aperta.", @@ -185,11 +198,16 @@ export const it: Catalog = { "Filter": "Filtra", "Filter blocks…": "Filtra i blocchi…", "Find": "Trova", + "Find a page or a section…": "Cerca una pagina o una sezione…", + "Find and replace": "Trova e sostituisci", "Find in this space…": "Trova in questo spazio…", "Find or create a page…": "Trova o crea una pagina…", "Fit": "Adatta", "Following — view only": "In ascolto — sola lettura", + "Formatting": "Formattazione", "Full width": "Larghezza piena", + "Gallery": "Galleria", + "Getting around": "Spostarsi", "Graph": "Grafo", "Gray": "Grigio", "Green": "Verde", @@ -203,6 +221,7 @@ export const it: Catalog = { "Hide the page list ([)": "Nascondi l'elenco delle pagine ([)", "Highlight": "Evidenziazione", "Highlight — ⇧⌘H": "Evidenziazione — ⇧⌘H", + "History": "Cronologia", "Icon": "Icona", "Image": "Immagine", "Image added ({size})": "Immagine aggiunta ({size})", @@ -219,6 +238,7 @@ export const it: Catalog = { "Include archived pages": "Includi le pagine archiviate", "Include the image files and they are embedded too. An image this browser cannot open is kept as its path rather than as a broken picture.": "Includi anche i file immagine e vengono incorporati. Un’immagine che questo browser non riesce ad aprire resta come percorso, invece che come immagine rotta.", "Include the pages nested under it": "Includi le pagine annidate sotto di essa", + "Indent, or move back out": "Rientra, o torna indietro", "Inline code — ⌘E": "Codice in linea — ⌘E", "Insert": "Inserisci", "Insert a block — text, headings, lists, code, images": "Inserisci un blocco — testo, titoli, elenchi, codice, immagini", @@ -232,16 +252,21 @@ export const it: Catalog = { "Italic — ⌘I": "Corsivo — ⌘I", "Journal date": "Data del diario", "Key-verified identity": "Identità verificata da chiave", + "Keyboard shortcuts": "Scorciatoie da tastiera", + "Labels": "Etichette", "Language": "Lingua", "Language follows whoever opens the file. It is never written into the document.": "La lingua segue chi apre il file. Non viene mai scritta nel documento.", "Language — what this block is highlighted as": "Linguaggio — come viene evidenziato questo blocco", "Last saved": "Ultimo salvataggio", "Launch check couldn't reach the release server ({m}). Check manually below.": "La verifica all'avvio non ha raggiunto il server ({m}). Verifica manualmente qui sotto.", "Leave it empty to use the tone mark": "Lascia vuoto per usare il simbolo del tipo", + "Leave the reading view": "Esci dalla vista di lettura", "Light": "Chiaro", "Link": "Collegamento", "Link address": "Indirizzo del collegamento", "Link card": "Scheda di collegamento", + "Link the selected words": "Collega le parole selezionate", + "Link to another page": "Collega a un’altra pagina", "Link to page": "Collega a una pagina", "Link to the web": "Collegamento al web", "Link — ⌘K": "Collegamento — ⌘K", @@ -265,11 +290,14 @@ export const it: Catalog = { "Move down": "Sposta giù", "Move up": "Sposta su", "Muted": "Muto", + "Name": "Nome", "Name this canvas": "Dai un nome a questa tela", + "Nested under": "Annidate sotto", "New issue": "Nuova issue", "New page": "Nuova pagina", "New page (⌘⌥N)": "Nuova pagina (⌘⌥N)", "New page inside": "Nuova pagina interna", + "New property": "Nuova proprietà", "New to Bento? Find templates, the gallery and the AI editing guide at {home} — or ⭐ it on {gh}.": "Nuovo su Bento? Trovi modelli, la galleria e la guida all’editing con IA su {home} — o metti una ⭐ su {gh}.", "Next (⏎)": "Successivo (⏎)", "No Markdown files in that selection": "Nessun file Markdown in questa selezione", @@ -277,7 +305,10 @@ export const it: Catalog = { "No field here has options to group by": "Nessun campo qui ha opzioni per raggruppare", "No issues match this filter.": "Nessuna issue corrisponde a questo filtro.", "No issues yet. Add a status field to any page and it appears here.": "Ancora nessuna issue. Aggiungi un campo stato a una pagina e comparirà qui.", + "No page matches": "Nessuna pagina corrisponde", "No pages yet": "Ancora nessuna pagina", + "No section named {name} on this page.": "Nessuna sezione chiamata {name} in questa pagina.", + "No versions yet — they build up as you write and save.": "Ancora nessuna versione: si accumulano mentre scrivi e salvi.", "Nobody else is here yet": "Non c’è ancora nessun altro", "Not in this file: it needs the network, and the site is told when someone opens the page": "Non è dentro questo file: per riprodurla serve la rete, e quel sito saprà che qualcuno ha aperto questa pagina", "Not live — turns on when you share": "Non live — si attiva quando condividi", @@ -290,6 +321,7 @@ export const it: Catalog = { "Nothing on this canvas yet.": "Non c’è ancora nulla su questa tela.", "Nothing to draw yet — link two pages with [[ and they will appear here.": "Non c’è ancora nulla da disegnare — collega due pagine con [[ e appariranno qui.", "Now an issue": "Ora è una issue", + "Number": "Numero", "Numbered list": "Elenco numerato", "Off by default — they were archived for a reason": "Disattivo per impostazione predefinita — sono state archiviate per un motivo", "Offline mode is on — nothing leaves this computer.": "La modalità offline è attiva: nulla lascia questo computer.", @@ -310,7 +342,9 @@ export const it: Catalog = { "Page": "Pagina", "Page options": "Opzioni della pagina", "Pages": "Pagine", + "Pages nested under this one": "Pagine annidate sotto questa", "Pages open at their normal width again": "Le pagine tornano alla larghezza normale", + "Pages that have this property": "Pagine che hanno questa proprietà", "Pages — show or hide the page list": "Pagine — mostra o nascondi l’elenco delle pagine", "Password": "Password", "Password removed. Save to write the space unencrypted.": "Password rimossa. Salva per scrivere lo spazio non cifrato.", @@ -319,6 +353,7 @@ export const it: Catalog = { "Paste document JSON here…": "Incolla qui il JSON del documento…", "Paste or type a link": "Incolla o scrivi un collegamento", "People in this space": "Persone in questo spazio", + "Person": "Persona", "Picture": "Immagine", "Picture…": "Immagine…", "Pink": "Rosa", @@ -381,6 +416,7 @@ export const it: Catalog = { "Resolve": "Risolvi", "Restore": "Ripristina", "Restore to the page list": "Ripristina nell’elenco delle pagine", + "Restored the version from {when} — ⌘Z undoes it": "Ripristinata la versione del {when} — ⌘Z annulla.", "Room for a board or a table": "Spazio per una bacheca o una tabella", "Rows": "Righe", "Rows and columns": "Righe e colonne", @@ -395,12 +431,18 @@ export const it: Catalog = { "Saving…": "Salvataggio…", "Search": "Cerca", "Search all pages (⌘K)": "Cerca in tutte le pagine (⌘K)", + "Search all pages, with nothing selected": "Cerca in tutte le pagine, senza nulla selezionato", "Search all pages…": "Cerca in tutte le pagine…", "Search this space": "Cerca in questo spazio", + "Select": "Selezione", "Set a password…": "Imposta una password…", "Share this space": "Condividi questo spazio", "Show as a board": "Mostra come bacheca", + "Show as a gallery": "Mostra come galleria", "Show as a list": "Mostra come elenco", + "Show as a table": "Mostra come tabella", + "Show or hide properties": "Mostra o nascondi le proprietà", + "Show or hide the page list": "Mostra o nascondi l’elenco delle pagine", "Show playback controls to the reader": "Mostra i controlli di riproduzione a chi legge", "Show properties (])": "Mostra proprietà (])", "Show the page list ([)": "Mostra l'elenco delle pagine ([)", @@ -432,7 +474,10 @@ export const it: Catalog = { "That is not a bento/spaces document": "Questo non è un documento bento/spaces", "That is {n} files — importing them all may take a moment. Continue?": "Sono {n} file — importarli tutti può richiedere un momento. Continuare?", "That needs to be an http or https address": "Serve un indirizzo http o https", + "That section only": "Solo quella sezione", "That space is password-protected. Open it, then export the pages you want.": "Quello spazio è protetto da password. Aprilo e da lì esporta le pagine che vuoi.", + "That version could not be read": "Non è stato possibile leggere quella versione", + "The block menu, on an empty line": "Il menu dei blocchi, su una riga vuota", "The counterpart of Copy document JSON: edit a space in another tool, then bring it back.": "Il corrispettivo di Copia il JSON del documento: modifica uno spazio in un altro strumento e riportalo qui.", "The day after": "Il giorno dopo", "The day before": "Il giorno prima", @@ -446,8 +491,10 @@ export const it: Catalog = { "The pages without the editing tools": "Le pagine senza gli strumenti di modifica", "The rest name notes that were not in the selection, and are left as text.": "Gli altri nominano note che non erano nella selezione e restano come testo.", "The theme follows whoever opens the file. It is never written into the document.": "Il tema segue chi apre il file. Non viene mai scritto nel documento.", + "The whole page": "L’intera pagina", "The whole space": "L’intero spazio", "The whole space, or just this page": "L’intero spazio, o solo questa pagina", + "The workspace": "Lo spazio di lavoro", "Then send someone the file": "Poi manda il file a qualcuno", "These windows are on this computer. Start a session to work with someone elsewhere.": "Queste finestre sono su questo computer. Avvia una sessione per lavorare con qualcuno altrove.", "This block has no settings of its own — its content is all of it.": "Questo blocco non ha impostazioni proprie: il suo contenuto è tutto.", @@ -455,6 +502,8 @@ export const it: Catalog = { "This browser cannot write back to the file, so every save makes a new copy. Chrome and Edge on a computer can save in place.": "Questo browser non può riscrivere il file, quindi ogni salvataggio crea una nuova copia. Chrome ed Edge su computer possono salvare sul posto.", "This clip is {size} and travels inside the file, making it that much bigger for everyone you send it to. Embed it anyway?": "Questa clip è di {size} e viaggia dentro il file, rendendolo altrettanto più grande per chiunque la riceva. Incorporarla comunque?", "This copy goes offline; the others carry on": "Questa copia va offline; le altre continuano", + "This embed is inside itself — the loop stops here.": "Questa incorporazione contiene sé stessa — il ciclo si ferma qui.", + "This embed points at a page that is not here.": "Questa incorporazione punta a una pagina che non c’è.", "This file": "Questo file", "This file carries its own app — it works offline, forever, as is.": "Questo file porta con sé la propria app — funziona offline, per sempre, così com'è.", "This file could not be opened": "Impossibile aprire questo file", @@ -466,10 +515,12 @@ export const it: Catalog = { "This is a reading copy. It opens for reading; nothing you do here changes the file.": "Questa è una copia di lettura. Si apre in lettura; nulla di ciò che fai qui modifica il file.", "This is a view-only copy — it follows the live session but can’t change this space.": "Questa è una copia di sola lettura — segue la sessione live ma non può modificare questo Space.", "This is not a bento/spaces document — {detail}.": "Questo non è un documento bento/spaces — {detail}.", + "This list": "Questo elenco", "This live session has run out of room. Your change is saved in your copy, but collaborators won’t see it.": "Questa sessione dal vivo ha esaurito lo spazio. La tua modifica è salvata nella tua copia, ma i collaboratori non la vedranno.", "This page only": "Solo questa pagina", "This space has no live session to follow": "Questo Space non ha una sessione live da seguire", "This space has no pages.": "Questo spazio non ha pagine.", + "This space is encrypted, so no versions are kept.": "Questo spazio è cifrato, quindi non viene conservata alcuna versione.", "This space is encrypted. Saves stay encrypted.": "Questo spazio è cifrato. I salvataggi restano cifrati.", "This space is locked": "Questo spazio è bloccato", "This window is still running v{v} — reload to finish. A v{v} backup was downloaded.": "Questa finestra esegue ancora la v{v} — ricarica per completare. È stato scaricato un backup v{v}.", @@ -483,6 +534,7 @@ export const it: Catalog = { "Today": "Oggi", "Today's journal": "Diario di oggi", "Toggle": "Blocco richiudibile", + "Toggle section": "Mostra o nascondi la sezione", "Tone": "Tono", "Too many changes at once — live sync is catching up.": "Troppe modifiche insieme — la sincronizzazione dal vivo sta recuperando.", "Top level": "Primo livello", @@ -491,6 +543,7 @@ export const it: Catalog = { "Underline": "Sottolineato", "Underline — ⌘U": "Sottolineato — ⌘U", "Undo (⌘Z)": "Annulla (⌘Z)", + "Undo, redo": "Annulla, ripeti", "Unlock": "Sblocca", "Unlocked, but the document inside could not be read.": "Sbloccato, ma non è stato possibile leggere il documento all’interno.", "Unsaved changes from a previous session were found.": "Sono state trovate modifiche non salvate di una sessione precedente.", @@ -508,6 +561,7 @@ export const it: Catalog = { "Verifying…": "Verifica…", "Version {v} is available.": "È disponibile la versione {v}.", "Version, language, password, exports": "Versione, lingua, password, esportazioni", + "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "Le versioni restano solo in questo browser: mai nel file, mai online. Il ripristino si può annullare.", "Video": "Video", "Video from {host}": "Video da {host}", "Video or audio": "Video o audio", @@ -521,12 +575,14 @@ export const it: Catalog = { "What is in the picture": "Che cosa mostra l’immagine", "What this is": "Di che cosa si tratta", "What’s new →": "Novità →", + "Which pages": "Quali pagine", "Wide": "Largo", "Width": "Larghezza", "Width %": "Larghezza %", "Width in the text column": "Larghezza nella colonna di testo", "Windows on this computer still sync; turn offline mode off in About to work with someone elsewhere.": "Le finestre su questo computer restano sincronizzate; disattiva la modalità offline in «Informazioni» per lavorare con qualcuno altrove.", "Words": "Parole", + "Writing": "Scrivere", "Wrong password — try again": "Password errata — riprova", "Yellow": "Giallo", "You have the newest version ({v}).": "Hai la versione più recente ({v}).", @@ -539,6 +595,7 @@ export const it: Catalog = { "bento/spaces {version} · {pages} page(s), {blocks} block(s). The document, the editor and the search are all in this one file.": "bento/spaces {version} · pagine: {pages}, blocchi: {blocks}. Il documento, l’editor e la ricerca sono tutti in questo unico file.", "format v{v}": "formato v{v}", "just now": "proprio ora", + "most recent": "più recente", "none": "nessuno", "you": "tu", "you: {name} ✎": "tu: {name} ✎", @@ -547,6 +604,8 @@ export const it: Catalog = { "{name} joined": "{name} si è unito", "{name} left": "{name} se n'è andato", "{name} was removed": "{name} è stato rimosso", + "{n} block": "{n} blocco", + "{n} blocks": "{n} blocchi", "{n} connected": "{n} connessi", "{n} duplicate or missing id(s) were repaired so links and pages resolve.": "Riparati {n} id duplicati o mancanti, così link e pagine funzionano.", "{n} embedded image(s) and clip(s) account for {size} of that — {pct}%. Everything else is text.": "{n} immagine/i e clip incorporate rappresentano {size} di quel totale — il {pct}%. Tutto il resto è testo.", @@ -558,9 +617,11 @@ export const it: Catalog = { "{n} image(s) go with them; the rest stay here.": "{n} immagine/i vanno con esse; il resto resta qui.", "{n} image(s) point at the web. Nothing loads until a reader asks.": "{n} immagini puntano al web. Non si carica nulla finché un lettore non lo chiede.", "{n} image(s) were left as paths because embedding stopped there.": "{n} immagini restano come percorsi perché l’incorporamento si è fermato lì.", + "{n} link to this page": "{n} link a questa pagina", "{n} link(s) named pages that were not in that file, and are kept as text.": "{n} collegamento/i nominavano pagine non presenti in quel file e restano come testo.", "{n} link(s) point outside them and are kept as text naming the page they meant.": "{n} collegamento/i puntano fuori e restano come testo che nomina la pagina a cui puntavano.", "{n} link(s) to it will stop working.": "{n} collegamenti a questa pagina smetteranno di funzionare.", + "{n} links to this page": "{n} link a questa pagina", "{n} note name(s) appear more than once, so links naming them all went to the first.": "{n} nome/i di nota compaiono più volte, quindi i link con quel nome puntano tutti al primo.", "{n} of {total} wikilink(s) resolved.": "{n} wikilink su {total} risolti.", "{n} page(s) had frontmatter, kept verbatim in a folded block.": "{n} pagine avevano un frontmatter, conservato alla lettera in un blocco richiuso.", @@ -569,6 +630,8 @@ export const it: Catalog = { "{n} table(s) imported, with their column alignment.": "{n} tabella/e importata/e, con l’allineamento delle colonne.", "{n} table(s) kept as text: there is no table block in this format yet.": "{n} tabelle conservate come testo: questo formato non ha ancora un blocco tabella.", "{n} unresolved comment(s)": "{n} commento/i non risolto/i", + "{n} word": "{n} parola", + "{n} words": "{n} parole", "{n}d ago": "{n} g fa", "{n}h ago": "{n} h fa", "{n}m ago": "{n} min fa", @@ -577,56 +640,4 @@ export const it: Catalog = { "{pages} page(s) and {blocks} block(s) will travel.": "Viaggeranno {pages} pagina/e e {blocks} blocco/blocchi.", "{pages} pages · {links} links": "{pages} pagine · {links} collegamenti", "⌘Z removes the imported pages again.": "⌘Z rimuove di nuovo le pagine importate.", - "History": "Cronologia", - "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "Le versioni restano solo in questo browser: mai nel file, mai online. Il ripristino si può annullare.", - "This space is encrypted, so no versions are kept.": "Questo spazio è cifrato, quindi non viene conservata alcuna versione.", - "No versions yet — they build up as you write and save.": "Ancora nessuna versione: si accumulano mentre scrivi e salvi.", - "most recent": "più recente", - "That version could not be read": "Non è stato possibile leggere quella versione", - "Restored the version from {when} — ⌘Z undoes it": "Ripristinata la versione del {when} — ⌘Z annulla.", - "Dismiss": "Chiudi", - "Toggle section": "Mostra o nascondi la sezione", - "{n} block": "{n} blocco", - "{n} blocks": "{n} blocchi", - "{n} word": "{n} parola", - "{n} words": "{n} parole", - "{n} link to this page": "{n} link a questa pagina", - "{n} links to this page": "{n} link a questa pagina", - "A new block": "Un nuovo blocco", - "Find and replace": "Trova e sostituisci", - "Formatting": "Formattazione", - "Getting around": "Spostarsi", - "Indent, or move back out": "Rientra, o torna indietro", - "Keyboard shortcuts": "Scorciatoie da tastiera", - "Leave the reading view": "Esci dalla vista di lettura", - "Link the selected words": "Collega le parole selezionate", - "Link to another page": "Collega a un’altra pagina", - "Search all pages, with nothing selected": "Cerca in tutte le pagine, senza nulla selezionato", - "Show or hide properties": "Mostra o nascondi le proprietà", - "Show or hide the page list": "Mostra o nascondi l’elenco delle pagine", - "The block menu, on an empty line": "Il menu dei blocchi, su una riga vuota", - "The workspace": "Lo spazio di lavoro", - "This list": "Questo elenco", - "Undo, redo": "Annulla, ripeti", - "Writing": "Scrivere", - "Add": "Aggiungi", - "Add a property": "Aggiungi una proprietà", - "Add property…": "Aggiungi proprietà…", - "Added {name}": "{name} aggiunta", - "Name": "Nome", - "New property": "Nuova proprietà", - "Choose which pages this view holds": "Scegli quali pagine contiene questa vista", - "Every page with a status": "Ogni pagina con uno stato", - "Nested under": "Annidate sotto", - "Pages nested under this one": "Pagine annidate sotto questa", - "Pages that have this property": "Pagine che hanno questa proprietà", - "Which pages": "Quali pagine", - "Gallery": "Galleria", - "Show as a gallery": "Mostra come galleria", - "Show as a table": "Mostra come tabella", - "Select": "Selezione", - "Number": "Numero", - "Date": "Data", - "Person": "Persona", - "Labels": "Etichette", } diff --git a/spaces/src/i18n/ja.ts b/spaces/src/i18n/ja.ts index 89841877..cc66f90e 100644 --- a/spaces/src/i18n/ja.ts +++ b/spaces/src/i18n/ja.ts @@ -18,7 +18,9 @@ export const ja: Catalog = { "A folder of notes, or another space": "ノートのフォルダー、または別のスペース", "A link with nothing in it yet": "まだ何も入っていないリンク", "A list of every page, in order": "すべてのページを順番に並べた一覧", + "A live view of another page": "別のページのライブ表示", "A live viewer: follows every edit as it happens but can never change this space — the relay enforces it.": "ライブビューア:編集をリアルタイムで追いますが、この Space を変更することはできません — リレーが強制します。", + "A new block": "新しいブロック", "A note, tip or warning": "メモ・ヒント・注意", "A page cannot contain itself": "ページを自分自身に入れることはできません", "A password encrypts the document inside the file. There is no recovery — lose it and the space is gone.": "パスワードはファイル内のドキュメントを暗号化します。復元手段はありません — 忘れたらこのスペースは失われます。", @@ -32,6 +34,7 @@ export const ja: Catalog = { "About bento/spaces — version, updates, language, password": "bento/spaces について — バージョン・更新・言語・パスワード", "About this space": "このスペースについて", "Access reset — only copies saved from now on can join": "アクセスをリセットしました — 今後保存したコピーだけが参加できます", + "Add": "追加", "Add a block below": "下にブロックを追加", "Add a card": "カードを追加", "Add a card that opens a page": "ページを開くカードを追加", @@ -39,9 +42,12 @@ export const ja: Catalog = { "Add a link": "リンクを追加", "Add a page": "ページを追加", "Add a picture": "画像を追加", + "Add a property": "プロパティを追加", "Add a row below": "下に行を追加", "Add below": "下に追加", "Add pages under": "ページを追加する場所", + "Add property…": "プロパティを追加…", + "Added {name}": "{name} を追加しました", "Address of a video or audio file": "動画または音声ファイルのアドレス", "Adds status, priority, assignee, estimate": "ステータス・優先度・担当者・見積もりを追加します", "All text. Nothing is embedded, so this file is as small as a space gets.": "すべてテキストです。埋め込みはないので、このファイルはスペースとして最小の大きさです。", @@ -100,6 +106,7 @@ export const ja: Catalog = { "Choose a space…": "スペースを選ぶ…", "Choose the field the columns come from": "列のもとになるフィールドを選ぶ", "Choose the order": "並び順を選ぶ", + "Choose which pages this view holds": "このビューに入れるページを選ぶ", "Choose…": "選択…", "Clear filter": "フィルターを解除", "Clear formatting": "書式をクリア", @@ -135,6 +142,7 @@ export const ja: Catalog = { "Cover": "カバー", "Create “{name}”": "「{name}」を作成", "Dark": "ダーク", + "Date": "日付", "Default": "デフォルト", "Delete": "削除", "Delete “{name}”?": "「{name}」を削除しますか?", @@ -142,6 +150,7 @@ export const ja: Catalog = { "Descending": "降順", "Description": "説明", "Discard": "破棄", + "Dismiss": "閉じる", "Divider": "区切り線", "Document": "文書", "Document JSON copied": "ドキュメント JSON をコピーしました", @@ -166,10 +175,14 @@ export const ja: Catalog = { "Editing": "編集中", "Editor": "編集者", "Editor copy saved — recipients join live with edit access": "編集者コピーを保存しました — 受け取った人は編集権限つきでライブに参加します", + "Embed a page": "ページを埋め込む", + "Embedded from {page}": "「{page}」から埋め込み", "Embedded in the file": "ファイルに埋め込み済み", + "Embeds are not followed deeper than this.": "埋め込みはこれ以上たどりません。", "Encrypt the document inside this file": "このファイル内のドキュメントを暗号化", "Enter the password to open it.": "開くにはパスワードを入力してください。", "Every page opens wide on this screen from now on": "この画面では今後すべてのページを広く表示します", + "Every page with a status": "ステータスを持つすべてのページ", "Every page, and what links to what": "すべてのページと、そのつながり", "Every page, as one .md file": "すべてのページを 1 つの .md ファイルに", "Everything in this space is replaced by what you paste. ⌘Z undoes it, but only while this window stays open.": "このスペースの内容はすべて貼り付けた内容に置き換わります。⌘Z で元に戻せますが、それはこのウィンドウを開いている間だけです。", @@ -185,11 +198,16 @@ export const ja: Catalog = { "Filter": "フィルター", "Filter blocks…": "ブロックを絞り込む…", "Find": "検索", + "Find a page or a section…": "ページまたはセクションを検索…", + "Find and replace": "検索と置換", "Find in this space…": "このスペース内を検索…", "Find or create a page…": "ページを検索または作成…", "Fit": "全体表示", "Following — view only": "追従中 — 閲覧のみ", + "Formatting": "書式", "Full width": "全幅", + "Gallery": "ギャラリー", + "Getting around": "移動", "Graph": "グラフ", "Gray": "グレー", "Green": "緑", @@ -203,6 +221,7 @@ export const ja: Catalog = { "Hide the page list ([)": "ページ一覧を隠す([)", "Highlight": "ハイライト", "Highlight — ⇧⌘H": "ハイライト — ⇧⌘H", + "History": "履歴", "Icon": "アイコン", "Image": "画像", "Image added ({size})": "画像を追加しました({size})", @@ -219,6 +238,7 @@ export const ja: Catalog = { "Include archived pages": "アーカイブしたページを含める", "Include the image files and they are embedded too. An image this browser cannot open is kept as its path rather than as a broken picture.": "画像ファイルも一緒に選べば、そのまま埋め込まれます。このブラウザーが開けない画像は、壊れた画像ではなくパスのまま残ります。", "Include the pages nested under it": "その下にあるページも含める", + "Indent, or move back out": "字下げ、または戻す", "Inline code — ⌘E": "インラインコード — ⌘E", "Insert": "挿入", "Insert a block — text, headings, lists, code, images": "ブロックを挿入 — テキスト、見出し、リスト、コード、画像", @@ -232,16 +252,21 @@ export const ja: Catalog = { "Italic — ⌘I": "イタリック — ⌘I", "Journal date": "ジャーナルの日付", "Key-verified identity": "鍵で検証済みのアイデンティティ", + "Keyboard shortcuts": "キーボードショートカット", + "Labels": "ラベル", "Language": "言語", "Language follows whoever opens the file. It is never written into the document.": "言語はファイルを開いた人に従います。ドキュメントには書き込まれません。", "Language — what this block is highlighted as": "言語 — このブロックを何として色分けするか", "Last saved": "最終保存", "Launch check couldn't reach the release server ({m}). Check manually below.": "起動時の確認でリリースサーバーに接続できませんでした ({m})。下から手動で確認してください。", "Leave it empty to use the tone mark": "空のままにすると種類の記号を使います", + "Leave the reading view": "読書ビューを終了", "Light": "ライト", "Link": "リンク", "Link address": "リンク先の URL", "Link card": "リンクカード", + "Link the selected words": "選択した語句にリンクを付ける", + "Link to another page": "別のページへリンク", "Link to page": "ページにリンク", "Link to the web": "ウェブへのリンク", "Link — ⌘K": "リンク — ⌘K", @@ -265,11 +290,14 @@ export const ja: Catalog = { "Move down": "下へ移動", "Move up": "上へ移動", "Muted": "ミュート", + "Name": "名前", "Name this canvas": "このキャンバスに名前を付ける", + "Nested under": "この下のページ", "New issue": "新しいイシュー", "New page": "新規ページ", "New page (⌘⌥N)": "新規ページ (⌘⌥N)", "New page inside": "この中に新規ページ", + "New property": "新しいプロパティ", "New to Bento? Find templates, the gallery and the AI editing guide at {home} — or ⭐ it on {gh}.": "Bento は初めてですか?テンプレート、ギャラリー、AI編集ガイドは {home} でどうぞ — {gh} で ⭐ もぜひ。", "Next (⏎)": "次へ (⏎)", "No Markdown files in that selection": "選択に Markdown ファイルがありません", @@ -277,7 +305,10 @@ export const ja: Catalog = { "No field here has options to group by": "グループ化に使える選択肢を持つフィールドがありません", "No issues match this filter.": "この条件に一致するイシューはありません。", "No issues yet. Add a status field to any page and it appears here.": "まだイシューはありません。どれかのページにステータス項目を追加すると、ここに表示されます。", + "No page matches": "一致するページがありません", "No pages yet": "ページがまだありません", + "No section named {name} on this page.": "このページに「{name}」という見出しはありません。", + "No versions yet — they build up as you write and save.": "まだバージョンはありません。書いて保存するたびに増えていきます。", "Nobody else is here yet": "まだほかに誰もいません", "Not in this file: it needs the network, and the site is told when someone opens the page": "このファイルの中にはありません。再生にはネットワークが必要で、誰かがこのページを開いたことがそのサイトに伝わります", "Not live — turns on when you share": "未接続 — 共有すると自動的にライブになります", @@ -290,6 +321,7 @@ export const ja: Catalog = { "Nothing on this canvas yet.": "このキャンバスにはまだ何もありません。", "Nothing to draw yet — link two pages with [[ and they will appear here.": "まだ描くものがありません — [[ で 2 つのページをつなぐと、ここに現れます。", "Now an issue": "イシューになりました", + "Number": "数値", "Numbered list": "番号付きリスト", "Off by default — they were archived for a reason": "既定ではオフ — アーカイブしたのには理由があります", "Offline mode is on — nothing leaves this computer.": "オフラインモードがオンです — このコンピュータから何も送信されません。", @@ -310,7 +342,9 @@ export const ja: Catalog = { "Page": "ページ", "Page options": "ページのオプション", "Pages": "ページ", + "Pages nested under this one": "このページの下にあるページ", "Pages open at their normal width again": "ページは通常の幅に戻りました", + "Pages that have this property": "このプロパティを持つページ", "Pages — show or hide the page list": "ページ — ページ一覧の表示/非表示", "Password": "パスワード", "Password removed. Save to write the space unencrypted.": "パスワードを削除しました。保存すると、このスペースは暗号化なしで書き込まれます。", @@ -319,6 +353,7 @@ export const ja: Catalog = { "Paste document JSON here…": "ここにドキュメント JSON を貼り付け…", "Paste or type a link": "リンクを貼り付けるか入力してください", "People in this space": "このスペースにいる人", + "Person": "担当者", "Picture": "画像", "Picture…": "画像…", "Pink": "ピンク", @@ -381,6 +416,7 @@ export const ja: Catalog = { "Resolve": "解決", "Restore": "復元", "Restore to the page list": "ページ一覧に戻す", + "Restored the version from {when} — ⌘Z undoes it": "{when} のバージョンを復元しました。⌘Z で取り消せます。", "Room for a board or a table": "ボードや表にちょうどよい幅", "Rows": "行", "Rows and columns": "行と列", @@ -395,12 +431,18 @@ export const ja: Catalog = { "Saving…": "保存中…", "Search": "検索", "Search all pages (⌘K)": "全ページを検索 (⌘K)", + "Search all pages, with nothing selected": "何も選択せずに全ページを検索", "Search all pages…": "全ページを検索…", "Search this space": "このスペースを検索", + "Select": "選択", "Set a password…": "パスワードを設定…", "Share this space": "このスペースを共有", "Show as a board": "ボードで表示", + "Show as a gallery": "ギャラリーで表示", "Show as a list": "リストで表示", + "Show as a table": "テーブルで表示", + "Show or hide properties": "プロパティの表示・非表示", + "Show or hide the page list": "ページ一覧の表示・非表示", "Show playback controls to the reader": "読み手に再生コントロールを表示する", "Show properties (])": "プロパティを表示(])", "Show the page list ([)": "ページ一覧を表示([)", @@ -432,7 +474,10 @@ export const ja: Catalog = { "That is not a bento/spaces document": "これは bento/spaces の文書ではありません", "That is {n} files — importing them all may take a moment. Continue?": "{n} 個のファイルがあります。すべて読み込むには少し時間がかかります。続けますか?", "That needs to be an http or https address": "http または https のアドレスを入力してください", + "That section only": "そのセクションのみ", "That space is password-protected. Open it, then export the pages you want.": "そのスペースはパスワードで保護されています。開いてから、必要なページを書き出してください。", + "That version could not be read": "そのバージョンを読み込めませんでした", + "The block menu, on an empty line": "空行でブロックメニュー", "The counterpart of Copy document JSON: edit a space in another tool, then bring it back.": "「文書 JSON をコピー」の対になる機能です。別のツールでスペースを編集して、ここへ戻せます。", "The day after": "翌日", "The day before": "前日", @@ -446,8 +491,10 @@ export const ja: Catalog = { "The pages without the editing tools": "編集ツールのないページ表示", "The rest name notes that were not in the selection, and are left as text.": "残りは選択に含まれていないノートを指しており、テキストのまま残ります。", "The theme follows whoever opens the file. It is never written into the document.": "テーマはファイルを開いた人に従います。ドキュメントには書き込まれません。", + "The whole page": "ページ全体", "The whole space": "スペース全体", "The whole space, or just this page": "スペース全体、またはこのページだけ", + "The workspace": "ワークスペース", "Then send someone the file": "そのあとファイルを相手に送ります", "These windows are on this computer. Start a session to work with someone elsewhere.": "これらのウィンドウはこのパソコン内のものです。別の場所にいる人と作業するにはセッションを開始してください。", "This block has no settings of its own — its content is all of it.": "このブロックに固有の設定はありません — 中身がすべてです。", @@ -455,6 +502,8 @@ export const ja: Catalog = { "This browser cannot write back to the file, so every save makes a new copy. Chrome and Edge on a computer can save in place.": "このブラウザはファイルに書き戻せないため、保存のたびに新しいコピーができます。パソコンの Chrome と Edge なら直接上書き保存できます。", "This clip is {size} and travels inside the file, making it that much bigger for everyone you send it to. Embed it anyway?": "このクリップは {size} あり、ファイルの中に一緒に入ります。その分、送る相手全員にとってファイルが大きくなります。それでも埋め込みますか?", "This copy goes offline; the others carry on": "このコピーはオフラインになります。ほかの人はそのまま続けられます", + "This embed is inside itself — the loop stops here.": "この埋め込みは自分自身を含んでいます。ここでループを止めました。", + "This embed points at a page that is not here.": "この埋め込みの参照先ページが見つかりません。", "This file": "このファイル", "This file carries its own app — it works offline, forever, as is.": "このファイルはアプリを内蔵しています — オフラインでも、ずっと、このまま動きます。", "This file could not be opened": "このファイルを開けませんでした", @@ -466,10 +515,12 @@ export const ja: Catalog = { "This is a reading copy. It opens for reading; nothing you do here changes the file.": "これは閲覧用のコピーです。読むために開きます。ここでの操作はファイルを変更しません。", "This is a view-only copy — it follows the live session but can’t change this space.": "これは閲覧専用コピーです — ライブセッションに追従しますが、この Space は変更できません。", "This is not a bento/spaces document — {detail}.": "これは bento/spaces のドキュメントではありません — {detail}。", + "This list": "この一覧", "This live session has run out of room. Your change is saved in your copy, but collaborators won’t see it.": "このライブセッションの容量がいっぱいです。変更は自分のコピーに保存されますが、共同編集者には表示されません。", "This page only": "このページだけ", "This space has no live session to follow": "この Space には追従できるライブセッションがありません", "This space has no pages.": "このスペースにはページがありません。", + "This space is encrypted, so no versions are kept.": "このスペースは暗号化されているため、バージョンは保存されません。", "This space is encrypted. Saves stay encrypted.": "このスペースは暗号化されています。保存しても暗号化されたままです。", "This space is locked": "このスペースはロックされています", "This window is still running v{v} — reload to finish. A v{v} backup was downloaded.": "このウィンドウはまだ v{v} で動作中 — 再読込で完了します。v{v} のバックアップをダウンロード済み。", @@ -483,6 +534,7 @@ export const ja: Catalog = { "Today": "今日", "Today's journal": "今日のジャーナル", "Toggle": "トグル", + "Toggle section": "セクションの開閉", "Tone": "トーン", "Too many changes at once — live sync is catching up.": "変更が多すぎます — ライブ同期が追いついています。", "Top level": "最上位", @@ -491,6 +543,7 @@ export const ja: Catalog = { "Underline": "下線", "Underline — ⌘U": "下線 — ⌘U", "Undo (⌘Z)": "元に戻す (⌘Z)", + "Undo, redo": "取り消し・やり直し", "Unlock": "ロック解除", "Unlocked, but the document inside could not be read.": "ロックは解除しましたが、中のドキュメントを読み取れませんでした。", "Unsaved changes from a previous session were found.": "前回のセッションの未保存の変更が見つかりました。", @@ -508,6 +561,7 @@ export const ja: Catalog = { "Verifying…": "検証中…", "Version {v} is available.": "バージョン {v} が利用可能です。", "Version, language, password, exports": "バージョン・言語・パスワード・書き出し", + "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "バージョンはこのブラウザーにのみ保存されます。ファイルにもオンラインにも残りません。復元は取り消せます。", "Video": "動画", "Video from {host}": "{host} の動画", "Video or audio": "動画または音声", @@ -521,12 +575,14 @@ export const ja: Catalog = { "What is in the picture": "画像に写っているもの", "What this is": "これが何か", "What’s new →": "新着情報 →", + "Which pages": "どのページ", "Wide": "広め", "Width": "幅", "Width %": "幅 %", "Width in the text column": "本文カラム内の幅", "Windows on this computer still sync; turn offline mode off in About to work with someone elsewhere.": "このコンピュータ上のウィンドウ同士は引き続き同期します。ほかの場所にいる人と作業するには「About」でオフラインモードを解除してください。", "Words": "語数", + "Writing": "書く", "Wrong password — try again": "パスワードが違います — もう一度お試しください", "Yellow": "黄", "You have the newest version ({v}).": "最新バージョンです ({v})。", @@ -539,6 +595,7 @@ export const ja: Catalog = { "bento/spaces {version} · {pages} page(s), {blocks} block(s). The document, the editor and the search are all in this one file.": "bento/spaces {version} · {pages} ページ、{blocks} ブロック。ドキュメントもエディターも検索も、すべてこの 1 つのファイルの中にあります。", "format v{v}": "フォーマット v{v}", "just now": "たった今", + "most recent": "最新", "none": "なし", "you": "あなた", "you: {name} ✎": "あなた: {name} ✎", @@ -547,6 +604,8 @@ export const ja: Catalog = { "{name} joined": "{name} が参加しました", "{name} left": "{name} が退出しました", "{name} was removed": "{name} を削除しました", + "{n} block": "{n} ブロック", + "{n} blocks": "{n} ブロック", "{n} connected": "{n} 人が接続中", "{n} duplicate or missing id(s) were repaired so links and pages resolve.": "重複または欠落した id を {n} 件修復し、リンクとページが解決するようにしました。", "{n} embedded image(s) and clip(s) account for {size} of that — {pct}%. Everything else is text.": "埋め込まれた画像とクリップ {n} 件で {size}、全体の {pct}% を占めます。残りはすべてテキストです。", @@ -558,9 +617,11 @@ export const ja: Catalog = { "{n} image(s) go with them; the rest stay here.": "{n} 件の画像が一緒に移動し、残りはここに残ります。", "{n} image(s) point at the web. Nothing loads until a reader asks.": "{n} 個の画像はウェブを指しています。読み手が求めるまで何も読み込みません。", "{n} image(s) were left as paths because embedding stopped there.": "{n} 個の画像は、そこで埋め込みを止めたためパスのまま残りました。", + "{n} link to this page": "このページへのリンク {n} 件", "{n} link(s) named pages that were not in that file, and are kept as text.": "{n} 件のリンクはそのファイルに無いページを指していたため、テキストとして残しました。", "{n} link(s) point outside them and are kept as text naming the page they meant.": "{n} 件のリンクは外を指しているため、指していたページ名を残したテキストになります。", "{n} link(s) to it will stop working.": "このページへのリンク {n} 件が機能しなくなります。", + "{n} links to this page": "このページへのリンク {n} 件", "{n} note name(s) appear more than once, so links naming them all went to the first.": "{n} 件のノート名が重複しているため、その名前のリンクはすべて最初のノートに向いています。", "{n} of {total} wikilink(s) resolved.": "{total} 個のウィキリンクのうち {n} 個を解決しました。", "{n} page(s) had frontmatter, kept verbatim in a folded block.": "{n} ページにフロントマターがありました。折りたたみブロックにそのまま保存しています。", @@ -569,6 +630,8 @@ export const ja: Catalog = { "{n} table(s) imported, with their column alignment.": "{n} 個の表を取り込みました(列の配置も含みます)。", "{n} table(s) kept as text: there is no table block in this format yet.": "{n} 個の表はテキストとして保存しました。この形式にはまだ表ブロックがありません。", "{n} unresolved comment(s)": "未解決のコメント {n} 件", + "{n} word": "{n} 語", + "{n} words": "{n} 語", "{n}d ago": "{n}日前", "{n}h ago": "{n}時間前", "{n}m ago": "{n}分前", @@ -577,56 +640,4 @@ export const ja: Catalog = { "{pages} page(s) and {blocks} block(s) will travel.": "{pages} ページ、{blocks} ブロックが移動します。", "{pages} pages · {links} links": "{pages} ページ · {links} リンク", "⌘Z removes the imported pages again.": "⌘Z で、読み込んだページを取り消せます。", - "History": "履歴", - "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "バージョンはこのブラウザーにのみ保存されます。ファイルにもオンラインにも残りません。復元は取り消せます。", - "This space is encrypted, so no versions are kept.": "このスペースは暗号化されているため、バージョンは保存されません。", - "No versions yet — they build up as you write and save.": "まだバージョンはありません。書いて保存するたびに増えていきます。", - "most recent": "最新", - "That version could not be read": "そのバージョンを読み込めませんでした", - "Restored the version from {when} — ⌘Z undoes it": "{when} のバージョンを復元しました。⌘Z で取り消せます。", - "Dismiss": "閉じる", - "Toggle section": "セクションの開閉", - "{n} block": "{n} ブロック", - "{n} blocks": "{n} ブロック", - "{n} word": "{n} 語", - "{n} words": "{n} 語", - "{n} link to this page": "このページへのリンク {n} 件", - "{n} links to this page": "このページへのリンク {n} 件", - "A new block": "新しいブロック", - "Find and replace": "検索と置換", - "Formatting": "書式", - "Getting around": "移動", - "Indent, or move back out": "字下げ、または戻す", - "Keyboard shortcuts": "キーボードショートカット", - "Leave the reading view": "読書ビューを終了", - "Link the selected words": "選択した語句にリンクを付ける", - "Link to another page": "別のページへリンク", - "Search all pages, with nothing selected": "何も選択せずに全ページを検索", - "Show or hide properties": "プロパティの表示・非表示", - "Show or hide the page list": "ページ一覧の表示・非表示", - "The block menu, on an empty line": "空行でブロックメニュー", - "The workspace": "ワークスペース", - "This list": "この一覧", - "Undo, redo": "取り消し・やり直し", - "Writing": "書く", - "Add": "追加", - "Add a property": "プロパティを追加", - "Add property…": "プロパティを追加…", - "Added {name}": "{name} を追加しました", - "Name": "名前", - "New property": "新しいプロパティ", - "Choose which pages this view holds": "このビューに入れるページを選ぶ", - "Every page with a status": "ステータスを持つすべてのページ", - "Nested under": "この下のページ", - "Pages nested under this one": "このページの下にあるページ", - "Pages that have this property": "このプロパティを持つページ", - "Which pages": "どのページ", - "Gallery": "ギャラリー", - "Show as a gallery": "ギャラリーで表示", - "Show as a table": "テーブルで表示", - "Select": "選択", - "Number": "数値", - "Date": "日付", - "Person": "担当者", - "Labels": "ラベル", } diff --git a/spaces/src/i18n/packed.ts b/spaces/src/i18n/packed.ts index 2ed45fd6..835dc691 100644 --- a/spaces/src/i18n/packed.ts +++ b/spaces/src/i18n/packed.ts @@ -19,6 +19,7 @@ export const PACKED: Record> = { "A folder of notes, or another space": ["ノートのフォルダー、または別のスペース","一个笔记文件夹,或另一个空间","一個筆記資料夾,或另一個空間","Una carpeta de notas u otro espacio","Un dossier de notes, ou un autre espace","Ein Ordner mit Notizen oder ein anderer Space","Una cartella di note o un altro spazio","Uma pasta de notas, ou outro espaço"], "A link with nothing in it yet": ["まだ何も入っていないリンク","还没有内容的链接","還沒有內容的連結","Un enlace todavía sin nada","Un lien encore vide","Ein Link, in dem noch nichts steht","Un collegamento ancora vuoto","Um link ainda sem nada"], "A list of every page, in order": ["すべてのページを順番に並べた一覧","按顺序列出的所有页面","依順序列出每一個頁面","Una lista de todas las páginas, en orden","La liste de toutes les pages, dans l’ordre","Eine Liste aller Seiten, der Reihe nach","Un elenco di tutte le pagine, in ordine","Uma lista de todas as páginas, por ordem"], + "A live view of another page": ["別のページのライブ表示","另一个页面的实时视图","另一個頁面的即時檢視","Una vista en vivo de otra página","Une vue en direct d’une autre page","Eine Live-Ansicht einer anderen Seite","Una vista dal vivo di un’altra pagina","Uma vista ao vivo de outra página"], "A live viewer: follows every edit as it happens but can never change this space — the relay enforces it.": ["ライブビューア:編集をリアルタイムで追いますが、この Space を変更することはできません — リレーが強制します。","实时查看副本:实时跟随每次编辑,但永远无法修改此 Space — 由中继强制执行。","即時檢視副本:即時跟隨每次編輯,但永遠無法修改此 Space — 由中繼強制執行。","Un visor en vivo: sigue cada edición al instante pero nunca puede modificar este Space — el relé lo garantiza.","Un visualiseur en direct : suit chaque modification mais ne peut jamais changer cet espace — le relais l’applique.","Ein Live-Viewer: folgt jeder Änderung, kann diesen Space aber nie verändern — vom Relay erzwungen.","Un visualizzatore live: segue ogni modifica ma non può mai cambiare questo Space — imposto dal relay.","Um visualizador ao vivo: acompanha cada edição conforme acontece, mas nunca pode alterar este Space — o retransmissor garante isso."], "A new block": ["新しいブロック","新建一个块","新增一個區塊","Un bloque nuevo","Un nouveau bloc","Ein neuer Block","Un nuovo blocco","Um bloco novo"], "A note, tip or warning": ["メモ・ヒント・注意","注释、提示或警告","註記、提示或警告","Una nota, un consejo o una advertencia","Une note, un conseil ou un avertissement","Eine Notiz, ein Tipp oder eine Warnung","Una nota, un consiglio o un avviso","Uma nota, dica ou aviso"], @@ -170,7 +171,10 @@ export const PACKED: Record> = { "Editing": ["編集中","编辑中","編輯中","Edición","Édition","Bearbeiten","In modifica","Edição"], "Editor": ["編集者","编辑者","編輯者","Editor","Éditeur","Bearbeiter","Editor","Editor"], "Editor copy saved — recipients join live with edit access": ["編集者コピーを保存しました — 受け取った人は編集権限つきでライブに参加します","编辑者副本已保存 — 收到的人可带编辑权限加入实时会话","編輯者副本已儲存 — 收到的人可帶編輯權限加入即時會話","Copia de editor guardada — quien la reciba se une en vivo con acceso de edición","Copie éditeur enregistrée — les destinataires rejoignent la session en direct avec accès en écriture","Bearbeiter-Kopie gespeichert — Empfänger treten live mit Schreibzugriff bei","Copia editor salvata — chi la riceve entra live con accesso in modifica","Cópia de editor salva — quem receber entra ao vivo com acesso de edição"], + "Embed a page": ["ページを埋め込む","嵌入页面","嵌入頁面","Insertar una página","Insérer une page","Seite einbetten","Incorpora una pagina","Incorporar uma página"], + "Embedded from {page}": ["「{page}」から埋め込み","嵌入自“{page}”","嵌入自「{page}」","Insertado desde {page}","Inséré depuis {page}","Eingebettet aus {page}","Incorporato da {page}","Incorporado de {page}"], "Embedded in the file": ["ファイルに埋め込み済み","已嵌入文件","已嵌入檔案","Incrustado en el archivo","Intégré au fichier","In der Datei eingebettet","Incorporato nel file","Embutido no arquivo"], + "Embeds are not followed deeper than this.": ["埋め込みはこれ以上たどりません。","嵌入不会再向下展开。","嵌入不會再向下展開。","Las inserciones no se siguen más allá de aquí.","Les insertions ne sont pas suivies plus loin.","Einbettungen werden nicht tiefer verfolgt.","Le incorporazioni non vengono seguite più in profondità.","As incorporações não são seguidas para além deste ponto."], "Enter the password to open it.": ["開くにはパスワードを入力してください。","输入密码以打开。","請輸入密碼以開啟。","Introduce la contraseña para abrirlo.","Saisissez le mot de passe pour l’ouvrir.","Gib das Passwort ein, um ihn zu öffnen.","Inserisci la password per aprirlo.","Introduza a palavra-passe para o abrir."], "Every page opens wide on this screen from now on": ["この画面では今後すべてのページを広く表示します","在此屏幕上,之后所有页面都会以加宽方式打开","在此螢幕上,之後所有頁面都會以加寬方式開啟","A partir de ahora todas las páginas se abren anchas en esta pantalla","Désormais toutes les pages s’ouvrent en large sur cet écran","Auf diesem Bildschirm öffnen ab jetzt alle Seiten breit","Da ora tutte le pagine si aprono larghe su questo schermo","A partir de agora todas as páginas abrem largas neste ecrã"], "Every page with a status": ["ステータスを持つすべてのページ","所有带状态的页面","所有帶狀態的頁面","Todas las páginas con estado","Toutes les pages ayant un statut","Jede Seite mit einem Status","Ogni pagina con uno stato","Todas as páginas com estado"], @@ -189,6 +193,7 @@ export const PACKED: Record> = { "Filter": ["フィルター","筛选","篩選","Filtrar","Filtrer","Filtern","Filtra","Filtrar"], "Filter blocks…": ["ブロックを絞り込む…","筛选块…","篩選區塊…","Filtrar bloques…","Filtrer les blocs…","Blöcke filtern…","Filtra i blocchi…","Filtrar blocos…"], "Find": ["検索","查找","尋找","Buscar","Rechercher","Suchen","Trova","Localizar"], + "Find a page or a section…": ["ページまたはセクションを検索…","查找页面或章节…","尋找頁面或章節…","Buscar una página o una sección…","Rechercher une page ou une section…","Seite oder Abschnitt suchen…","Cerca una pagina o una sezione…","Procurar uma página ou uma secção…"], "Find and replace": ["検索と置換","查找和替换","尋找與取代","Buscar y reemplazar","Rechercher et remplacer","Suchen und ersetzen","Trova e sostituisci","Localizar e substituir"], "Find in this space…": ["このスペース内を検索…","在此空间中查找…","在此空間尋找…","Buscar en este espacio…","Rechercher dans cet espace…","In diesem Space suchen…","Trova in questo spazio…","Localizar neste espaço…"], "Find or create a page…": ["ページを検索または作成…","查找或新建页面…","尋找或建立頁面…","Buscar o crear una página…","Trouver ou créer une page…","Seite finden oder erstellen…","Trova o crea una pagina…","Encontrar ou criar uma página…"], @@ -293,7 +298,9 @@ export const PACKED: Record> = { "No field here has options to group by": ["グループ化に使える選択肢を持つフィールドがありません","这里没有可用于分组的选项字段","這裡沒有可用於分組的選項欄位","Ningún campo de aquí tiene opciones para agrupar","Aucun champ ici n’a d’options pour grouper","Kein Feld hier hat Optionen zum Gruppieren","Nessun campo qui ha opzioni per raggruppare","Nenhum campo aqui tem opções para agrupar"], "No issues match this filter.": ["この条件に一致するイシューはありません。","没有符合此筛选条件的事项。","沒有符合此篩選條件的項目。","Ninguna incidencia coincide con este filtro.","Aucun ticket ne correspond à ce filtre.","Keine Issues entsprechen diesem Filter.","Nessuna issue corrisponde a questo filtro.","Nenhuma tarefa corresponde a este filtro."], "No issues yet. Add a status field to any page and it appears here.": ["まだイシューはありません。どれかのページにステータス項目を追加すると、ここに表示されます。","还没有事项。给任意页面添加状态字段,它就会出现在这里。","還沒有項目。為任一頁面新增狀態欄位,它就會出現在這裡。","Aún no hay incidencias. Añade un campo de estado a cualquier página y aparecerá aquí.","Aucun ticket pour l’instant. Ajoutez un champ statut à une page et elle apparaîtra ici.","Noch keine Issues. Gib einer Seite ein Status-Feld, dann erscheint sie hier.","Ancora nessuna issue. Aggiungi un campo stato a una pagina e comparirà qui.","Ainda não há tarefas. Adiciona um campo de estado a qualquer página e ela aparece aqui."], + "No page matches": ["一致するページがありません","没有匹配的页面","沒有相符的頁面","Ninguna página coincide","Aucune page ne correspond","Keine Seite passt","Nessuna pagina corrisponde","Nenhuma página corresponde"], "No pages yet": ["ページがまだありません","还没有页面","尚無頁面","Aún no hay páginas","Aucune page pour l’instant","Noch keine Seiten","Ancora nessuna pagina","Ainda sem páginas"], + "No section named {name} on this page.": ["このページに「{name}」という見出しはありません。","本页没有名为“{name}”的标题。","本頁沒有名為「{name}」的標題。","No hay ninguna sección llamada {name} en esta página.","Aucune section nommée {name} sur cette page.","Kein Abschnitt namens {name} auf dieser Seite.","Nessuna sezione chiamata {name} in questa pagina.","Não há nenhuma secção chamada {name} nesta página."], "No versions yet — they build up as you write and save.": ["まだバージョンはありません。書いて保存するたびに増えていきます。","暂无版本。随着你的书写和保存,版本会逐渐累积。","尚無版本。隨著你的書寫與儲存,版本會逐漸累積。","Aún no hay versiones: se acumulan a medida que escribes y guardas.","Pas encore de versions — elles s’accumulent à mesure que vous écrivez et enregistrez.","Noch keine Versionen — sie sammeln sich beim Schreiben und Speichern an.","Ancora nessuna versione: si accumulano mentre scrivi e salvi.","Ainda sem versões — vão-se acumulando à medida que escreves e guardas."], "Not in this file: it needs the network, and the site is told when someone opens the page": ["このファイルの中にはありません。再生にはネットワークが必要で、誰かがこのページを開いたことがそのサイトに伝わります","不在此文件内:播放需要联网,而且该网站会知道有人打开了这个页面","不在這個檔案裡:播放需要網路,而且該網站會知道有人開啟了這個頁面","No está en este archivo: necesita la red, y ese sitio sabrá que alguien ha abierto esta página","N’est pas dans ce fichier : la lecture nécessite le réseau, et ce site saura que quelqu’un a ouvert cette page","Nicht in dieser Datei: das Abspielen braucht das Netz, und die Website erfährt, dass jemand diese Seite geöffnet hat","Non è dentro questo file: per riprodurla serve la rete, e quel sito saprà che qualcuno ha aperto questa pagina","Não está neste ficheiro: precisa da rede, e esse site fica a saber que alguém abriu esta página"], "Not live — turns on when you share": ["未接続 — 共有すると自動的にライブになります","未上线 — 分享时自动开启","未上線 — 分享時自動開啟","Sin conexión — se activa al compartir","Hors ligne — s’active quand vous partagez","Nicht live — startet beim Teilen","Non live — si attiva quando condividi","Fora do ar — ativa quando você compartilha"], @@ -452,6 +459,7 @@ export const PACKED: Record> = { "That is not a bento/spaces document": ["これは bento/spaces の文書ではありません","这不是 bento/spaces 文档","這不是 bento/spaces 文件","Eso no es un documento de bento/spaces","Ce n’est pas un document bento/spaces","Das ist kein bento/spaces-Dokument","Questo non è un documento bento/spaces","Isto não é um documento bento/spaces"], "That is {n} files — importing them all may take a moment. Continue?": ["{n} 個のファイルがあります。すべて読み込むには少し時間がかかります。続けますか?","一共 {n} 个文件,全部导入需要一点时间。继续吗?","共 {n} 個檔案,全部匯入需要一點時間。要繼續嗎?","Son {n} archivos: importarlos todos puede tardar un momento. ¿Continuar?","Cela fait {n} fichiers — tout importer peut prendre un moment. Continuer ?","Das sind {n} Dateien — der Import kann einen Moment dauern. Fortfahren?","Sono {n} file — importarli tutti può richiedere un momento. Continuare?","São {n} ficheiros — importar todos pode demorar um momento. Continuar?"], "That needs to be an http or https address": ["http または https のアドレスを入力してください","这里需要填写 http 或 https 地址","這裡需要填 http 或 https 位址","Tiene que ser una dirección http o https","Il faut une adresse http ou https","Das muss eine http- oder https-Adresse sein","Serve un indirizzo http o https","Tem de ser um endereço http ou https"], + "That section only": ["そのセクションのみ","仅该章节","僅該章節","Solo esa sección","Cette section uniquement","Nur dieser Abschnitt","Solo quella sezione","Apenas essa secção"], "That space is password-protected. Open it, then export the pages you want.": ["そのスペースはパスワードで保護されています。開いてから、必要なページを書き出してください。","该空间受密码保护。请先打开它,再导出你需要的页面。","該空間受密碼保護。請先開啟它,再匯出你需要的頁面。","Ese espacio está protegido con contraseña. Ábrelo y exporta desde allí las páginas que quieras.","Cet espace est protégé par un mot de passe. Ouvrez-le, puis exportez-en les pages voulues.","Dieser Space ist passwortgeschützt. Öffnen Sie ihn und exportieren Sie dort die gewünschten Seiten.","Quello spazio è protetto da password. Aprilo e da lì esporta le pagine che vuoi.","Esse espaço está protegido por palavra-passe. Abra-o e exporte de lá as páginas que quiser."], "That version could not be read": ["そのバージョンを読み込めませんでした","无法读取该版本","無法讀取該版本","No se pudo leer esa versión","Cette version n’a pas pu être lue","Diese Version konnte nicht gelesen werden","Non è stato possibile leggere quella versione","Não foi possível ler essa versão"], "The block menu, on an empty line": ["空行でブロックメニュー","在空行上打开块菜单","在空行上開啟區塊選單","El menú de bloques, en una línea vacía","Le menu de blocs, sur une ligne vide","Das Blockmenü, in einer leeren Zeile","Il menu dei blocchi, su una riga vuota","O menu de blocos, numa linha vazia"], @@ -468,6 +476,7 @@ export const PACKED: Record> = { "The pages without the editing tools": ["編集ツールのないページ表示","不带编辑工具的页面","不含編輯工具的頁面","Las páginas sin las herramientas de edición","Les pages sans les outils d'édition","Die Seiten ohne die Bearbeitungswerkzeuge","Le pagine senza gli strumenti di modifica","As páginas sem as ferramentas de edição"], "The rest name notes that were not in the selection, and are left as text.": ["残りは選択に含まれていないノートを指しており、テキストのまま残ります。","其余指向不在所选范围内的笔记,保留为文字。","其餘指向不在所選範圍內的筆記,保留為文字。","El resto nombra notas que no estaban en la selección y se dejan como texto.","Les autres désignent des notes absentes de la sélection et restent en texte.","Der Rest nennt Notizen, die nicht in der Auswahl waren, und bleibt als Text stehen.","Gli altri nominano note che non erano nella selezione e restano come testo.","Os restantes nomeiam notas que não estavam na seleção e ficam como texto."], "The theme follows whoever opens the file. It is never written into the document.": ["テーマはファイルを開いた人に従います。ドキュメントには書き込まれません。","主题跟随打开文件的人,绝不会写入文档。","主題取決於開啟檔案的人,永遠不會寫入文件中。","El tema sigue a quien abre el archivo. Nunca se escribe en el documento.","Le thème suit la personne qui ouvre le fichier. Il n’est jamais écrit dans le document.","Das Design richtet sich danach, wer die Datei öffnet. Es wird nie ins Dokument geschrieben.","Il tema segue chi apre il file. Non viene mai scritto nel documento.","O tema segue quem abre o ficheiro. Nunca é escrito no documento."], + "The whole page": ["ページ全体","整个页面","整個頁面","La página entera","La page entière","Die ganze Seite","L’intera pagina","A página inteira"], "The whole space": ["スペース全体","整个空间","整個空間","Todo el espacio","Tout l’espace","Der ganze Space","L’intero spazio","Todo o espaço"], "The workspace": ["ワークスペース","工作区","工作區","El espacio de trabajo","L’espace de travail","Der Arbeitsbereich","Lo spazio di lavoro","A área de trabalho"], "These windows are on this computer. Start a session to work with someone elsewhere.": ["これらのウィンドウはこのパソコン内のものです。別の場所にいる人と作業するにはセッションを開始してください。","这些窗口都在本机。若要与其他地方的人协作,请开始一个会话。","這些視窗都在本機。若要與其他地方的人協作,請開始一個工作階段。","Estas ventanas están en este ordenador. Inicia una sesión para trabajar con alguien en otro sitio.","Ces fenêtres sont sur cet ordinateur. Démarrez une session pour travailler avec quelqu’un ailleurs.","Diese Fenster sind auf diesem Computer. Starte eine Sitzung, um mit jemand anderem zu arbeiten.","Queste finestre sono su questo computer. Avvia una sessione per lavorare con qualcuno altrove.","Estas janelas estão neste computador. Inicia uma sessão para trabalhar com alguém noutro lugar."], @@ -476,6 +485,8 @@ export const PACKED: Record> = { "This browser cannot write back to the file, so every save makes a new copy. Chrome and Edge on a computer can save in place.": ["このブラウザはファイルに書き戻せないため、保存のたびに新しいコピーができます。パソコンの Chrome と Edge なら直接上書き保存できます。","此浏览器无法写回原文件,因此每次保存都会生成新副本。电脑上的 Chrome 和 Edge 可以就地保存。","此瀏覽器無法寫回檔案,因此每次儲存都會產生新的副本。電腦版的 Chrome 和 Edge 可以就地儲存。","Este navegador no puede reescribir el archivo, así que cada guardado crea una copia nueva. Chrome y Edge en un ordenador sí pueden guardar en el sitio.","Ce navigateur ne peut pas réécrire le fichier : chaque enregistrement crée donc une nouvelle copie. Chrome et Edge sur ordinateur enregistrent sur place.","Dieser Browser kann nicht in die Datei zurückschreiben, deshalb entsteht bei jedem Speichern eine neue Kopie. Chrome und Edge auf dem Computer können direkt speichern.","Questo browser non può riscrivere il file, quindi ogni salvataggio crea una nuova copia. Chrome ed Edge su computer possono salvare sul posto.","Este navegador não consegue escrever de volta no ficheiro, por isso cada vez que guarda cria uma nova cópia. O Chrome e o Edge num computador conseguem guardar no local."], "This clip is {size} and travels inside the file, making it that much bigger for everyone you send it to. Embed it anyway?": ["このクリップは {size} あり、ファイルの中に一緒に入ります。その分、送る相手全員にとってファイルが大きくなります。それでも埋め込みますか?","这个片段有 {size},会嵌在文件内部,你发给谁,文件对谁就大出这么多。仍要嵌入吗?","這個片段有 {size},而且會跟著檔案一起走,你傳給的每個人拿到的檔案都會大這麼多。仍要嵌入嗎?","Este clip pesa {size} y viaja dentro del archivo, que crecerá otro tanto para todos a quienes se lo envíes. ¿Incrustarlo de todos modos?","Ce clip fait {size} et voyage dans le fichier, ce qui l’alourdit d’autant pour toutes les personnes à qui vous l’envoyez. L’intégrer quand même ?","Dieser Clip ist {size} groß und reist in der Datei mit — die Datei wird für jeden, dem du sie schickst, entsprechend größer. Trotzdem einbetten?","Questa clip è di {size} e viaggia dentro il file, rendendolo altrettanto più grande per chiunque la riceva. Incorporarla comunque?","Este clipe tem {size} e viaja dentro do ficheiro, tornando-o maior para todas as pessoas a quem o enviar. Incorporar mesmo assim?"], "This copy goes offline; the others carry on": ["このコピーはオフラインになります。ほかの人はそのまま続けられます","此副本将离线;其他人继续协作","此副本將離線;其他人繼續協作","Esta copia se desconecta; las demás siguen","Cette copie passe hors ligne ; les autres continuent","Diese Kopie geht offline; die anderen machen weiter","Questa copia va offline; le altre continuano","Esta cópia fica offline; as outras continuam"], + "This embed is inside itself — the loop stops here.": ["この埋め込みは自分自身を含んでいます。ここでループを止めました。","该嵌入包含了自身——循环在此停止。","這個嵌入包含了自身——迴圈在此停止。","Esta inserción se contiene a sí misma: el bucle se detiene aquí.","Cette insertion se contient elle-même — la boucle s’arrête ici.","Diese Einbettung enthält sich selbst – die Schleife endet hier.","Questa incorporazione contiene sé stessa — il ciclo si ferma qui.","Esta incorporação contém-se a si própria — o ciclo para aqui."], + "This embed points at a page that is not here.": ["この埋め込みの参照先ページが見つかりません。","该嵌入指向的页面不存在。","這個嵌入指向的頁面不存在。","Esta inserción apunta a una página que no está aquí.","Cette insertion pointe vers une page qui n’est pas là.","Diese Einbettung zeigt auf eine Seite, die nicht hier ist.","Questa incorporazione punta a una pagina che non c’è.","Esta incorporação aponta para uma página que não está aqui."], "This file": ["このファイル","此文件","此檔案","Este archivo","Ce fichier","Diese Datei","Questo file","Este ficheiro"], "This file carries its own app — it works offline, forever, as is.": ["このファイルはアプリを内蔵しています — オフラインでも、ずっと、このまま動きます。","此文件自带应用 — 离线可用,永久如此。","此檔案自帶應用程式 — 離線可用,永遠如此。","Este archivo lleva su propia aplicación — funciona sin conexión, para siempre, tal cual.","Ce fichier embarque sa propre application — il fonctionne hors ligne, pour toujours, tel quel.","Diese Datei trägt ihre eigene App — sie funktioniert offline, für immer, so wie sie ist.","Questo file porta con sé la propria app — funziona offline, per sempre, così com'è.","Este arquivo carrega o próprio aplicativo — funciona offline, para sempre, do jeito que está."], "This file could not be opened": ["このファイルを開けませんでした","无法打开此文件","無法開啟此檔案","No se pudo abrir este archivo","Ce fichier n’a pas pu être ouvert","Diese Datei konnte nicht geöffnet werden","Impossibile aprire questo file","Não foi possível abrir este ficheiro"], diff --git a/spaces/src/i18n/pt.ts b/spaces/src/i18n/pt.ts index 0e26b5a6..b76c803a 100644 --- a/spaces/src/i18n/pt.ts +++ b/spaces/src/i18n/pt.ts @@ -18,7 +18,9 @@ export const pt: Catalog = { "A folder of notes, or another space": "Uma pasta de notas, ou outro espaço", "A link with nothing in it yet": "Um link ainda sem nada", "A list of every page, in order": "Uma lista de todas as páginas, por ordem", + "A live view of another page": "Uma vista ao vivo de outra página", "A live viewer: follows every edit as it happens but can never change this space — the relay enforces it.": "Um visualizador ao vivo: acompanha cada edição conforme acontece, mas nunca pode alterar este Space — o retransmissor garante isso.", + "A new block": "Um bloco novo", "A note, tip or warning": "Uma nota, dica ou aviso", "A page cannot contain itself": "Uma página não pode conter-se a si própria", "A password encrypts the document inside the file. There is no recovery — lose it and the space is gone.": "Uma palavra-passe encripta o documento dentro do ficheiro. Não há recuperação — se a perder, o espaço desaparece.", @@ -32,6 +34,7 @@ export const pt: Catalog = { "About bento/spaces — version, updates, language, password": "Acerca do bento/spaces — versão, atualizações, idioma, palavra-passe", "About this space": "Sobre este espaço", "Access reset — only copies saved from now on can join": "Acesso redefinido — apenas cópias salvas de agora em diante podem entrar", + "Add": "Adicionar", "Add a block below": "Adicionar um bloco abaixo", "Add a card": "Adicionar um cartão", "Add a card that opens a page": "Adicionar um cartão que abre uma página", @@ -39,9 +42,12 @@ export const pt: Catalog = { "Add a link": "Adicionar um link", "Add a page": "Adicionar uma página", "Add a picture": "Adicionar uma imagem", + "Add a property": "Adicionar uma propriedade", "Add a row below": "Adicionar uma linha abaixo", "Add below": "Adicionar abaixo", "Add pages under": "Adicionar as páginas por baixo de", + "Add property…": "Adicionar propriedade…", + "Added {name}": "{name} adicionada", "Address of a video or audio file": "Endereço de um ficheiro de vídeo ou áudio", "Adds status, priority, assignee, estimate": "Adiciona estado, prioridade, responsável e estimativa", "All text. Nothing is embedded, so this file is as small as a space gets.": "Só texto. Nada está incorporado, por isso este ficheiro é o mais pequeno que um espaço pode ser.", @@ -100,6 +106,7 @@ export const pt: Catalog = { "Choose a space…": "Escolher um espaço…", "Choose the field the columns come from": "Escolha o campo de onde vêm as colunas", "Choose the order": "Escolha a ordem", + "Choose which pages this view holds": "Escolhe que páginas esta vista contém", "Choose…": "Escolher…", "Clear filter": "Limpar o filtro", "Clear formatting": "Limpar formatação", @@ -135,6 +142,7 @@ export const pt: Catalog = { "Cover": "Capa", "Create “{name}”": "Criar “{name}”", "Dark": "Escuro", + "Date": "Data", "Default": "Padrão", "Delete": "Eliminar", "Delete “{name}”?": "Eliminar “{name}”?", @@ -142,6 +150,7 @@ export const pt: Catalog = { "Descending": "Decrescente", "Description": "Descrição", "Discard": "Descartar", + "Dismiss": "Dispensar", "Divider": "Separador", "Document": "Documento", "Document JSON copied": "JSON do documento copiado", @@ -166,10 +175,14 @@ export const pt: Catalog = { "Editing": "Edição", "Editor": "Editor", "Editor copy saved — recipients join live with edit access": "Cópia de editor salva — quem receber entra ao vivo com acesso de edição", + "Embed a page": "Incorporar uma página", + "Embedded from {page}": "Incorporado de {page}", "Embedded in the file": "Embutido no arquivo", + "Embeds are not followed deeper than this.": "As incorporações não são seguidas para além deste ponto.", "Encrypt the document inside this file": "Encriptar o documento dentro deste ficheiro", "Enter the password to open it.": "Introduza a palavra-passe para o abrir.", "Every page opens wide on this screen from now on": "A partir de agora todas as páginas abrem largas neste ecrã", + "Every page with a status": "Todas as páginas com estado", "Every page, and what links to what": "Todas as páginas, e o que liga ao quê", "Every page, as one .md file": "Todas as páginas num único ficheiro .md", "Everything in this space is replaced by what you paste. ⌘Z undoes it, but only while this window stays open.": "Tudo o que está neste espaço é substituído pelo que colar. ⌘Z desfaz, mas só enquanto esta janela continuar aberta.", @@ -185,11 +198,16 @@ export const pt: Catalog = { "Filter": "Filtrar", "Filter blocks…": "Filtrar blocos…", "Find": "Localizar", + "Find a page or a section…": "Procurar uma página ou uma secção…", + "Find and replace": "Localizar e substituir", "Find in this space…": "Localizar neste espaço…", "Find or create a page…": "Encontrar ou criar uma página…", "Fit": "Ajustar", "Following — view only": "Acompanhando — somente visualização", + "Formatting": "Formatação", "Full width": "Largura total", + "Gallery": "Galeria", + "Getting around": "Navegar", "Graph": "Grafo", "Gray": "Cinza", "Green": "Verde", @@ -203,6 +221,7 @@ export const pt: Catalog = { "Hide the page list ([)": "Ocultar a lista de páginas ([)", "Highlight": "Destaque", "Highlight — ⇧⌘H": "Destaque — ⇧⌘H", + "History": "Histórico", "Icon": "Ícone", "Image": "Imagem", "Image added ({size})": "Imagem adicionada ({size})", @@ -219,6 +238,7 @@ export const pt: Catalog = { "Include archived pages": "Incluir páginas arquivadas", "Include the image files and they are embedded too. An image this browser cannot open is kept as its path rather than as a broken picture.": "Inclua os ficheiros de imagem e também são incorporados. Uma imagem que este navegador não consegue abrir fica como o seu caminho, em vez de uma imagem partida.", "Include the pages nested under it": "Incluir as páginas aninhadas por baixo", + "Indent, or move back out": "Avançar, ou recuar de novo", "Inline code — ⌘E": "Código em linha — ⌘E", "Insert": "Inserir", "Insert a block — text, headings, lists, code, images": "Inserir um bloco — texto, títulos, listas, código, imagens", @@ -232,16 +252,21 @@ export const pt: Catalog = { "Italic — ⌘I": "Itálico — ⌘I", "Journal date": "Data do diário", "Key-verified identity": "Identidade verificada por chave", + "Keyboard shortcuts": "Atalhos de teclado", + "Labels": "Etiquetas", "Language": "Idioma", "Language follows whoever opens the file. It is never written into the document.": "O idioma segue quem abre o ficheiro. Nunca é escrito no documento.", "Language — what this block is highlighted as": "Linguagem — como este bloco é realçado", "Last saved": "Última gravação", "Launch check couldn't reach the release server ({m}). Check manually below.": "A verificação ao abrir não alcançou o servidor de versões ({m}). Verifique manualmente abaixo.", "Leave it empty to use the tone mark": "Deixe vazio para usar o símbolo do tipo", + "Leave the reading view": "Sair da vista de leitura", "Light": "Claro", "Link": "Link", "Link address": "Endereço do link", "Link card": "Cartão de link", + "Link the selected words": "Ligar as palavras selecionadas", + "Link to another page": "Ligar a outra página", "Link to page": "Ligar a uma página", "Link to the web": "Ligação para a web", "Link — ⌘K": "Link — ⌘K", @@ -265,11 +290,14 @@ export const pt: Catalog = { "Move down": "Mover para baixo", "Move up": "Mover para cima", "Muted": "Sem som", + "Name": "Nome", "Name this canvas": "Dê um nome a esta tela", + "Nested under": "Aninhadas em", "New issue": "Nova tarefa", "New page": "Nova página", "New page (⌘⌥N)": "Nova página (⌘⌥N)", "New page inside": "Nova página dentro desta", + "New property": "Nova propriedade", "New to Bento? Find templates, the gallery and the AI editing guide at {home} — or ⭐ it on {gh}.": "Novo no Bento? Encontre modelos, a galeria e o guia de edição com IA em {home} — ou dê uma ⭐ no {gh}.", "Next (⏎)": "Seguinte (⏎)", "No Markdown files in that selection": "Não há ficheiros Markdown nessa seleção", @@ -277,7 +305,10 @@ export const pt: Catalog = { "No field here has options to group by": "Nenhum campo aqui tem opções para agrupar", "No issues match this filter.": "Nenhuma tarefa corresponde a este filtro.", "No issues yet. Add a status field to any page and it appears here.": "Ainda não há tarefas. Adiciona um campo de estado a qualquer página e ela aparece aqui.", + "No page matches": "Nenhuma página corresponde", "No pages yet": "Ainda sem páginas", + "No section named {name} on this page.": "Não há nenhuma secção chamada {name} nesta página.", + "No versions yet — they build up as you write and save.": "Ainda sem versões — vão-se acumulando à medida que escreves e guardas.", "Nobody else is here yet": "Ainda não há mais ninguém", "Not in this file: it needs the network, and the site is told when someone opens the page": "Não está neste ficheiro: precisa da rede, e esse site fica a saber que alguém abriu esta página", "Not live — turns on when you share": "Fora do ar — ativa quando você compartilha", @@ -290,6 +321,7 @@ export const pt: Catalog = { "Nothing on this canvas yet.": "Ainda não há nada nesta tela.", "Nothing to draw yet — link two pages with [[ and they will appear here.": "Ainda não há nada para desenhar — liga duas páginas com [[ e aparecerão aqui.", "Now an issue": "Agora é uma tarefa", + "Number": "Número", "Numbered list": "Lista numerada", "Off by default — they were archived for a reason": "Desativado por predefinição — foram arquivadas por alguma razão", "Offline mode is on — nothing leaves this computer.": "O modo offline está ativado — nada sai deste computador.", @@ -310,7 +342,9 @@ export const pt: Catalog = { "Page": "Página", "Page options": "Opções da página", "Pages": "Páginas", + "Pages nested under this one": "Páginas aninhadas sob esta", "Pages open at their normal width again": "As páginas voltam à largura normal", + "Pages that have this property": "Páginas que têm esta propriedade", "Pages — show or hide the page list": "Páginas — mostrar ou ocultar a lista de páginas", "Password": "Palavra-passe", "Password removed. Save to write the space unencrypted.": "Palavra-passe removida. Guarde para escrever o espaço sem encriptação.", @@ -319,6 +353,7 @@ export const pt: Catalog = { "Paste document JSON here…": "Cole aqui o JSON do documento…", "Paste or type a link": "Cole ou digite um link", "People in this space": "Pessoas neste espaço", + "Person": "Pessoa", "Picture": "Imagem", "Picture…": "Imagem…", "Pink": "Rosa", @@ -381,6 +416,7 @@ export const pt: Catalog = { "Resolve": "Resolver", "Restore": "Restaurar", "Restore to the page list": "Restaurar para a lista de páginas", + "Restored the version from {when} — ⌘Z undoes it": "Versão de {when} restaurada — ⌘Z desfaz.", "Room for a board or a table": "Espaço para um quadro ou uma tabela", "Rows": "Linhas", "Rows and columns": "Linhas e colunas", @@ -395,12 +431,18 @@ export const pt: Catalog = { "Saving…": "A guardar…", "Search": "Pesquisar", "Search all pages (⌘K)": "Pesquisar em todas as páginas (⌘K)", + "Search all pages, with nothing selected": "Pesquisar todas as páginas, sem nada selecionado", "Search all pages…": "Pesquisar em todas as páginas…", "Search this space": "Pesquisar neste espaço", + "Select": "Seleção", "Set a password…": "Definir uma palavra-passe…", "Share this space": "Partilhar este espaço", "Show as a board": "Mostrar como quadro", + "Show as a gallery": "Mostrar como galeria", "Show as a list": "Mostrar como lista", + "Show as a table": "Mostrar como tabela", + "Show or hide properties": "Mostrar ou ocultar as propriedades", + "Show or hide the page list": "Mostrar ou ocultar a lista de páginas", "Show playback controls to the reader": "Mostrar os controlos de reprodução a quem lê", "Show properties (])": "Mostrar propriedades (])", "Show the page list ([)": "Mostrar a lista de páginas ([)", @@ -432,7 +474,10 @@ export const pt: Catalog = { "That is not a bento/spaces document": "Isto não é um documento bento/spaces", "That is {n} files — importing them all may take a moment. Continue?": "São {n} ficheiros — importar todos pode demorar um momento. Continuar?", "That needs to be an http or https address": "Tem de ser um endereço http ou https", + "That section only": "Apenas essa secção", "That space is password-protected. Open it, then export the pages you want.": "Esse espaço está protegido por palavra-passe. Abra-o e exporte de lá as páginas que quiser.", + "That version could not be read": "Não foi possível ler essa versão", + "The block menu, on an empty line": "O menu de blocos, numa linha vazia", "The counterpart of Copy document JSON: edit a space in another tool, then bring it back.": "O par de Copiar o JSON do documento: edite um espaço noutra ferramenta e traga-o de volta.", "The day after": "O dia seguinte", "The day before": "O dia anterior", @@ -446,8 +491,10 @@ export const pt: Catalog = { "The pages without the editing tools": "As páginas sem as ferramentas de edição", "The rest name notes that were not in the selection, and are left as text.": "Os restantes nomeiam notas que não estavam na seleção e ficam como texto.", "The theme follows whoever opens the file. It is never written into the document.": "O tema segue quem abre o ficheiro. Nunca é escrito no documento.", + "The whole page": "A página inteira", "The whole space": "Todo o espaço", "The whole space, or just this page": "Todo o espaço, ou apenas esta página", + "The workspace": "A área de trabalho", "Then send someone the file": "Depois envia o ficheiro a alguém", "These windows are on this computer. Start a session to work with someone elsewhere.": "Estas janelas estão neste computador. Inicia uma sessão para trabalhar com alguém noutro lugar.", "This block has no settings of its own — its content is all of it.": "Este bloco não tem definições próprias — o conteúdo é tudo o que há.", @@ -455,6 +502,8 @@ export const pt: Catalog = { "This browser cannot write back to the file, so every save makes a new copy. Chrome and Edge on a computer can save in place.": "Este navegador não consegue escrever de volta no ficheiro, por isso cada vez que guarda cria uma nova cópia. O Chrome e o Edge num computador conseguem guardar no local.", "This clip is {size} and travels inside the file, making it that much bigger for everyone you send it to. Embed it anyway?": "Este clipe tem {size} e viaja dentro do ficheiro, tornando-o maior para todas as pessoas a quem o enviar. Incorporar mesmo assim?", "This copy goes offline; the others carry on": "Esta cópia fica offline; as outras continuam", + "This embed is inside itself — the loop stops here.": "Esta incorporação contém-se a si própria — o ciclo para aqui.", + "This embed points at a page that is not here.": "Esta incorporação aponta para uma página que não está aqui.", "This file": "Este ficheiro", "This file carries its own app — it works offline, forever, as is.": "Este arquivo carrega o próprio aplicativo — funciona offline, para sempre, do jeito que está.", "This file could not be opened": "Não foi possível abrir este ficheiro", @@ -466,10 +515,12 @@ export const pt: Catalog = { "This is a reading copy. It opens for reading; nothing you do here changes the file.": "Esta é uma cópia de leitura. Abre para leitura; nada do que fizeres aqui altera o ficheiro.", "This is a view-only copy — it follows the live session but can’t change this space.": "Esta é uma cópia somente para visualização — ela acompanha a sessão ao vivo, mas não pode alterar este Space.", "This is not a bento/spaces document — {detail}.": "Isto não é um documento bento/spaces — {detail}.", + "This list": "Esta lista", "This live session has run out of room. Your change is saved in your copy, but collaborators won’t see it.": "Esta sessão ao vivo ficou sem espaço. Sua alteração fica salva na sua cópia, mas os colaboradores não a verão.", "This page only": "Apenas esta página", "This space has no live session to follow": "Este Space não tem sessão ao vivo para acompanhar", "This space has no pages.": "Este espaço não tem páginas.", + "This space is encrypted, so no versions are kept.": "Este espaço está cifrado, por isso não são guardadas versões.", "This space is encrypted. Saves stay encrypted.": "Este espaço está encriptado. Continua encriptado sempre que guardar.", "This space is locked": "Este espaço está bloqueado", "This window is still running v{v} — reload to finish. A v{v} backup was downloaded.": "Esta janela ainda está executando a v{v} — recarregue para concluir. Um backup da v{v} foi baixado.", @@ -483,6 +534,7 @@ export const pt: Catalog = { "Today": "Hoje", "Today's journal": "Diário de hoje", "Toggle": "Bloco recolhível", + "Toggle section": "Mostrar ou ocultar a secção", "Tone": "Tom", "Too many changes at once — live sync is catching up.": "Alterações demais de uma vez — a sincronização ao vivo está se atualizando.", "Top level": "Nível superior", @@ -491,6 +543,7 @@ export const pt: Catalog = { "Underline": "Sublinhado", "Underline — ⌘U": "Sublinhado — ⌘U", "Undo (⌘Z)": "Desfazer (⌘Z)", + "Undo, redo": "Anular, refazer", "Unlock": "Desbloquear", "Unlocked, but the document inside could not be read.": "Desbloqueado, mas não foi possível ler o documento lá dentro.", "Unsaved changes from a previous session were found.": "Foram encontradas alterações não guardadas de uma sessão anterior.", @@ -508,6 +561,7 @@ export const pt: Catalog = { "Verifying…": "Verificando…", "Version {v} is available.": "A versão {v} está disponível.", "Version, language, password, exports": "Versão, idioma, senha, exportações", + "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "As versões ficam apenas neste navegador — nunca no ficheiro, nunca online. Restaurar pode ser desfeito.", "Video": "Vídeo", "Video from {host}": "Vídeo de {host}", "Video or audio": "Vídeo ou áudio", @@ -521,12 +575,14 @@ export const pt: Catalog = { "What is in the picture": "O que está na imagem", "What this is": "O que é isto", "What’s new →": "Novidades →", + "Which pages": "Que páginas", "Wide": "Largo", "Width": "Largura", "Width %": "Largura %", "Width in the text column": "Largura na coluna de texto", "Windows on this computer still sync; turn offline mode off in About to work with someone elsewhere.": "As janelas neste computador continuam sincronizando; desative o modo offline em Sobre para trabalhar com alguém em outro lugar.", "Words": "Palavras", + "Writing": "Escrever", "Wrong password — try again": "Palavra-passe incorreta — tente novamente", "Yellow": "Amarelo", "You have the newest version ({v}).": "Já tem a versão mais recente ({v}).", @@ -539,6 +595,7 @@ export const pt: Catalog = { "bento/spaces {version} · {pages} page(s), {blocks} block(s). The document, the editor and the search are all in this one file.": "bento/spaces {version} · {pages} página(s), {blocks} bloco(s). O documento, o editor e a pesquisa estão todos neste único ficheiro.", "format v{v}": "formato v{v}", "just now": "agora mesmo", + "most recent": "mais recente", "none": "nenhuma", "you": "você", "you: {name} ✎": "você: {name} ✎", @@ -547,6 +604,8 @@ export const pt: Catalog = { "{name} joined": "{name} entrou", "{name} left": "{name} saiu", "{name} was removed": "{name} foi removido", + "{n} block": "{n} bloco", + "{n} blocks": "{n} blocos", "{n} connected": "{n} conectados", "{n} duplicate or missing id(s) were repaired so links and pages resolve.": "Foram reparados {n} id(s) duplicados ou em falta para que as ligações e as páginas funcionem.", "{n} embedded image(s) and clip(s) account for {size} of that — {pct}%. Everything else is text.": "{n} imagem(ns) e clipe(s) incorporados representam {size} desse total — {pct}%. O resto é tudo texto.", @@ -558,9 +617,11 @@ export const pt: Catalog = { "{n} image(s) go with them; the rest stay here.": "{n} imagem(ns) vão com elas; as restantes ficam aqui.", "{n} image(s) point at the web. Nothing loads until a reader asks.": "{n} imagem(ns) apontam para a web. Nada é carregado até um leitor pedir.", "{n} image(s) were left as paths because embedding stopped there.": "{n} imagem(ns) ficaram como caminhos porque a incorporação parou aí.", + "{n} link to this page": "{n} ligação para esta página", "{n} link(s) named pages that were not in that file, and are kept as text.": "{n} ligação(ões) nomeavam páginas que não estavam nesse ficheiro e ficam como texto.", "{n} link(s) point outside them and are kept as text naming the page they meant.": "{n} ligação(ões) apontam para fora e ficam como texto que nomeia a página pretendida.", "{n} link(s) to it will stop working.": "{n} ligação(ões) para ela deixam de funcionar.", + "{n} links to this page": "{n} ligações para esta página", "{n} note name(s) appear more than once, so links naming them all went to the first.": "{n} nome(s) de nota aparecem mais de uma vez, por isso as ligações com esse nome foram todas para a primeira.", "{n} of {total} wikilink(s) resolved.": "{n} de {total} wikilink(s) resolvidos.", "{n} page(s) had frontmatter, kept verbatim in a folded block.": "{n} página(s) tinham frontmatter, guardado tal e qual num bloco fechado.", @@ -569,6 +630,8 @@ export const pt: Catalog = { "{n} table(s) imported, with their column alignment.": "{n} tabela(s) importada(s), com o alinhamento das colunas.", "{n} table(s) kept as text: there is no table block in this format yet.": "{n} tabela(s) ficaram como texto: este formato ainda não tem bloco de tabela.", "{n} unresolved comment(s)": "{n} comentário(s) não resolvido(s)", + "{n} word": "{n} palavra", + "{n} words": "{n} palavras", "{n}d ago": "há {n}d", "{n}h ago": "há {n}h", "{n}m ago": "há {n}min", @@ -577,56 +640,4 @@ export const pt: Catalog = { "{pages} page(s) and {blocks} block(s) will travel.": "Vão viajar {pages} página(s) e {blocks} bloco(s).", "{pages} pages · {links} links": "{pages} páginas · {links} ligações", "⌘Z removes the imported pages again.": "⌘Z remove novamente as páginas importadas.", - "History": "Histórico", - "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "As versões ficam apenas neste navegador — nunca no ficheiro, nunca online. Restaurar pode ser desfeito.", - "This space is encrypted, so no versions are kept.": "Este espaço está cifrado, por isso não são guardadas versões.", - "No versions yet — they build up as you write and save.": "Ainda sem versões — vão-se acumulando à medida que escreves e guardas.", - "most recent": "mais recente", - "That version could not be read": "Não foi possível ler essa versão", - "Restored the version from {when} — ⌘Z undoes it": "Versão de {when} restaurada — ⌘Z desfaz.", - "Dismiss": "Dispensar", - "Toggle section": "Mostrar ou ocultar a secção", - "{n} block": "{n} bloco", - "{n} blocks": "{n} blocos", - "{n} word": "{n} palavra", - "{n} words": "{n} palavras", - "{n} link to this page": "{n} ligação para esta página", - "{n} links to this page": "{n} ligações para esta página", - "A new block": "Um bloco novo", - "Find and replace": "Localizar e substituir", - "Formatting": "Formatação", - "Getting around": "Navegar", - "Indent, or move back out": "Avançar, ou recuar de novo", - "Keyboard shortcuts": "Atalhos de teclado", - "Leave the reading view": "Sair da vista de leitura", - "Link the selected words": "Ligar as palavras selecionadas", - "Link to another page": "Ligar a outra página", - "Search all pages, with nothing selected": "Pesquisar todas as páginas, sem nada selecionado", - "Show or hide properties": "Mostrar ou ocultar as propriedades", - "Show or hide the page list": "Mostrar ou ocultar a lista de páginas", - "The block menu, on an empty line": "O menu de blocos, numa linha vazia", - "The workspace": "A área de trabalho", - "This list": "Esta lista", - "Undo, redo": "Anular, refazer", - "Writing": "Escrever", - "Add": "Adicionar", - "Add a property": "Adicionar uma propriedade", - "Add property…": "Adicionar propriedade…", - "Added {name}": "{name} adicionada", - "Name": "Nome", - "New property": "Nova propriedade", - "Choose which pages this view holds": "Escolhe que páginas esta vista contém", - "Every page with a status": "Todas as páginas com estado", - "Nested under": "Aninhadas em", - "Pages nested under this one": "Páginas aninhadas sob esta", - "Pages that have this property": "Páginas que têm esta propriedade", - "Which pages": "Que páginas", - "Gallery": "Galeria", - "Show as a gallery": "Mostrar como galeria", - "Show as a table": "Mostrar como tabela", - "Select": "Seleção", - "Number": "Número", - "Date": "Data", - "Person": "Pessoa", - "Labels": "Etiquetas", } diff --git a/spaces/src/i18n/zh-Hans.ts b/spaces/src/i18n/zh-Hans.ts index f97f50a2..4dc8bce2 100644 --- a/spaces/src/i18n/zh-Hans.ts +++ b/spaces/src/i18n/zh-Hans.ts @@ -18,7 +18,9 @@ export const zh_Hans: Catalog = { "A folder of notes, or another space": "一个笔记文件夹,或另一个空间", "A link with nothing in it yet": "还没有内容的链接", "A list of every page, in order": "按顺序列出的所有页面", + "A live view of another page": "另一个页面的实时视图", "A live viewer: follows every edit as it happens but can never change this space — the relay enforces it.": "实时查看副本:实时跟随每次编辑,但永远无法修改此 Space — 由中继强制执行。", + "A new block": "新建一个块", "A note, tip or warning": "注释、提示或警告", "A page cannot contain itself": "页面不能包含自身", "A password encrypts the document inside the file. There is no recovery — lose it and the space is gone.": "密码会加密文件内的文档。没有任何找回方式 — 密码丢了,空间就没了。", @@ -32,6 +34,7 @@ export const zh_Hans: Catalog = { "About bento/spaces — version, updates, language, password": "关于 bento/spaces — 版本、更新、语言、密码", "About this space": "关于此空间", "Access reset — only copies saved from now on can join": "访问已重置 — 只有此后保存的副本才能加入", + "Add": "添加", "Add a block below": "在下方添加块", "Add a card": "添加卡片", "Add a card that opens a page": "添加一张打开页面的卡片", @@ -39,9 +42,12 @@ export const zh_Hans: Catalog = { "Add a link": "添加链接", "Add a page": "添加页面", "Add a picture": "添加图片", + "Add a property": "添加属性", "Add a row below": "在下方添加一行", "Add below": "在下方添加", "Add pages under": "将页面添加到", + "Add property…": "添加属性…", + "Added {name}": "已添加{name}", "Address of a video or audio file": "视频或音频文件的地址", "Adds status, priority, assignee, estimate": "添加状态、优先级、负责人和估算", "All text. Nothing is embedded, so this file is as small as a space gets.": "全是文字。没有嵌入任何内容,所以这个文件已是空间能达到的最小体积。", @@ -100,6 +106,7 @@ export const zh_Hans: Catalog = { "Choose a space…": "选择一个空间…", "Choose the field the columns come from": "选择用作分栏的字段", "Choose the order": "选择排序方式", + "Choose which pages this view holds": "选择此视图包含哪些页面", "Choose…": "选择…", "Clear filter": "清除筛选", "Clear formatting": "清除格式", @@ -135,6 +142,7 @@ export const zh_Hans: Catalog = { "Cover": "封面", "Create “{name}”": "创建“{name}”", "Dark": "深色", + "Date": "日期", "Default": "默认", "Delete": "删除", "Delete “{name}”?": "要删除“{name}”吗?", @@ -142,6 +150,7 @@ export const zh_Hans: Catalog = { "Descending": "降序", "Description": "描述", "Discard": "放弃", + "Dismiss": "关闭", "Divider": "分隔线", "Document": "文档", "Document JSON copied": "已复制文档 JSON", @@ -166,10 +175,14 @@ export const zh_Hans: Catalog = { "Editing": "编辑中", "Editor": "编辑者", "Editor copy saved — recipients join live with edit access": "编辑者副本已保存 — 收到的人可带编辑权限加入实时会话", + "Embed a page": "嵌入页面", + "Embedded from {page}": "嵌入自“{page}”", "Embedded in the file": "已嵌入文件", + "Embeds are not followed deeper than this.": "嵌入不会再向下展开。", "Encrypt the document inside this file": "加密此文件内的文档", "Enter the password to open it.": "输入密码以打开。", "Every page opens wide on this screen from now on": "在此屏幕上,之后所有页面都会以加宽方式打开", + "Every page with a status": "所有带状态的页面", "Every page, and what links to what": "所有页面,以及它们之间的链接", "Every page, as one .md file": "所有页面合并为一个 .md 文件", "Everything in this space is replaced by what you paste. ⌘Z undoes it, but only while this window stays open.": "这个空间里的所有内容都会被你粘贴的内容替换。⌘Z 可以撤销,但仅限于这个窗口保持打开时。", @@ -185,11 +198,16 @@ export const zh_Hans: Catalog = { "Filter": "筛选", "Filter blocks…": "筛选块…", "Find": "查找", + "Find a page or a section…": "查找页面或章节…", + "Find and replace": "查找和替换", "Find in this space…": "在此空间中查找…", "Find or create a page…": "查找或新建页面…", "Fit": "适应窗口", "Following — view only": "跟随中 — 仅供查看", + "Formatting": "格式", "Full width": "全宽", + "Gallery": "图库", + "Getting around": "导航", "Graph": "关系图", "Gray": "灰色", "Green": "绿色", @@ -203,6 +221,7 @@ export const zh_Hans: Catalog = { "Hide the page list ([)": "隐藏页面列表([)", "Highlight": "高亮", "Highlight — ⇧⌘H": "高亮 — ⇧⌘H", + "History": "历史记录", "Icon": "图标", "Image": "图片", "Image added ({size})": "已添加图片 ({size})", @@ -219,6 +238,7 @@ export const zh_Hans: Catalog = { "Include archived pages": "包含已归档页面", "Include the image files and they are embedded too. An image this browser cannot open is kept as its path rather than as a broken picture.": "把图片文件一起选上,它们也会被嵌入。这个浏览器打不开的图片会以路径的形式保留,而不是一张坏掉的图。", "Include the pages nested under it": "包含其下级页面", + "Indent, or move back out": "缩进,或退回一级", "Inline code — ⌘E": "行内代码 — ⌘E", "Insert": "插入", "Insert a block — text, headings, lists, code, images": "插入块 — 文本、标题、列表、代码、图片", @@ -232,16 +252,21 @@ export const zh_Hans: Catalog = { "Italic — ⌘I": "斜体 — ⌘I", "Journal date": "日志日期", "Key-verified identity": "密钥验证的身份", + "Keyboard shortcuts": "键盘快捷键", + "Labels": "标签", "Language": "语言", "Language follows whoever opens the file. It is never written into the document.": "语言跟随打开文件的人,绝不会写入文档。", "Language — what this block is highlighted as": "语言 — 这个块按什么语言着色", "Last saved": "上次保存", "Launch check couldn't reach the release server ({m}). Check manually below.": "启动检查无法连接发布服务器 ({m})。请在下方手动检查。", "Leave it empty to use the tone mark": "留空则使用该类型的标记", + "Leave the reading view": "退出阅读视图", "Light": "浅色", "Link": "链接", "Link address": "链接地址", "Link card": "链接卡片", + "Link the selected words": "为选中的文字添加链接", + "Link to another page": "链接到另一个页面", "Link to page": "链接到页面", "Link to the web": "链接到网页", "Link — ⌘K": "链接 — ⌘K", @@ -265,11 +290,14 @@ export const zh_Hans: Catalog = { "Move down": "下移", "Move up": "上移", "Muted": "静音", + "Name": "名称", "Name this canvas": "为此画布命名", + "Nested under": "嵌套于", "New issue": "新建事项", "New page": "新建页面", "New page (⌘⌥N)": "新建页面 (⌘⌥N)", "New page inside": "在其中新建页面", + "New property": "新建属性", "New to Bento? Find templates, the gallery and the AI editing guide at {home} — or ⭐ it on {gh}.": "第一次用 Bento?在 {home} 查看模板、图库和 AI 编辑指南 — 也欢迎到 {gh} 点 ⭐。", "Next (⏎)": "下一个 (⏎)", "No Markdown files in that selection": "所选内容里没有 Markdown 文件", @@ -277,7 +305,10 @@ export const zh_Hans: Catalog = { "No field here has options to group by": "这里没有可用于分组的选项字段", "No issues match this filter.": "没有符合此筛选条件的事项。", "No issues yet. Add a status field to any page and it appears here.": "还没有事项。给任意页面添加状态字段,它就会出现在这里。", + "No page matches": "没有匹配的页面", "No pages yet": "还没有页面", + "No section named {name} on this page.": "本页没有名为“{name}”的标题。", + "No versions yet — they build up as you write and save.": "暂无版本。随着你的书写和保存,版本会逐渐累积。", "Nobody else is here yet": "还没有其他人", "Not in this file: it needs the network, and the site is told when someone opens the page": "不在此文件内:播放需要联网,而且该网站会知道有人打开了这个页面", "Not live — turns on when you share": "未上线 — 分享时自动开启", @@ -290,6 +321,7 @@ export const zh_Hans: Catalog = { "Nothing on this canvas yet.": "此画布上还没有内容。", "Nothing to draw yet — link two pages with [[ and they will appear here.": "还没有可绘制的内容 — 用 [[ 链接两个页面,它们就会出现在这里。", "Now an issue": "现在是事项了", + "Number": "数字", "Numbered list": "编号列表", "Off by default — they were archived for a reason": "默认关闭 — 当初归档总是有原因的", "Offline mode is on — nothing leaves this computer.": "离线模式已开启 — 任何数据都不会离开这台电脑。", @@ -310,7 +342,9 @@ export const zh_Hans: Catalog = { "Page": "页面", "Page options": "页面选项", "Pages": "页面", + "Pages nested under this one": "嵌套在此页面下的页面", "Pages open at their normal width again": "页面已恢复为正常宽度", + "Pages that have this property": "具有此属性的页面", "Pages — show or hide the page list": "页面 — 显示或隐藏页面列表", "Password": "密码", "Password removed. Save to write the space unencrypted.": "已移除密码。保存后空间将以未加密方式写入。", @@ -319,6 +353,7 @@ export const zh_Hans: Catalog = { "Paste document JSON here…": "在此粘贴文档 JSON…", "Paste or type a link": "粘贴或输入链接", "People in this space": "此空间中的人", + "Person": "人员", "Picture": "图片", "Picture…": "图片…", "Pink": "粉色", @@ -381,6 +416,7 @@ export const zh_Hans: Catalog = { "Resolve": "解决", "Restore": "恢复", "Restore to the page list": "恢复到页面列表", + "Restored the version from {when} — ⌘Z undoes it": "已恢复 {when} 的版本,按 ⌘Z 可撤销。", "Room for a board or a table": "给看板或表格留出空间", "Rows": "行", "Rows and columns": "行和列", @@ -395,12 +431,18 @@ export const zh_Hans: Catalog = { "Saving…": "保存中…", "Search": "搜索", "Search all pages (⌘K)": "搜索所有页面 (⌘K)", + "Search all pages, with nothing selected": "未选中内容时,搜索所有页面", "Search all pages…": "搜索所有页面…", "Search this space": "搜索此空间", + "Select": "单选", "Set a password…": "设置密码…", "Share this space": "共享此空间", "Show as a board": "以看板显示", + "Show as a gallery": "以图库显示", "Show as a list": "以列表显示", + "Show as a table": "以表格显示", + "Show or hide properties": "显示或隐藏属性", + "Show or hide the page list": "显示或隐藏页面列表", "Show playback controls to the reader": "向读者显示播放控件", "Show properties (])": "显示属性面板(])", "Show the page list ([)": "显示页面列表([)", @@ -432,7 +474,10 @@ export const zh_Hans: Catalog = { "That is not a bento/spaces document": "这不是 bento/spaces 文档", "That is {n} files — importing them all may take a moment. Continue?": "一共 {n} 个文件,全部导入需要一点时间。继续吗?", "That needs to be an http or https address": "这里需要填写 http 或 https 地址", + "That section only": "仅该章节", "That space is password-protected. Open it, then export the pages you want.": "该空间受密码保护。请先打开它,再导出你需要的页面。", + "That version could not be read": "无法读取该版本", + "The block menu, on an empty line": "在空行上打开块菜单", "The counterpart of Copy document JSON: edit a space in another tool, then bring it back.": "“复制文档 JSON”的对应功能:在别的工具里编辑一个空间,再把它带回来。", "The day after": "后一天", "The day before": "前一天", @@ -446,8 +491,10 @@ export const zh_Hans: Catalog = { "The pages without the editing tools": "不带编辑工具的页面", "The rest name notes that were not in the selection, and are left as text.": "其余指向不在所选范围内的笔记,保留为文字。", "The theme follows whoever opens the file. It is never written into the document.": "主题跟随打开文件的人,绝不会写入文档。", + "The whole page": "整个页面", "The whole space": "整个空间", "The whole space, or just this page": "整个空间,或仅这一页", + "The workspace": "工作区", "Then send someone the file": "然后把文件发给对方", "These windows are on this computer. Start a session to work with someone elsewhere.": "这些窗口都在本机。若要与其他地方的人协作,请开始一个会话。", "This block has no settings of its own — its content is all of it.": "这个块没有自己的设置 — 内容就是它的全部。", @@ -455,6 +502,8 @@ export const zh_Hans: Catalog = { "This browser cannot write back to the file, so every save makes a new copy. Chrome and Edge on a computer can save in place.": "此浏览器无法写回原文件,因此每次保存都会生成新副本。电脑上的 Chrome 和 Edge 可以就地保存。", "This clip is {size} and travels inside the file, making it that much bigger for everyone you send it to. Embed it anyway?": "这个片段有 {size},会嵌在文件内部,你发给谁,文件对谁就大出这么多。仍要嵌入吗?", "This copy goes offline; the others carry on": "此副本将离线;其他人继续协作", + "This embed is inside itself — the loop stops here.": "该嵌入包含了自身——循环在此停止。", + "This embed points at a page that is not here.": "该嵌入指向的页面不存在。", "This file": "此文件", "This file carries its own app — it works offline, forever, as is.": "此文件自带应用 — 离线可用,永久如此。", "This file could not be opened": "无法打开此文件", @@ -466,10 +515,12 @@ export const zh_Hans: Catalog = { "This is a reading copy. It opens for reading; nothing you do here changes the file.": "这是一份阅读副本。它以阅读方式打开;你在此处的操作不会更改文件。", "This is a view-only copy — it follows the live session but can’t change this space.": "这是只读副本 — 会跟随实时会话,但无法修改此 Space。", "This is not a bento/spaces document — {detail}.": "这不是 bento/spaces 文档 — {detail}。", + "This list": "本列表", "This live session has run out of room. Your change is saved in your copy, but collaborators won’t see it.": "此实时会话空间已满。你的更改已保存在你的副本中,但协作者不会看到。", "This page only": "仅此页面", "This space has no live session to follow": "此 Space 没有可跟随的实时会话", "This space has no pages.": "此空间没有页面。", + "This space is encrypted, so no versions are kept.": "此空间已加密,因此不保存任何版本。", "This space is encrypted. Saves stay encrypted.": "此空间已加密。保存时仍保持加密。", "This space is locked": "此空间已锁定", "This window is still running v{v} — reload to finish. A v{v} backup was downloaded.": "此窗口仍在运行 v{v} — 重新加载以完成。已下载 v{v} 备份。", @@ -483,6 +534,7 @@ export const zh_Hans: Catalog = { "Today": "今天", "Today's journal": "今天的日志", "Toggle": "折叠块", + "Toggle section": "展开或收起此节", "Tone": "语气", "Too many changes at once — live sync is catching up.": "更改过多 — 实时同步正在追赶。", "Top level": "顶层", @@ -491,6 +543,7 @@ export const zh_Hans: Catalog = { "Underline": "下划线", "Underline — ⌘U": "下划线 — ⌘U", "Undo (⌘Z)": "撤销 (⌘Z)", + "Undo, redo": "撤销、重做", "Unlock": "解锁", "Unlocked, but the document inside could not be read.": "已解锁,但无法读取其中的文档。", "Unsaved changes from a previous session were found.": "发现上一次会话未保存的更改。", @@ -508,6 +561,7 @@ export const zh_Hans: Catalog = { "Verifying…": "验证中…", "Version {v} is available.": "版本 {v} 可用。", "Version, language, password, exports": "版本、语言、密码、导出", + "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "版本仅保存在此浏览器中,不会写入文件,也不会上传。恢复操作可以撤销。", "Video": "视频", "Video from {host}": "来自 {host} 的视频", "Video or audio": "视频或音频", @@ -521,12 +575,14 @@ export const zh_Hans: Catalog = { "What is in the picture": "图片里是什么", "What this is": "这是什么", "What’s new →": "更新内容 →", + "Which pages": "哪些页面", "Wide": "加宽", "Width": "宽度", "Width %": "宽度 %", "Width in the text column": "在正文栏中的宽度", "Windows on this computer still sync; turn offline mode off in About to work with someone elsewhere.": "本机的窗口仍会同步;要与其他地方的人协作,请在“关于”中关闭离线模式。", "Words": "字数", + "Writing": "写作", "Wrong password — try again": "密码错误 — 请重试", "Yellow": "黄色", "You have the newest version ({v}).": "你已是最新版本 ({v})。", @@ -539,6 +595,7 @@ export const zh_Hans: Catalog = { "bento/spaces {version} · {pages} page(s), {blocks} block(s). The document, the editor and the search are all in this one file.": "bento/spaces {version} · {pages} 个页面,{blocks} 个块。文档、编辑器和搜索全都在这一个文件里。", "format v{v}": "格式 v{v}", "just now": "刚刚", + "most recent": "最新", "none": "无", "you": "你", "you: {name} ✎": "你:{name} ✎", @@ -547,6 +604,8 @@ export const zh_Hans: Catalog = { "{name} joined": "{name} 已加入", "{name} left": "{name} 已离开", "{name} was removed": "已移除 {name}", + "{n} block": "{n} 个块", + "{n} blocks": "{n} 个块", "{n} connected": "{n} 人已连接", "{n} duplicate or missing id(s) were repaired so links and pages resolve.": "已修复 {n} 个重复或缺失的 id,链接和页面现在都能正常解析。", "{n} embedded image(s) and clip(s) account for {size} of that — {pct}%. Everything else is text.": "其中 {n} 个嵌入的图片和片段占了 {size},也就是 {pct}%。其余全是文字。", @@ -558,9 +617,11 @@ export const zh_Hans: Catalog = { "{n} image(s) go with them; the rest stay here.": "{n} 张图片会一起带走,其余留在这里。", "{n} image(s) point at the web. Nothing loads until a reader asks.": "{n} 张图片指向网络。在读者主动请求之前不会加载。", "{n} image(s) were left as paths because embedding stopped there.": "{n} 张图片以路径形式保留,因为嵌入到此为止。", + "{n} link to this page": "{n} 个链接指向此页", "{n} link(s) named pages that were not in that file, and are kept as text.": "有 {n} 个链接指向该文件中不存在的页面,已保留为文字。", "{n} link(s) point outside them and are kept as text naming the page they meant.": "有 {n} 个链接指向其外部,将保留为写明原目标页面的文字。", "{n} link(s) to it will stop working.": "指向它的 {n} 个链接会失效。", + "{n} links to this page": "{n} 个链接指向此页", "{n} note name(s) appear more than once, so links naming them all went to the first.": "有 {n} 个笔记名称重复,因此指向该名称的链接都指向了第一个。", "{n} of {total} wikilink(s) resolved.": "{total} 个 wiki 链接中解析了 {n} 个。", "{n} page(s) had frontmatter, kept verbatim in a folded block.": "{n} 个页面带有 frontmatter,已原样保存在折叠块里。", @@ -569,6 +630,8 @@ export const zh_Hans: Catalog = { "{n} table(s) imported, with their column alignment.": "已导入 {n} 个表格,并保留列对齐方式。", "{n} table(s) kept as text: there is no table block in this format yet.": "{n} 个表格按原文保留为文字:这个格式还没有表格块。", "{n} unresolved comment(s)": "{n} 条未解决的评论", + "{n} word": "{n} 个词", + "{n} words": "{n} 个词", "{n}d ago": "{n} 天前", "{n}h ago": "{n} 小时前", "{n}m ago": "{n} 分钟前", @@ -577,56 +640,4 @@ export const zh_Hans: Catalog = { "{pages} page(s) and {blocks} block(s) will travel.": "将带走 {pages} 个页面、{blocks} 个块。", "{pages} pages · {links} links": "{pages} 个页面 · {links} 条链接", "⌘Z removes the imported pages again.": "按 ⌘Z 可以把导入的页面再撤销掉。", - "History": "历史记录", - "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "版本仅保存在此浏览器中,不会写入文件,也不会上传。恢复操作可以撤销。", - "This space is encrypted, so no versions are kept.": "此空间已加密,因此不保存任何版本。", - "No versions yet — they build up as you write and save.": "暂无版本。随着你的书写和保存,版本会逐渐累积。", - "most recent": "最新", - "That version could not be read": "无法读取该版本", - "Restored the version from {when} — ⌘Z undoes it": "已恢复 {when} 的版本,按 ⌘Z 可撤销。", - "Dismiss": "关闭", - "Toggle section": "展开或收起此节", - "{n} block": "{n} 个块", - "{n} blocks": "{n} 个块", - "{n} word": "{n} 个词", - "{n} words": "{n} 个词", - "{n} link to this page": "{n} 个链接指向此页", - "{n} links to this page": "{n} 个链接指向此页", - "A new block": "新建一个块", - "Find and replace": "查找和替换", - "Formatting": "格式", - "Getting around": "导航", - "Indent, or move back out": "缩进,或退回一级", - "Keyboard shortcuts": "键盘快捷键", - "Leave the reading view": "退出阅读视图", - "Link the selected words": "为选中的文字添加链接", - "Link to another page": "链接到另一个页面", - "Search all pages, with nothing selected": "未选中内容时,搜索所有页面", - "Show or hide properties": "显示或隐藏属性", - "Show or hide the page list": "显示或隐藏页面列表", - "The block menu, on an empty line": "在空行上打开块菜单", - "The workspace": "工作区", - "This list": "本列表", - "Undo, redo": "撤销、重做", - "Writing": "写作", - "Add": "添加", - "Add a property": "添加属性", - "Add property…": "添加属性…", - "Added {name}": "已添加{name}", - "Name": "名称", - "New property": "新建属性", - "Choose which pages this view holds": "选择此视图包含哪些页面", - "Every page with a status": "所有带状态的页面", - "Nested under": "嵌套于", - "Pages nested under this one": "嵌套在此页面下的页面", - "Pages that have this property": "具有此属性的页面", - "Which pages": "哪些页面", - "Gallery": "图库", - "Show as a gallery": "以图库显示", - "Show as a table": "以表格显示", - "Select": "单选", - "Number": "数字", - "Date": "日期", - "Person": "人员", - "Labels": "标签", } diff --git a/spaces/src/i18n/zh-Hant.ts b/spaces/src/i18n/zh-Hant.ts index 85526d58..c798243e 100644 --- a/spaces/src/i18n/zh-Hant.ts +++ b/spaces/src/i18n/zh-Hant.ts @@ -18,7 +18,9 @@ export const zh_Hant: Catalog = { "A folder of notes, or another space": "一個筆記資料夾,或另一個空間", "A link with nothing in it yet": "還沒有內容的連結", "A list of every page, in order": "依順序列出每一個頁面", + "A live view of another page": "另一個頁面的即時檢視", "A live viewer: follows every edit as it happens but can never change this space — the relay enforces it.": "即時檢視副本:即時跟隨每次編輯,但永遠無法修改此 Space — 由中繼強制執行。", + "A new block": "新增一個區塊", "A note, tip or warning": "註記、提示或警告", "A page cannot contain itself": "頁面不能包含自己", "A password encrypts the document inside the file. There is no recovery — lose it and the space is gone.": "密碼會加密檔案內的文件。沒有任何復原方式 — 密碼一旦遺失,這個空間就沒了。", @@ -32,6 +34,7 @@ export const zh_Hant: Catalog = { "About bento/spaces — version, updates, language, password": "關於 bento/spaces — 版本、更新、語言、密碼", "About this space": "關於此空間", "Access reset — only copies saved from now on can join": "存取已重設 — 只有此後儲存的副本才能加入", + "Add": "新增", "Add a block below": "在下方新增區塊", "Add a card": "新增卡片", "Add a card that opens a page": "新增可開啟頁面的卡片", @@ -39,9 +42,12 @@ export const zh_Hant: Catalog = { "Add a link": "新增連結", "Add a page": "新增頁面", "Add a picture": "新增圖片", + "Add a property": "新增屬性", "Add a row below": "在下方新增一列", "Add below": "在下方新增", "Add pages under": "將頁面加入到", + "Add property…": "新增屬性…", + "Added {name}": "已新增{name}", "Address of a video or audio file": "影片或音訊檔案的位址", "Adds status, priority, assignee, estimate": "新增狀態、優先順序、負責人與估算", "All text. Nothing is embedded, so this file is as small as a space gets.": "全是文字。沒有嵌入任何內容,所以這個檔案已是空間能達到的最小體積。", @@ -100,6 +106,7 @@ export const zh_Hant: Catalog = { "Choose a space…": "選擇一個空間…", "Choose the field the columns come from": "選擇作為欄的欄位", "Choose the order": "選擇排序方式", + "Choose which pages this view holds": "選擇此檢視包含哪些頁面", "Choose…": "選擇…", "Clear filter": "清除篩選", "Clear formatting": "清除格式", @@ -135,6 +142,7 @@ export const zh_Hant: Catalog = { "Cover": "封面", "Create “{name}”": "建立「{name}」", "Dark": "深色", + "Date": "日期", "Default": "預設", "Delete": "刪除", "Delete “{name}”?": "要刪除「{name}」嗎?", @@ -142,6 +150,7 @@ export const zh_Hant: Catalog = { "Descending": "遞減", "Description": "描述", "Discard": "捨棄", + "Dismiss": "關閉", "Divider": "分隔線", "Document": "文件", "Document JSON copied": "已複製文件 JSON", @@ -166,10 +175,14 @@ export const zh_Hant: Catalog = { "Editing": "編輯中", "Editor": "編輯者", "Editor copy saved — recipients join live with edit access": "編輯者副本已儲存 — 收到的人可帶編輯權限加入即時會話", + "Embed a page": "嵌入頁面", + "Embedded from {page}": "嵌入自「{page}」", "Embedded in the file": "已嵌入檔案", + "Embeds are not followed deeper than this.": "嵌入不會再向下展開。", "Encrypt the document inside this file": "加密此檔案內的文件", "Enter the password to open it.": "請輸入密碼以開啟。", "Every page opens wide on this screen from now on": "在此螢幕上,之後所有頁面都會以加寬方式開啟", + "Every page with a status": "所有帶狀態的頁面", "Every page, and what links to what": "所有頁面,以及它們之間的連結", "Every page, as one .md file": "所有頁面合併為一個 .md 檔", "Everything in this space is replaced by what you paste. ⌘Z undoes it, but only while this window stays open.": "這個空間裡的所有內容都會被你貼上的內容取代。⌘Z 可以復原,但僅限於這個視窗保持開啟時。", @@ -185,11 +198,16 @@ export const zh_Hant: Catalog = { "Filter": "篩選", "Filter blocks…": "篩選區塊…", "Find": "尋找", + "Find a page or a section…": "尋找頁面或章節…", + "Find and replace": "尋找與取代", "Find in this space…": "在此空間尋找…", "Find or create a page…": "尋找或建立頁面…", "Fit": "適應視窗", "Following — view only": "跟隨中 — 僅供檢視", + "Formatting": "格式", "Full width": "全寬", + "Gallery": "圖庫", + "Getting around": "導覽", "Graph": "關係圖", "Gray": "灰色", "Green": "綠色", @@ -203,6 +221,7 @@ export const zh_Hant: Catalog = { "Hide the page list ([)": "隱藏頁面清單([)", "Highlight": "螢光標示", "Highlight — ⇧⌘H": "螢光標示 — ⇧⌘H", + "History": "歷史記錄", "Icon": "圖示", "Image": "影像", "Image added ({size})": "已新增圖片 ({size})", @@ -219,6 +238,7 @@ export const zh_Hant: Catalog = { "Include archived pages": "包含已封存的頁面", "Include the image files and they are embedded too. An image this browser cannot open is kept as its path rather than as a broken picture.": "把圖片檔案一起選取,它們也會一併嵌入。這個瀏覽器打不開的圖片會以路徑的形式保留,而不是一張破圖。", "Include the pages nested under it": "包含其下層頁面", + "Indent, or move back out": "縮排,或退回一層", "Inline code — ⌘E": "行內程式碼 — ⌘E", "Insert": "插入", "Insert a block — text, headings, lists, code, images": "插入區塊 — 文字、標題、清單、程式碼、圖片", @@ -232,16 +252,21 @@ export const zh_Hant: Catalog = { "Italic — ⌘I": "斜體 — ⌘I", "Journal date": "日誌日期", "Key-verified identity": "金鑰驗證的身份", + "Keyboard shortcuts": "鍵盤快速鍵", + "Labels": "標籤", "Language": "語言", "Language follows whoever opens the file. It is never written into the document.": "語言取決於開啟檔案的人,永遠不會寫入文件中。", "Language — what this block is highlighted as": "語言 — 這個區塊以什麼語言上色", "Last saved": "上次儲存", "Launch check couldn't reach the release server ({m}). Check manually below.": "啟動檢查無法連線發佈伺服器 ({m})。請在下方手動檢查。", "Leave it empty to use the tone mark": "留空則使用該類型的標記", + "Leave the reading view": "離開閱讀檢視", "Light": "淺色", "Link": "連結", "Link address": "連結網址", "Link card": "連結卡片", + "Link the selected words": "為選取的文字加上連結", + "Link to another page": "連結到另一個頁面", "Link to page": "連結到頁面", "Link to the web": "連結到網頁", "Link — ⌘K": "連結 — ⌘K", @@ -265,11 +290,14 @@ export const zh_Hant: Catalog = { "Move down": "下移", "Move up": "上移", "Muted": "靜音", + "Name": "名稱", "Name this canvas": "為此畫布命名", + "Nested under": "巢狀於", "New issue": "新增項目", "New page": "新增頁面", "New page (⌘⌥N)": "新增頁面 (⌘⌥N)", "New page inside": "在其中新增頁面", + "New property": "新增屬性", "New to Bento? Find templates, the gallery and the AI editing guide at {home} — or ⭐ it on {gh}.": "第一次用 Bento?在 {home} 查看範本、圖庫和 AI 編輯指南 — 也歡迎到 {gh} 按 ⭐。", "Next (⏎)": "下一個 (⏎)", "No Markdown files in that selection": "所選項目中沒有 Markdown 檔案", @@ -277,7 +305,10 @@ export const zh_Hant: Catalog = { "No field here has options to group by": "這裡沒有可用於分組的選項欄位", "No issues match this filter.": "沒有符合此篩選條件的項目。", "No issues yet. Add a status field to any page and it appears here.": "還沒有項目。為任一頁面新增狀態欄位,它就會出現在這裡。", + "No page matches": "沒有相符的頁面", "No pages yet": "尚無頁面", + "No section named {name} on this page.": "本頁沒有名為「{name}」的標題。", + "No versions yet — they build up as you write and save.": "尚無版本。隨著你的書寫與儲存,版本會逐漸累積。", "Nobody else is here yet": "還沒有其他人", "Not in this file: it needs the network, and the site is told when someone opens the page": "不在這個檔案裡:播放需要網路,而且該網站會知道有人開啟了這個頁面", "Not live — turns on when you share": "未上線 — 分享時自動開啟", @@ -290,6 +321,7 @@ export const zh_Hant: Catalog = { "Nothing on this canvas yet.": "此畫布上還沒有內容。", "Nothing to draw yet — link two pages with [[ and they will appear here.": "還沒有可繪製的內容 — 用 [[ 連結兩個頁面,它們就會出現在這裡。", "Now an issue": "現在是項目了", + "Number": "數字", "Numbered list": "編號清單", "Off by default — they were archived for a reason": "預設關閉 — 它們被封存是有原因的", "Offline mode is on — nothing leaves this computer.": "離線模式已開啟 — 任何資料都不會離開這台電腦。", @@ -310,7 +342,9 @@ export const zh_Hant: Catalog = { "Page": "頁面", "Page options": "頁面選項", "Pages": "頁面", + "Pages nested under this one": "巢狀在此頁面下的頁面", "Pages open at their normal width again": "頁面已恢復為正常寬度", + "Pages that have this property": "具有此屬性的頁面", "Pages — show or hide the page list": "頁面 — 顯示或隱藏頁面清單", "Password": "密碼", "Password removed. Save to write the space unencrypted.": "已移除密碼。儲存後空間將以未加密方式寫入。", @@ -319,6 +353,7 @@ export const zh_Hant: Catalog = { "Paste document JSON here…": "在此貼上文件 JSON…", "Paste or type a link": "貼上或輸入連結", "People in this space": "此空間中的人", + "Person": "人員", "Picture": "圖片", "Picture…": "圖片…", "Pink": "粉紅色", @@ -381,6 +416,7 @@ export const zh_Hant: Catalog = { "Resolve": "解決", "Restore": "還原", "Restore to the page list": "還原到頁面清單", + "Restored the version from {when} — ⌘Z undoes it": "已還原 {when} 的版本,按 ⌘Z 可復原。", "Room for a board or a table": "給看板或表格留出空間", "Rows": "列", "Rows and columns": "列與欄", @@ -395,12 +431,18 @@ export const zh_Hant: Catalog = { "Saving…": "儲存中…", "Search": "搜尋", "Search all pages (⌘K)": "搜尋所有頁面 (⌘K)", + "Search all pages, with nothing selected": "未選取內容時,搜尋所有頁面", "Search all pages…": "搜尋所有頁面…", "Search this space": "搜尋此空間", + "Select": "單選", "Set a password…": "設定密碼…", "Share this space": "分享此空間", "Show as a board": "以看板顯示", + "Show as a gallery": "以圖庫顯示", "Show as a list": "以清單顯示", + "Show as a table": "以表格顯示", + "Show or hide properties": "顯示或隱藏屬性", + "Show or hide the page list": "顯示或隱藏頁面清單", "Show playback controls to the reader": "向讀者顯示播放控制項", "Show properties (])": "顯示屬性面板(])", "Show the page list ([)": "顯示頁面清單([)", @@ -432,7 +474,10 @@ export const zh_Hant: Catalog = { "That is not a bento/spaces document": "這不是 bento/spaces 文件", "That is {n} files — importing them all may take a moment. Continue?": "共 {n} 個檔案,全部匯入需要一點時間。要繼續嗎?", "That needs to be an http or https address": "這裡需要填 http 或 https 位址", + "That section only": "僅該章節", "That space is password-protected. Open it, then export the pages you want.": "該空間受密碼保護。請先開啟它,再匯出你需要的頁面。", + "That version could not be read": "無法讀取該版本", + "The block menu, on an empty line": "在空行上開啟區塊選單", "The counterpart of Copy document JSON: edit a space in another tool, then bring it back.": "「複製文件 JSON」的對應功能:在別的工具裡編輯一個空間,再把它帶回來。", "The day after": "後一天", "The day before": "前一天", @@ -446,8 +491,10 @@ export const zh_Hant: Catalog = { "The pages without the editing tools": "不含編輯工具的頁面", "The rest name notes that were not in the selection, and are left as text.": "其餘指向不在所選範圍內的筆記,保留為文字。", "The theme follows whoever opens the file. It is never written into the document.": "主題取決於開啟檔案的人,永遠不會寫入文件中。", + "The whole page": "整個頁面", "The whole space": "整個空間", "The whole space, or just this page": "整個空間,或僅此頁面", + "The workspace": "工作區", "Then send someone the file": "然後把檔案傳給對方", "These windows are on this computer. Start a session to work with someone elsewhere.": "這些視窗都在本機。若要與其他地方的人協作,請開始一個工作階段。", "This block has no settings of its own — its content is all of it.": "這個區塊沒有自己的設定 — 內容就是它的全部。", @@ -455,6 +502,8 @@ export const zh_Hant: Catalog = { "This browser cannot write back to the file, so every save makes a new copy. Chrome and Edge on a computer can save in place.": "此瀏覽器無法寫回檔案,因此每次儲存都會產生新的副本。電腦版的 Chrome 和 Edge 可以就地儲存。", "This clip is {size} and travels inside the file, making it that much bigger for everyone you send it to. Embed it anyway?": "這個片段有 {size},而且會跟著檔案一起走,你傳給的每個人拿到的檔案都會大這麼多。仍要嵌入嗎?", "This copy goes offline; the others carry on": "此副本將離線;其他人繼續協作", + "This embed is inside itself — the loop stops here.": "這個嵌入包含了自身——迴圈在此停止。", + "This embed points at a page that is not here.": "這個嵌入指向的頁面不存在。", "This file": "此檔案", "This file carries its own app — it works offline, forever, as is.": "此檔案自帶應用程式 — 離線可用,永遠如此。", "This file could not be opened": "無法開啟此檔案", @@ -466,10 +515,12 @@ export const zh_Hant: Catalog = { "This is a reading copy. It opens for reading; nothing you do here changes the file.": "這是一份閱讀副本。它以閱讀方式開啟;你在此處的操作不會變更檔案。", "This is a view-only copy — it follows the live session but can’t change this space.": "這是唯讀副本 — 會跟隨即時會話,但無法修改此 Space。", "This is not a bento/spaces document — {detail}.": "這不是 bento/spaces 文件 — {detail}。", + "This list": "本清單", "This live session has run out of room. Your change is saved in your copy, but collaborators won’t see it.": "此即時工作階段空間已滿。你的變更已儲存在你的副本中,但協作者不會看到。", "This page only": "僅此頁面", "This space has no live session to follow": "此 Space 沒有可跟隨的即時會話", "This space has no pages.": "此空間沒有任何頁面。", + "This space is encrypted, so no versions are kept.": "此空間已加密,因此不保存任何版本。", "This space is encrypted. Saves stay encrypted.": "此空間已加密。往後的儲存也會維持加密。", "This space is locked": "此空間已鎖定", "This window is still running v{v} — reload to finish. A v{v} backup was downloaded.": "此視窗仍在執行 v{v} — 重新載入以完成。已下載 v{v} 備份。", @@ -483,6 +534,7 @@ export const zh_Hant: Catalog = { "Today": "今天", "Today's journal": "今天的日誌", "Toggle": "摺疊區塊", + "Toggle section": "展開或收合此節", "Tone": "語氣", "Too many changes at once — live sync is catching up.": "變更太多 — 即時同步正在追趕。", "Top level": "最上層", @@ -491,6 +543,7 @@ export const zh_Hant: Catalog = { "Underline": "底線", "Underline — ⌘U": "底線 — ⌘U", "Undo (⌘Z)": "還原 (⌘Z)", + "Undo, redo": "復原、重做", "Unlock": "解鎖", "Unlocked, but the document inside could not be read.": "已解鎖,但無法讀取裡面的文件。", "Unsaved changes from a previous session were found.": "發現上一次工作階段未儲存的變更。", @@ -508,6 +561,7 @@ export const zh_Hant: Catalog = { "Verifying…": "驗證中…", "Version {v} is available.": "版本 {v} 可用。", "Version, language, password, exports": "版本、語言、密碼、匯出", + "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "版本僅保存在此瀏覽器中,不會寫入檔案,也不會上傳。還原操作可以復原。", "Video": "影片", "Video from {host}": "來自 {host} 的影片", "Video or audio": "影片或音訊", @@ -521,12 +575,14 @@ export const zh_Hant: Catalog = { "What is in the picture": "圖片裡是什麼", "What this is": "這是什麼", "What’s new →": "更新內容 →", + "Which pages": "哪些頁面", "Wide": "加寬", "Width": "寬度", "Width %": "寬度 %", "Width in the text column": "相對於文字欄的寬度", "Windows on this computer still sync; turn offline mode off in About to work with someone elsewhere.": "本機的視窗仍會同步;要與其他地方的人協作,請在「關於」中關閉離線模式。", "Words": "字數", + "Writing": "書寫", "Wrong password — try again": "密碼錯誤 — 請重試", "Yellow": "黃色", "You have the newest version ({v}).": "你已是最新版本 ({v})。", @@ -539,6 +595,7 @@ export const zh_Hant: Catalog = { "bento/spaces {version} · {pages} page(s), {blocks} block(s). The document, the editor and the search are all in this one file.": "bento/spaces {version} · {pages} 個頁面、{blocks} 個區塊。文件、編輯器和搜尋全都在這一個檔案裡。", "format v{v}": "格式 v{v}", "just now": "剛剛", + "most recent": "最新", "none": "無", "you": "你", "you: {name} ✎": "你:{name} ✎", @@ -547,6 +604,8 @@ export const zh_Hant: Catalog = { "{name} joined": "{name} 已加入", "{name} left": "{name} 已離開", "{name} was removed": "已移除 {name}", + "{n} block": "{n} 個區塊", + "{n} blocks": "{n} 個區塊", "{n} connected": "{n} 人已連線", "{n} duplicate or missing id(s) were repaired so links and pages resolve.": "已修復 {n} 個重複或遺失的 id,讓連結與頁面能正確對應。", "{n} embedded image(s) and clip(s) account for {size} of that — {pct}%. Everything else is text.": "其中 {n} 個嵌入的圖片和片段占了 {size},也就是 {pct}%。其餘全是文字。", @@ -558,9 +617,11 @@ export const zh_Hant: Catalog = { "{n} image(s) go with them; the rest stay here.": "{n} 張圖片會一起帶走,其餘留在這裡。", "{n} image(s) point at the web. Nothing loads until a reader asks.": "{n} 張圖片指向網路。在讀者主動要求之前不會載入。", "{n} image(s) were left as paths because embedding stopped there.": "{n} 張圖片以路徑形式保留,因為嵌入到此為止。", + "{n} link to this page": "{n} 個連結指向此頁", "{n} link(s) named pages that were not in that file, and are kept as text.": "有 {n} 個連結指向該檔案中不存在的頁面,已保留為文字。", "{n} link(s) point outside them and are kept as text naming the page they meant.": "有 {n} 個連結指向其外部,將保留為寫明原目標頁面的文字。", "{n} link(s) to it will stop working.": "指向它的 {n} 個連結將會失效。", + "{n} links to this page": "{n} 個連結指向此頁", "{n} note name(s) appear more than once, so links naming them all went to the first.": "有 {n} 個筆記名稱重複,因此指向該名稱的連結都指向了第一個。", "{n} of {total} wikilink(s) resolved.": "{total} 個 wiki 連結中解析了 {n} 個。", "{n} page(s) had frontmatter, kept verbatim in a folded block.": "{n} 個頁面帶有 frontmatter,已原樣保存在摺疊區塊裡。", @@ -569,6 +630,8 @@ export const zh_Hant: Catalog = { "{n} table(s) imported, with their column alignment.": "已匯入 {n} 個表格,並保留欄位對齊方式。", "{n} table(s) kept as text: there is no table block in this format yet.": "{n} 個表格按原文保留為文字:這個格式還沒有表格區塊。", "{n} unresolved comment(s)": "{n} 則未解決的註解", + "{n} word": "{n} 個詞", + "{n} words": "{n} 個詞", "{n}d ago": "{n} 天前", "{n}h ago": "{n} 小時前", "{n}m ago": "{n} 分鐘前", @@ -577,56 +640,4 @@ export const zh_Hant: Catalog = { "{pages} page(s) and {blocks} block(s) will travel.": "將帶走 {pages} 個頁面、{blocks} 個區塊。", "{pages} pages · {links} links": "{pages} 個頁面 · {links} 條連結", "⌘Z removes the imported pages again.": "按 ⌘Z 可以把匯入的頁面再撤銷掉。", - "History": "歷史記錄", - "Versions are kept in this browser only — never in the file, never online. Restoring is undoable.": "版本僅保存在此瀏覽器中,不會寫入檔案,也不會上傳。還原操作可以復原。", - "This space is encrypted, so no versions are kept.": "此空間已加密,因此不保存任何版本。", - "No versions yet — they build up as you write and save.": "尚無版本。隨著你的書寫與儲存,版本會逐漸累積。", - "most recent": "最新", - "That version could not be read": "無法讀取該版本", - "Restored the version from {when} — ⌘Z undoes it": "已還原 {when} 的版本,按 ⌘Z 可復原。", - "Dismiss": "關閉", - "Toggle section": "展開或收合此節", - "{n} block": "{n} 個區塊", - "{n} blocks": "{n} 個區塊", - "{n} word": "{n} 個詞", - "{n} words": "{n} 個詞", - "{n} link to this page": "{n} 個連結指向此頁", - "{n} links to this page": "{n} 個連結指向此頁", - "A new block": "新增一個區塊", - "Find and replace": "尋找與取代", - "Formatting": "格式", - "Getting around": "導覽", - "Indent, or move back out": "縮排,或退回一層", - "Keyboard shortcuts": "鍵盤快速鍵", - "Leave the reading view": "離開閱讀檢視", - "Link the selected words": "為選取的文字加上連結", - "Link to another page": "連結到另一個頁面", - "Search all pages, with nothing selected": "未選取內容時,搜尋所有頁面", - "Show or hide properties": "顯示或隱藏屬性", - "Show or hide the page list": "顯示或隱藏頁面清單", - "The block menu, on an empty line": "在空行上開啟區塊選單", - "The workspace": "工作區", - "This list": "本清單", - "Undo, redo": "復原、重做", - "Writing": "書寫", - "Add": "新增", - "Add a property": "新增屬性", - "Add property…": "新增屬性…", - "Added {name}": "已新增{name}", - "Name": "名稱", - "New property": "新增屬性", - "Choose which pages this view holds": "選擇此檢視包含哪些頁面", - "Every page with a status": "所有帶狀態的頁面", - "Nested under": "巢狀於", - "Pages nested under this one": "巢狀在此頁面下的頁面", - "Pages that have this property": "具有此屬性的頁面", - "Which pages": "哪些頁面", - "Gallery": "圖庫", - "Show as a gallery": "以圖庫顯示", - "Show as a table": "以表格顯示", - "Select": "單選", - "Number": "數字", - "Date": "日期", - "Person": "人員", - "Labels": "標籤", } diff --git a/spaces/src/markdown.ts b/spaces/src/markdown.ts index f9803ef9..eb4262a1 100644 --- a/spaces/src/markdown.ts +++ b/spaces/src/markdown.ts @@ -19,6 +19,7 @@ import { type Block, type Page, uid, writeTable } from './model.ts' import { esc } from './sanitize.ts' import { keepClasses } from './marks.ts' +import { parseEmbedLine, linkEmbeds } from './embed.ts' /** A tab indents four columns. Nothing here depends on the exact number; it * only has to be the same everywhere so nesting is consistent. */ @@ -117,8 +118,13 @@ export function inlineHtml(src: string): string { return hold(ok) }) - // ![[embed]] and [[wikilink|alias]] before ordinary links: an embed of a - // note is just a link to it, because there is no transclusion in the model + // ![[embed]] and [[wikilink|alias]] before ordinary links. + // + // AN INLINE EMBED IS A LINK, and that is now a statement about grammar + // rather than about the model: `embed` is a real block type (embed.ts), and + // parseNote below turns a `![[Note]]` that is a whole LINE into one. A block + // cannot live inside a sentence, so an `![[Note]]` with words either side of + // it stays what it can be here — a link to the note. s = s.replace(/!?\[\[([^\]]+)\]\]/g, (_m, inner: string) => { const [target, alias] = splitOnce(inner, '|') return hold(``) + @@ -405,6 +411,25 @@ export function parseNote(text: string, fileTitle: string): ParsedNote { continue } + // A WHOLE LINE THAT IS AN EMBED BECOMES ONE. Strictly after `imageOf`, + // which owns the image-extension list: `![[diagram.png]]` is a picture and + // `![[Design notes]]` is a transclusion, and the two are told apart in + // exactly one place. + // + // The block leaves here with NO `page` — at parse time a wikilink names a + // file and no page exists yet — carrying the same `#w/` placeholder link + // every other block carries. planImport resolves it and embed.ts + // `linkEmbeds` reads the answer back off the html. + const emb = parseEmbedLine(body) + if (emb) { + para = null + add(mk('embed', { + html: `${esc(emb.target)}`, + ...(emb.anchor ? { anchor: emb.anchor } : {}), + }), ownerFor(indent)) + continue + } + // a plain line: a continuation of the block above, or a new paragraph. // // A SOFT LINE BREAK BECOMES
rather than a space. Notes are written @@ -637,6 +662,13 @@ export function planImport( } } + // …and then the EMBEDS, which read their target back out of the html the + // sweep above just resolved. It has to be after, not folded into the loop: + // an embed's `page` is whatever `#p/` the resolver decided on, and + // deciding it twice in two places is how the block and its own fallback link + // would come to point at different pages. + linkEmbeds(pages) + // NO PAGE ARRIVES WITH ZERO BLOCKS. // // A folder without a folder note, an empty .md, and the invented root all diff --git a/spaces/src/model.ts b/spaces/src/model.ts index 82c6d1b8..4b0648ef 100644 --- a/spaces/src/model.ts +++ b/spaces/src/model.ts @@ -15,6 +15,9 @@ import type { CollabCreds } from './sync/crdt.ts' import { esc, externalHref } from './sanitize.ts' +// A VALUE import, and the cycle it looks like is not one: embed.ts imports +// only TYPES from here, and a type import is erased before anything runs. +import { isPageRef } from './embed.ts' export const FORMAT = 'bento/spaces' export const FORMAT_VERSION = 1 @@ -105,8 +108,26 @@ export interface Block { /** intrinsic px at insert: holds the aspect box while the image decodes */ w?: number h?: number - /** pagelink: the target page id */ + /** + * pagelink, embed: the target page id. + * + * ONE FIELD FOR BOTH, because both mean the identical thing — "this block + * refers to that page" — and every sweep in the app that follows a page + * reference (backlinks, extract, graft, validate, the graph) then extends by + * a name in one condition instead of growing a second concept it could + * forget. embed.ts `isPageRef` is that condition. + */ page?: string + /** + * embed: the heading inside the target page to show, instead of all of it. + * + * Absent = the whole page, which is the common case and therefore the case + * that stores no bytes. Matched by NAME, case- and whitespace-insensitively, + * never by position: a section moved up the page is still that section, and + * an index would silently show the wrong one. A name that matches nothing is + * reported, never quietly widened back to the whole page. + */ + anchor?: string /** * table: the cells, row-major, each one INLINE HTML. @@ -702,8 +723,14 @@ export function buildIndex(doc: SpacesDoc): SpaceIndex { pushInto(backlinks, linkTarget(m[1], page), { pageId: p.id, blockId: b.id }) } } - if (b.type === 'pagelink' && typeof b.page === 'string') { - pushInto(backlinks, b.page, { pageId: p.id, blockId: b.id }) + // AN EMBED IS A REFERENCE, so it backlinks exactly as a pagelink does. + // "Linked from" is how an author finds out who depends on a page before + // rewriting it, and an embed is the strongest dependency in the model — + // the page it names is not merely mentioned, it is being SHOWN + // somewhere else. Leaving embeds out would make the one reference you + // most need to be warned about the one the panel does not list. + if (isPageRef(b)) { + pushInto(backlinks, String(b.page), { pageId: p.id, blockId: b.id }) } } } diff --git a/spaces/src/portable.ts b/spaces/src/portable.ts index fe6a3f8f..72eb0b39 100644 --- a/spaces/src/portable.ts +++ b/spaces/src/portable.ts @@ -19,6 +19,7 @@ // `.ts` extensions ON PURPOSE: node resolves this module directly for the rig. import { type SpacesDoc, type Page, type Block, repairId, pageAssetKeys } from './model.ts' import { esc } from './sanitize.ts' +import { isPageRef } from './embed.ts' /** An `` in a block, however many attributes it carries. */ const PAGE_LINK = /]*?)href="#p\/([^"]*)"([^>]*)>([\s\S]*?)<\/a>/g @@ -185,13 +186,21 @@ export function extractSpace( b.html = r.html unlinked += r.cut } - if (b.type === 'pagelink' && typeof b.page === 'string' && !inSet.has(b.page)) { + if (isPageRef(b) && !inSet.has(String(b.page))) { // a pagelink IS its target; with the target gone there is no block left - // to be, so it becomes the same honest text an inline link becomes + // to be, so it becomes the same honest text an inline link becomes. + // + // AN EMBED IS THE SAME BLOCK-SHAPED REFERENCE and takes the same + // treatment — more urgently, if anything: a pagelink whose target + // stayed behind is a chip that does nothing, while an embed whose + // target stayed behind is a page's worth of CONTENT that silently is + // not in the extract. `anchor` goes with it; a section name is + // meaningless once there is no page to find it on. unlinked++ b.type = 'p' - b.html = literalLink(titleOf.get(b.page) ?? b.page, '') + b.html = literalLink(titleOf.get(String(b.page)) ?? String(b.page), '') delete b.page + delete b.anchor } } } @@ -349,15 +358,21 @@ export function planGraft( dropped += r.cut relinked += r.changed } - if (b.type === 'pagelink' && typeof b.page === 'string') { - if (arrived.has(b.page)) { - const next = idMap.get(b.page) ?? b.page - if (next !== b.page) { b.page = next; relinked++ } + // Both kinds of page reference, by the one predicate — an embed grafted + // into another space that kept pointing at the ORIGINAL space's page id + // would render "that page is not here" in a document where the page + // demonstrably is. + if (isPageRef(b)) { + const ref = String(b.page) + if (arrived.has(ref)) { + const next = idMap.get(ref) ?? ref + if (next !== ref) { b.page = next; relinked++ } } else { dropped++ b.type = 'p' - b.html = literalLink(titleOf.get(b.page) ?? b.page, '') + b.html = literalLink(titleOf.get(ref) ?? ref, '') delete b.page + delete b.anchor } } } diff --git a/spaces/src/render.ts b/spaces/src/render.ts index 8d5c05d3..38bfbe53 100644 --- a/spaces/src/render.ts +++ b/spaces/src/render.ts @@ -23,6 +23,7 @@ import { import { answer, feed, freshContext, type CalcCtx } from './calc.ts' import { ICONS, type IconName } from './icons' import { renderCanvasHead, placeCard } from './canvas.ts' +import { viewEmbed, anchorOf } from './embed.ts' export interface RenderOpts { /** editable per-block hosts (the editor); false for reader/print */ @@ -50,6 +51,17 @@ export interface RenderOpts { * it would travel to the next person the file is mailed to. */ allowRemote?: (src: string) => boolean + /** + * The pages already open above this render, host page first — the embed + * cycle and depth guard (embed.ts `viewEmbed`). + * + * NOT a document field and not module state: it is a fact about one render + * pass, and two surfaces render at once (the editor canvas and the still + * preview), so a shared counter between them would be a race that only shows + * up in a saved thumbnail. `renderBlocks` seeds it with the page it was + * given, so nothing outside this file ever has to pass it. + */ + embedChain?: readonly string[] } // The tag and list maps come from the block registry (blocks.ts), so a new @@ -66,6 +78,10 @@ export interface RenderOpts { */ export function renderBlocks(page: Page, doc: SpacesDoc, opts: RenderOpts = {}): DocumentFragment { const frag = document.createDocumentFragment() + // The embed guard starts here, with the page being drawn, so an embed of the + // page you are ON is a cycle at depth zero. Seeded rather than mutated: the + // caller's opts object is not ours to write to. + if (!opts.embedChain) opts = { ...opts, embedChain: [page.id] } // MAGIC NOTES' CONTEXT, accumulated as the pass goes. A name is defined by a // line and usable by the lines BELOW it — the same direction a person reads // in, and the reason this needs no second pass and cannot cycle. @@ -373,6 +389,11 @@ export function renderBlock(b: Block, doc: SpacesDoc, opts: RenderOpts = {}, cal return el } + case 'embed': { + renderEmbed(el, b, doc, opts) + return el + } + case 'link': { // A CARD DRAWN ENTIRELY FROM THE FILE. // @@ -658,6 +679,73 @@ function renderTable(b: Block, opts: RenderOpts): HTMLElement { return wrap } +/** + * An embed: the source page's own blocks, drawn here. + * + * THREE THINGS THIS DOES THAT ARE NOT DECORATION. + * + * It is ATTRIBUTED and clickable. A block of someone else's page dropped into + * yours with no seam is a lie about where the words live, and the reader who + * wants to fix a typo has nowhere to go. The header names the source page and + * links to it, and the section when there is one. + * + * It is NEVER EDITABLE, whatever the host surface is. The blocks belong to + * another page; an editable host here would write a keystroke into `html` on a + * block the editor is not showing, and the change would appear to happen + * nowhere. `editable: false` on the nested render is the whole of that. + * + * And it carries NO `data-block-id`. The editor's paint sweeps every + * `[data-block-id]` under the page and hangs a drag gutter, a checkbox + * handler, a language chip on each — all keyed to `store.block(id)`, which + * resolves ANY id in the document. A checkbox ticked inside an embed would + * have committed to the source page from a surface that was not showing it. + * Stripping the hook is one line and closes the whole class. + */ +function renderEmbed(el: HTMLElement, b: Block, doc: SpacesDoc, opts: RenderOpts): void { + const box = document.createElement('div') + box.className = 'sp-embed' + const view = viewEmbed(b, doc, opts.embedChain ?? []) + + const head = document.createElement(view.page ? 'a' : 'div') + head.className = 'sp-embed-src' + const anchor = anchorOf(b) + if (view.page) { + ;(head as HTMLAnchorElement).href = `#p/${view.page.id}` + head.textContent = anchor ? `${view.page.title} › ${anchor}` : view.page.title + head.setAttribute('aria-label', t('Embedded from {page}', { page: view.page.title })) + } else { + head.textContent = anchor ? `[[?#${anchor}]]` : '[[?]]' + head.classList.add('sp-dead') + } + box.appendChild(head) + + if (!view.ok) { + const note = document.createElement('p') + note.className = 'sp-embed-note' + note.textContent = + view.why === 'cycle' ? t('This embed is inside itself — the loop stops here.') + : view.why === 'depth' ? t('Embeds are not followed deeper than this.') + : view.why === 'no-section' ? t('No section named {name} on this page.', { name: view.anchor ?? '' }) + : t('This embed points at a page that is not here.') + box.appendChild(note) + el.appendChild(box) + return + } + + const body = document.createElement('div') + body.className = 'sp-embed-body' + body.appendChild(renderBlocks({ ...view.page, blocks: view.blocks }, doc, { + ...opts, + editable: false, + embedChain: [...(opts.embedChain ?? []), view.page.id], + })) + for (const node of body.querySelectorAll('[data-block-id]')) { + delete node.dataset.blockId + } + box.appendChild(body) + el.appendChild(box) +} + /** * A callout tone's name, in the reader's language. * diff --git a/spaces/src/styles.css b/spaces/src/styles.css index ce524af5..179c94a2 100644 --- a/spaces/src/styles.css +++ b/spaces/src/styles.css @@ -753,6 +753,33 @@ button.sp-callout-chip:hover { text-decoration: underline; text-underline-offset .sp-pagecard:hover { background: var(--chrome); } .sp-pagecard.sp-dead { color: var(--muted); font-style: italic; font-weight: 400; } +/* ————— embeds (transclusion) ————— + A QUOTATION, DRAWN AS ONE. The rule down the left edge and the inset are the + blockquote's, not the page card's: what is inside came from somewhere else + and is being shown here, which is exactly what a quote mark has always meant. + The card border was tried and read as a widget — a thing to press rather than + a passage to read. + + The source line is the ATTRIBUTION and sits above the content, small and + quiet, because it is a citation and not a heading. It is also the way back to + the page, so it keeps a hover state; a citation nobody can click is how a + reader ends up searching the sidebar for the page they are already looking at. */ +.sp-b-embed { margin: 10px 0; } +.sp-embed { border-inline-start: 3px solid var(--line); padding-inline-start: 14px; } +.sp-embed-src { + display: block; font-size: 12px; color: var(--muted); + text-decoration: none; margin-bottom: 4px; +} +a.sp-embed-src:hover { color: var(--ink); text-decoration: underline; } +.sp-embed-src.sp-dead { font-style: italic; } +/* The nested page's own blocks keep their spacing, minus the first and last + margins — an embed that opens with a blank line looks like a rendering bug. */ +.sp-embed-body > :first-child { margin-block-start: 0; } +.sp-embed-body > :last-child { margin-block-end: 0; } +/* A NAMED PLACEHOLDER, never a blank: a cycle, a depth stop and a missing page + each say which one they are, in the reader's language. */ +.sp-embed-note { color: var(--muted); font-style: italic; font-size: 13px; margin: 0; } + /* ————— link cards (outward) ————— Deliberately the page card's sibling: same border, radius and hover, so the two read as one family and the difference a reader sees is the one that