mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
feat(notices): add the 4.0.0 release modal
The thank-you modal was one column of markdown for every notice. A release needs more room than that: what shipped, and why it took what it took. Adds a `release` block to the notice contract. A notice carrying it renders through ReleaseNoticeModal instead of the generic body — the release on the left, the note from the maintainer on the right, both halves driven entirely by translation keys so the next release only edits the registry entry. Sizing lives on one CSS variable (--rn-s), so the panel scales as a piece and the design's proportions hold at every breakpoint. The thank-you notice gets maxVersion 4.0.0: from there on the release modal carries the same thank-you and the same two support links, and installs still on 3.x keep the old one. All 23 locales.
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
// FE-RN-001 to FE-RN-011
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { render, screen } from '../../../tests/helpers/render'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { ReleaseNoticeModal } from './ReleaseNoticeModal'
|
||||
import type { SystemNoticeDTO } from '../../store/systemNoticeStore'
|
||||
|
||||
/** A notice shaped like the 4.0.0 registry entry, with the pieces a test needs to vary. */
|
||||
function releaseNotice(overrides: Partial<SystemNoticeDTO> = {}): SystemNoticeDTO {
|
||||
return {
|
||||
id: 'release-4-0-0',
|
||||
display: 'modal',
|
||||
severity: 'info',
|
||||
titleKey: 'rel.headline',
|
||||
bodyKey: 'rel.intro',
|
||||
dismissible: true,
|
||||
desktopOnly: true,
|
||||
cta: { kind: 'link', labelKey: 'rel.bmc', href: 'https://buymeacoffee.com/mauriceboe' },
|
||||
secondaryCta: { kind: 'link', labelKey: 'rel.kofi', href: 'https://ko-fi.com/mauriceboe' },
|
||||
release: {
|
||||
version: '4.0.0',
|
||||
eyebrowKey: 'rel.eyebrow',
|
||||
tagKey: 'rel.tag',
|
||||
headlineKey: 'rel.headline',
|
||||
introKey: 'rel.intro',
|
||||
features: [
|
||||
{ iconName: 'Smartphone', titleKey: 'rel.f1.title', bodyKey: 'rel.f1.body' },
|
||||
{ iconName: 'BookOpen', titleKey: 'rel.f2.title', bodyKey: 'rel.f2.body', badgeKey: 'rel.f2.badge' },
|
||||
],
|
||||
note: {
|
||||
eyebrowKey: 'rel.note.eyebrow',
|
||||
titleKey: 'rel.note.title',
|
||||
bodyKey: 'rel.note.body',
|
||||
promiseLabelKey: 'rel.promise.label',
|
||||
promiseTextKey: 'rel.promise.text',
|
||||
bodyAfterKey: 'rel.note.after',
|
||||
closingKey: 'rel.note.closing',
|
||||
signatureKey: 'rel.note.signature',
|
||||
},
|
||||
supportTextKey: 'rel.support',
|
||||
},
|
||||
...overrides,
|
||||
} as SystemNoticeDTO
|
||||
}
|
||||
|
||||
function renderModal(notice = releaseNotice(), handlers: Partial<{
|
||||
onDismiss: () => void; onCTA: () => void; onSecondaryCTA: () => void
|
||||
}> = {}) {
|
||||
const onDismiss = handlers.onDismiss ?? vi.fn()
|
||||
const onCTA = handlers.onCTA ?? vi.fn()
|
||||
const onSecondaryCTA = handlers.onSecondaryCTA ?? vi.fn()
|
||||
const view = render(
|
||||
<ReleaseNoticeModal
|
||||
notice={notice}
|
||||
visible
|
||||
onDismiss={onDismiss}
|
||||
onCTA={onCTA}
|
||||
onSecondaryCTA={onSecondaryCTA}
|
||||
/>
|
||||
)
|
||||
return { ...view, onDismiss, onCTA, onSecondaryCTA }
|
||||
}
|
||||
|
||||
describe('ReleaseNoticeModal', () => {
|
||||
it('FE-RN-001: renders nothing for a notice without a release block', () => {
|
||||
const { container } = renderModal(releaseNotice({ release: undefined }))
|
||||
expect(container.querySelector('.rn-overlay')).toBeNull()
|
||||
})
|
||||
|
||||
it('FE-RN-002: shows the version as a plain figure, not a translation key', () => {
|
||||
renderModal()
|
||||
expect(screen.getByText('4.0.0')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('FE-RN-003: renders one row per feature, with the badge only where declared', () => {
|
||||
renderModal()
|
||||
expect(screen.getByText('rel.f1.title')).toBeInTheDocument()
|
||||
expect(screen.getByText('rel.f2.badge')).toBeInTheDocument()
|
||||
expect(document.querySelectorAll('.rn-feature')).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('FE-RN-004: splits the note body on the blank line into separate paragraphs', () => {
|
||||
const n = releaseNotice()
|
||||
// The translation stub echoes the key, so drive the split through a real value.
|
||||
n.release!.note.bodyKey = 'first paragraph\n\nsecond paragraph'
|
||||
renderModal(n)
|
||||
expect(screen.getByText('first paragraph')).toBeInTheDocument()
|
||||
expect(screen.getByText('second paragraph')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('FE-RN-005: omits the stats row when the release carries no stats', () => {
|
||||
renderModal()
|
||||
expect(document.querySelector('.rn-release-foot')).toBeNull()
|
||||
})
|
||||
|
||||
it('FE-RN-006: shows the stats row and the notes link when both are present', () => {
|
||||
const n = releaseNotice()
|
||||
n.release!.stats = [{ value: '~150', labelKey: 'rel.stat.bugs' }]
|
||||
n.release!.notes = { labelKey: 'rel.notes', href: 'https://example.test/notes' }
|
||||
renderModal(n)
|
||||
expect(screen.getByText('~150')).toBeInTheDocument()
|
||||
const link = screen.getByRole('link', { name: /rel\.notes/ })
|
||||
expect(link).toHaveAttribute('href', 'https://example.test/notes')
|
||||
expect(link).toHaveAttribute('target', '_blank')
|
||||
expect(link).toHaveAttribute('rel', 'noopener noreferrer')
|
||||
})
|
||||
|
||||
it('FE-RN-007: dismisses from the close button and from the backdrop', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onDismiss = vi.fn()
|
||||
renderModal(releaseNotice(), { onDismiss })
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'Close' }))
|
||||
expect(onDismiss).toHaveBeenCalledTimes(1)
|
||||
|
||||
await user.click(document.querySelector('.rn-overlay')!)
|
||||
expect(onDismiss).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('FE-RN-008: a click inside the panel does not dismiss', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onDismiss = vi.fn()
|
||||
renderModal(releaseNotice(), { onDismiss })
|
||||
await user.click(screen.getByText('rel.note.title'))
|
||||
expect(onDismiss).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('FE-RN-009: a non-dismissible notice has no close button and an inert backdrop', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onDismiss = vi.fn()
|
||||
renderModal(releaseNotice({ dismissible: false }), { onDismiss })
|
||||
expect(screen.queryByRole('button', { name: 'Close' })).toBeNull()
|
||||
await user.click(document.querySelector('.rn-overlay')!)
|
||||
expect(onDismiss).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('FE-RN-010: wires each support button to its own handler', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onCTA = vi.fn()
|
||||
const onSecondaryCTA = vi.fn()
|
||||
renderModal(releaseNotice(), { onCTA, onSecondaryCTA })
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /rel\.bmc/ }))
|
||||
expect(onCTA).toHaveBeenCalledTimes(1)
|
||||
expect(onSecondaryCTA).not.toHaveBeenCalled()
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /rel\.kofi/ }))
|
||||
expect(onSecondaryCTA).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('FE-RN-011: labels the dialog with the headline and describes it with the intro', () => {
|
||||
renderModal()
|
||||
const dialog = screen.getByRole('dialog')
|
||||
expect(dialog).toHaveAttribute('aria-modal', 'true')
|
||||
expect(dialog).toHaveAttribute('aria-labelledby', 'notice-title-release-4-0-0')
|
||||
expect(dialog).toHaveAttribute('aria-describedby', 'notice-body-release-4-0-0')
|
||||
expect(document.getElementById('notice-title-release-4-0-0')).toHaveTextContent('rel.headline')
|
||||
expect(document.getElementById('notice-body-release-4-0-0')).toHaveTextContent('rel.intro')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,189 @@
|
||||
import React from 'react';
|
||||
import * as LucideIcons from 'lucide-react';
|
||||
import { ArrowRight, Coffee, Heart, Infinity as InfinityIcon, Sparkles, X } from 'lucide-react';
|
||||
import { useTranslation } from '../../i18n/TranslationContext.js';
|
||||
import type { SystemNoticeDTO } from '../../store/systemNoticeStore.js';
|
||||
import './releaseNotice.css';
|
||||
|
||||
interface Props {
|
||||
notice: SystemNoticeDTO;
|
||||
visible: boolean;
|
||||
onDismiss: () => void;
|
||||
onCTA: () => void;
|
||||
onSecondaryCTA: () => void;
|
||||
}
|
||||
|
||||
/** Splits a translated block into paragraphs the way the notice bodies are written. */
|
||||
function paragraphs(text: string): string[] {
|
||||
return text.split('\n\n').map(p => p.trim()).filter(Boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* The release modal: what shipped on the left, a note from the maintainer on
|
||||
* the right. Rendered instead of the generic notice body whenever a notice
|
||||
* carries `release` — every string comes from that block, so a later release
|
||||
* only edits the registry entry.
|
||||
*
|
||||
* Desktop only (the notices carrying it set `desktopOnly`), and it keeps the
|
||||
* generic modal's behaviour: the host hook still owns ESC, the scroll lock and
|
||||
* the dismissal, this component only draws.
|
||||
*/
|
||||
export function ReleaseNoticeModal({ notice, visible, onDismiss, onCTA, onSecondaryCTA }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const release = notice.release;
|
||||
if (!release) return null;
|
||||
|
||||
const titleId = `notice-title-${notice.id}`;
|
||||
const bodyId = `notice-body-${notice.id}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="rn-overlay"
|
||||
role="presentation"
|
||||
style={{ opacity: visible ? 1 : 0, transition: 'opacity 260ms ease' }}
|
||||
onClick={notice.dismissible ? onDismiss : undefined}
|
||||
>
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby={titleId}
|
||||
aria-describedby={bodyId}
|
||||
className="rn-panel"
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
{/* ── Left: the release ─────────────────────────────────────────── */}
|
||||
<div className="rn-release">
|
||||
<div className="rn-release-grain" aria-hidden="true" />
|
||||
|
||||
<div className="rn-release-inner">
|
||||
<div className="rn-eyebrow-row">
|
||||
<span className="rn-mark">
|
||||
<img src="/icons/icon-white.svg" alt="" aria-hidden="true" />
|
||||
</span>
|
||||
<span className="rn-eyebrow">{t(release.eyebrowKey)}</span>
|
||||
</div>
|
||||
|
||||
<div className="rn-version-row">
|
||||
<div className="rn-version">{release.version}</div>
|
||||
<div className="rn-tag">{t(release.tagKey)}</div>
|
||||
</div>
|
||||
|
||||
<h2 id={titleId} className="rn-headline">{t(release.headlineKey)}</h2>
|
||||
<p id={bodyId} className="rn-intro">{t(release.introKey)}</p>
|
||||
|
||||
<div className="rn-features">
|
||||
{release.features.map(f => {
|
||||
const Icon: React.ElementType =
|
||||
((LucideIcons as Record<string, unknown>)[f.iconName] as React.ElementType) ?? Sparkles;
|
||||
return (
|
||||
<div key={f.titleKey} className="rn-feature">
|
||||
<span className="rn-feature-icon">
|
||||
<Icon size={18} strokeWidth={1.9} aria-hidden="true" />
|
||||
</span>
|
||||
<div className="rn-feature-text">
|
||||
<div className="rn-feature-title">
|
||||
{t(f.titleKey)}
|
||||
{f.badgeKey && <span className="rn-badge">{t(f.badgeKey)}</span>}
|
||||
</div>
|
||||
<div className="rn-feature-body">{t(f.bodyKey)}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{((release.stats?.length ?? 0) > 0 || release.notes) && (
|
||||
<div className="rn-release-foot">
|
||||
{release.stats && release.stats.length > 0 && (
|
||||
<div className="rn-stats">
|
||||
{release.stats.map(s => (
|
||||
<div key={s.labelKey}>
|
||||
<div className="rn-stat-value">{s.value}</div>
|
||||
<div className="rn-stat-label">{t(s.labelKey)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{release.notes && (
|
||||
<a
|
||||
className="rn-notes"
|
||||
href={release.notes.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{t(release.notes.labelKey)}
|
||||
<span className="rn-notes-arrow">
|
||||
<ArrowRight size={16} strokeWidth={2.4} aria-hidden="true" />
|
||||
</span>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{release.footnoteKey && (
|
||||
<div className="rn-footnote">{t(release.footnoteKey)}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Right: the note ───────────────────────────────────────────── */}
|
||||
<div className="rn-note">
|
||||
{notice.dismissible && (
|
||||
<button className="rn-close" onClick={onDismiss} aria-label={t('common.close')}>
|
||||
<X size={18} strokeWidth={2} />
|
||||
</button>
|
||||
)}
|
||||
|
||||
<div className="rn-note-body">
|
||||
<div className="rn-note-eyebrow">{t(release.note.eyebrowKey)}</div>
|
||||
<h3 className="rn-note-title">{t(release.note.titleKey)}</h3>
|
||||
|
||||
{paragraphs(t(release.note.bodyKey)).map((p, i) => <p key={i}>{p}</p>)}
|
||||
|
||||
<div className="rn-promise">
|
||||
<div className="rn-promise-label">
|
||||
<InfinityIcon size={14} strokeWidth={2.1} aria-hidden="true" />
|
||||
{t(release.note.promiseLabelKey)}
|
||||
</div>
|
||||
<div className="rn-promise-text">{t(release.note.promiseTextKey)}</div>
|
||||
</div>
|
||||
|
||||
{paragraphs(t(release.note.bodyAfterKey)).map((p, i) => <p key={i}>{p}</p>)}
|
||||
|
||||
<div className="rn-signoff">
|
||||
<div className="rn-closing">{t(release.note.closingKey)}</div>
|
||||
<div className="rn-signature">{t(release.note.signatureKey)}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rn-support">
|
||||
<div className="rn-support-text">{t(release.supportTextKey)}</div>
|
||||
<div className="rn-support-buttons">
|
||||
{notice.cta && (
|
||||
<button
|
||||
id={`notice-cta-${notice.id}`}
|
||||
className="rn-support-btn rn-support-bmc"
|
||||
onClick={onCTA}
|
||||
>
|
||||
<Coffee size={18} strokeWidth={2.1} aria-hidden="true" />
|
||||
{t(notice.cta.labelKey)}
|
||||
</button>
|
||||
)}
|
||||
{notice.secondaryCta && (
|
||||
<button
|
||||
id={`notice-cta2-${notice.id}`}
|
||||
className="rn-support-btn rn-support-kofi"
|
||||
onClick={onSecondaryCTA}
|
||||
>
|
||||
<Heart size={18} strokeWidth={2.1} aria-hidden="true" />
|
||||
{t(notice.secondaryCta.labelKey)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { useSystemNoticeStore } from '../../store/systemNoticeStore.js';
|
||||
import type { SystemNoticeDTO } from '../../store/systemNoticeStore.js';
|
||||
import { useTranslation, isRtlLanguage } from '../../i18n/index.js';
|
||||
import { runNoticeAction } from './noticeActions.js';
|
||||
import { ReleaseNoticeModal } from './ReleaseNoticeModal.js';
|
||||
import { lockBodyScroll } from '../../utils/bodyScrollLock.js';
|
||||
|
||||
const ReactMarkdown = React.lazy(() =>
|
||||
@@ -864,5 +865,18 @@ export function ModalRenderer({ notices }: Props) {
|
||||
const S = useSystemNoticeModal(notices);
|
||||
// No notice to show
|
||||
if (!S.notice) return null;
|
||||
// A notice carrying `release` draws its own two-column panel, but keeps this
|
||||
// hook's ESC handling, scroll lock and dismissal.
|
||||
if (S.notice.release && !S.isMobile) {
|
||||
return (
|
||||
<ReleaseNoticeModal
|
||||
notice={S.notice}
|
||||
visible={S.visible}
|
||||
onDismiss={S.handleDismissAll}
|
||||
onCTA={S.handleCTA}
|
||||
onSecondaryCTA={S.handleSecondaryCTA}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return S.isMobile ? <MobileNoticeSheet {...S} /> : <DesktopNoticeModal {...S} />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
* The release notice modal — a two-column panel: the release on the left,
|
||||
* a note from the maintainer on the right.
|
||||
*
|
||||
* The measurements here are the design's own, kept in one stylesheet rather
|
||||
* than spread across inline styles (the same arrangement mobile.css uses for
|
||||
* the mobile shell). Every one of them is multiplied by --rn-s, so the whole
|
||||
* panel scales as one piece and the proportions never drift: change that one
|
||||
* number to resize the modal.
|
||||
*
|
||||
* The left column is deliberately dark in both themes: it is a brand panel,
|
||||
* not a surface, so it does not read the surface tokens. Everything on the
|
||||
* right does.
|
||||
*/
|
||||
|
||||
.rn-overlay {
|
||||
/* Design is drawn at 1420px wide; anything below full size is this factor. */
|
||||
--rn-s: 0.72;
|
||||
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 50;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 28px;
|
||||
background: oklch(0.12 0.01 60 / 0.52);
|
||||
backdrop-filter: blur(14px) saturate(1.1);
|
||||
-webkit-backdrop-filter: blur(14px) saturate(1.1);
|
||||
animation: rn-fade 0.35s ease both;
|
||||
}
|
||||
|
||||
/* Roomy screens can carry a little more of the design's own scale. */
|
||||
@media (min-width: 1900px) and (min-height: 1150px) {
|
||||
.rn-overlay { --rn-s: 0.86; }
|
||||
}
|
||||
|
||||
@media (max-width: 1280px), (max-height: 760px) {
|
||||
.rn-overlay { --rn-s: 0.64; padding: 16px; }
|
||||
}
|
||||
|
||||
@keyframes rn-fade {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes rn-rise {
|
||||
from { opacity: 0; transform: translateY(16px) scale(0.985); }
|
||||
to { opacity: 1; transform: none; }
|
||||
}
|
||||
|
||||
.rn-panel {
|
||||
width: calc(1420px * var(--rn-s));
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 1.3fr 1fr;
|
||||
border-radius: calc(34px * var(--rn-s));
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 12px oklch(0 0 0 / 0.2), 0 60px 130px -40px oklch(0 0 0 / 0.7);
|
||||
border: 1px solid oklch(1 0 0 / 0.1);
|
||||
animation: rn-rise 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
font-family: var(--font-system);
|
||||
}
|
||||
|
||||
/* Below the two-column breakpoint the halves stack and the panel scrolls. */
|
||||
@media (max-width: 1080px) {
|
||||
.rn-panel { grid-template-columns: 1fr; overflow-y: auto; }
|
||||
}
|
||||
|
||||
[data-reduce-motion='true'] .rn-overlay,
|
||||
[data-reduce-motion='true'] .rn-panel {
|
||||
animation-duration: 0.01ms;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.rn-overlay, .rn-panel { animation-duration: 0.01ms; }
|
||||
}
|
||||
|
||||
/* ── Left: the release ─────────────────────────────────────────────────── */
|
||||
|
||||
.rn-release {
|
||||
position: relative;
|
||||
background: radial-gradient(120% 95% at 18% 0%, #1b1b22 0%, #0e0e12 52%, #08080a 100%);
|
||||
color: #f5f5f7;
|
||||
padding: calc(50px * var(--rn-s)) calc(54px * var(--rn-s)) calc(38px * var(--rn-s));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* The dot field, faded out towards the lower right. */
|
||||
.rn-release-grain {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: radial-gradient(circle, oklch(1 0 0 / 0.1) 1px, transparent 1.3px);
|
||||
background-size: 24px 24px;
|
||||
-webkit-mask-image: radial-gradient(90% 70% at 25% 12%, #000 0%, transparent 78%);
|
||||
mask-image: radial-gradient(90% 70% at 25% 12%, #000 0%, transparent 78%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.rn-release-inner {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.rn-eyebrow-row { display: flex; align-items: center; gap: calc(11px * var(--rn-s)); }
|
||||
|
||||
.rn-mark {
|
||||
width: calc(32px * var(--rn-s));
|
||||
height: calc(32px * var(--rn-s));
|
||||
border-radius: calc(10px * var(--rn-s));
|
||||
background: oklch(1 0 0 / 0.1);
|
||||
border: 1px solid oklch(1 0 0 / 0.16);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.rn-mark img {
|
||||
width: calc(19px * var(--rn-s));
|
||||
height: calc(19px * var(--rn-s));
|
||||
display: block;
|
||||
}
|
||||
|
||||
.rn-eyebrow {
|
||||
font-family: var(--font-subtext);
|
||||
font-size: calc(11.5px * var(--rn-s));
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: oklch(1 0 0 / 0.5);
|
||||
}
|
||||
|
||||
.rn-version-row {
|
||||
margin-top: calc(30px * var(--rn-s));
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: calc(20px * var(--rn-s));
|
||||
}
|
||||
|
||||
.rn-version {
|
||||
font-family: var(--font-subtext);
|
||||
font-size: calc(112px * var(--rn-s));
|
||||
font-weight: 600;
|
||||
line-height: 0.82;
|
||||
letter-spacing: -0.055em;
|
||||
color: #fbfbfd;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.rn-tag {
|
||||
padding-bottom: calc(11px * var(--rn-s));
|
||||
font-size: calc(12.5px * var(--rn-s));
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
color: oklch(0.84 0.18 85);
|
||||
}
|
||||
|
||||
.rn-headline {
|
||||
word-break: auto-phrase;
|
||||
margin: calc(24px * var(--rn-s)) 0 0;
|
||||
font-size: calc(30px * var(--rn-s));
|
||||
font-weight: 600;
|
||||
line-height: 1.22;
|
||||
letter-spacing: -0.03em;
|
||||
max-width: calc(600px * var(--rn-s));
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
.rn-intro {
|
||||
margin: calc(13px * var(--rn-s)) 0 0;
|
||||
max-width: calc(620px * var(--rn-s));
|
||||
font-size: calc(15.5px * var(--rn-s));
|
||||
line-height: 1.62;
|
||||
color: oklch(1 0 0 / 0.62);
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
.rn-features {
|
||||
margin-top: calc(30px * var(--rn-s));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.rn-feature {
|
||||
display: flex;
|
||||
gap: calc(18px * var(--rn-s));
|
||||
padding: calc(16px * var(--rn-s)) 0;
|
||||
border-top: 1px solid oklch(1 0 0 / 0.09);
|
||||
}
|
||||
|
||||
.rn-feature:last-child { border-bottom: 1px solid oklch(1 0 0 / 0.09); }
|
||||
|
||||
.rn-feature-icon {
|
||||
width: calc(38px * var(--rn-s));
|
||||
height: calc(38px * var(--rn-s));
|
||||
flex: none;
|
||||
border-radius: calc(12px * var(--rn-s));
|
||||
background: oklch(1 0 0 / 0.07);
|
||||
border: 1px solid oklch(1 0 0 / 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: oklch(0.9 0.16 88);
|
||||
}
|
||||
|
||||
.rn-feature-text { min-width: 0; }
|
||||
|
||||
.rn-feature-title {
|
||||
font-size: calc(16.5px * var(--rn-s));
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.rn-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-left: calc(4px * var(--rn-s));
|
||||
padding: calc(3px * var(--rn-s)) calc(8px * var(--rn-s));
|
||||
border-radius: calc(6px * var(--rn-s));
|
||||
background: oklch(0.84 0.18 85 / 0.16);
|
||||
border: 1px solid oklch(0.84 0.18 85 / 0.4);
|
||||
font-family: var(--font-subtext);
|
||||
font-size: calc(9.5px * var(--rn-s));
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
color: oklch(0.9 0.16 88);
|
||||
vertical-align: 2px;
|
||||
}
|
||||
|
||||
.rn-feature-body {
|
||||
margin-top: calc(5px * var(--rn-s));
|
||||
font-size: calc(14.5px * var(--rn-s));
|
||||
line-height: 1.55;
|
||||
color: oklch(1 0 0 / 0.58);
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
.rn-release-foot {
|
||||
margin-top: auto;
|
||||
padding-top: calc(28px * var(--rn-s));
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: calc(24px * var(--rn-s));
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.rn-stats { display: flex; gap: calc(34px * var(--rn-s)); }
|
||||
|
||||
.rn-stat-value {
|
||||
font-family: var(--font-subtext);
|
||||
font-size: calc(20px * var(--rn-s));
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.02em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.rn-stat-label {
|
||||
margin-top: calc(3px * var(--rn-s));
|
||||
font-size: calc(11px * var(--rn-s));
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
color: oklch(1 0 0 / 0.42);
|
||||
}
|
||||
|
||||
.rn-notes {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: calc(12px * var(--rn-s));
|
||||
height: calc(46px * var(--rn-s));
|
||||
padding: 0 calc(8px * var(--rn-s)) 0 calc(20px * var(--rn-s));
|
||||
border-radius: 999px;
|
||||
background: oklch(1 0 0 / 0.07);
|
||||
border: 1px solid oklch(1 0 0 / 0.14);
|
||||
color: #f5f5f7;
|
||||
font-size: calc(14px * var(--rn-s));
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
box-shadow: inset 0 1px 0 oklch(1 0 0 / 0.1);
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
.rn-notes:hover { background: oklch(1 0 0 / 0.12); color: #f5f5f7; }
|
||||
|
||||
.rn-notes-arrow {
|
||||
width: calc(30px * var(--rn-s));
|
||||
height: calc(30px * var(--rn-s));
|
||||
border-radius: 50%;
|
||||
background: oklch(0.9 0.16 88);
|
||||
color: #101013;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.rn-footnote {
|
||||
margin-top: calc(22px * var(--rn-s));
|
||||
padding-top: calc(18px * var(--rn-s));
|
||||
font-size: calc(14px * var(--rn-s));
|
||||
line-height: 1.6;
|
||||
color: oklch(1 0 0 / 0.5);
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
/*
|
||||
* Its own rule so the footnote only draws a divider when something sits between
|
||||
* it and the feature list. Without the stats row the list's closing border is
|
||||
* already right above it, and two lines land on top of each other.
|
||||
*/
|
||||
.rn-release-foot + .rn-footnote {
|
||||
border-top: 1px solid oklch(1 0 0 / 0.09);
|
||||
}
|
||||
|
||||
/* ── Right: the note ───────────────────────────────────────────────────── */
|
||||
|
||||
.rn-note {
|
||||
position: relative;
|
||||
background: var(--bg-card);
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.rn-close {
|
||||
position: absolute;
|
||||
top: calc(24px * var(--rn-s));
|
||||
right: calc(24px * var(--rn-s));
|
||||
width: calc(38px * var(--rn-s));
|
||||
height: calc(38px * var(--rn-s));
|
||||
border-radius: 50%;
|
||||
background: var(--bg-hover);
|
||||
border: 1px solid var(--border-faint);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-secondary);
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s ease, color 0.15s ease;
|
||||
}
|
||||
|
||||
.rn-close:hover { background: var(--bg-selected); color: var(--text-primary); }
|
||||
|
||||
.rn-note-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: calc(50px * var(--rn-s)) calc(52px * var(--rn-s)) calc(30px * var(--rn-s));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.rn-note-eyebrow {
|
||||
font-family: var(--font-subtext);
|
||||
font-size: calc(11.5px * var(--rn-s));
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.rn-note-title {
|
||||
/* CJK breaks anywhere by default; break at phrase boundaries where supported. */
|
||||
word-break: auto-phrase;
|
||||
margin: calc(16px * var(--rn-s)) 0 0;
|
||||
font-size: calc(29px * var(--rn-s));
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.03em;
|
||||
max-width: calc(420px * var(--rn-s));
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
.rn-note p {
|
||||
margin: calc(20px * var(--rn-s)) 0 0;
|
||||
font-size: calc(15px * var(--rn-s));
|
||||
line-height: 1.68;
|
||||
color: var(--text-secondary);
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
.rn-note p + p { margin-top: calc(15px * var(--rn-s)); }
|
||||
|
||||
.rn-promise {
|
||||
margin-top: calc(24px * var(--rn-s));
|
||||
padding: calc(20px * var(--rn-s)) calc(22px * var(--rn-s));
|
||||
border-radius: calc(20px * var(--rn-s));
|
||||
background: oklch(0.72 0.11 190 / 0.1);
|
||||
border: 1px solid oklch(0.72 0.11 190 / 0.28);
|
||||
}
|
||||
|
||||
.rn-promise-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: calc(9px * var(--rn-s));
|
||||
font-family: var(--font-subtext);
|
||||
font-size: calc(11px * var(--rn-s));
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
color: oklch(0.55 0.09 190);
|
||||
}
|
||||
|
||||
.dark .rn-promise-label { color: oklch(0.78 0.1 190); }
|
||||
|
||||
.rn-promise-text {
|
||||
margin-top: calc(10px * var(--rn-s));
|
||||
font-size: calc(15.5px * var(--rn-s));
|
||||
font-weight: 600;
|
||||
line-height: 1.55;
|
||||
letter-spacing: -0.01em;
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
.rn-signoff { margin-top: auto; padding-top: calc(26px * var(--rn-s)); }
|
||||
|
||||
.rn-closing {
|
||||
font-size: calc(18px * var(--rn-s));
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.rn-signature {
|
||||
margin-top: calc(8px * var(--rn-s));
|
||||
font-size: calc(15px * var(--rn-s));
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.rn-support {
|
||||
padding: calc(26px * var(--rn-s)) calc(52px * var(--rn-s)) calc(34px * var(--rn-s));
|
||||
border-top: 1px solid var(--border-primary);
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.rn-support-text {
|
||||
font-size: calc(15px * var(--rn-s));
|
||||
line-height: 1.68;
|
||||
color: var(--text-secondary);
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
.rn-support-buttons {
|
||||
margin-top: calc(16px * var(--rn-s));
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: calc(10px * var(--rn-s));
|
||||
}
|
||||
|
||||
.rn-support-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: calc(9px * var(--rn-s));
|
||||
height: calc(50px * var(--rn-s));
|
||||
border-radius: calc(16px * var(--rn-s));
|
||||
font-size: calc(14.5px * var(--rn-s));
|
||||
font-weight: 600;
|
||||
box-shadow: 0 2px 6px oklch(0 0 0 / 0.1);
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
transition: filter 0.15s ease;
|
||||
}
|
||||
|
||||
.rn-support-btn:hover { filter: brightness(0.96); }
|
||||
|
||||
/* Brand colours — theme-lint-disable: Buy Me a Coffee and Ko-fi own these. */
|
||||
.rn-support-bmc { background: #ffdd00; color: #101013; }
|
||||
.rn-support-kofi { background: #ff5e5b; color: #ffffff; }
|
||||
|
||||
/* Narrow viewports: keep the proportions, drop the outer padding. */
|
||||
@media (max-width: 900px) {
|
||||
.rn-overlay { padding: 0; }
|
||||
.rn-panel { border-radius: 0; border: 0; max-height: 100%; }
|
||||
}
|
||||
|
||||
/* Icons carry fixed size attributes from lucide; scale them with the panel. */
|
||||
.rn-feature-icon svg { width: calc(18px * var(--rn-s)); height: calc(18px * var(--rn-s)); }
|
||||
.rn-notes-arrow svg { width: calc(16px * var(--rn-s)); height: calc(16px * var(--rn-s)); }
|
||||
.rn-promise-label svg { width: calc(14px * var(--rn-s)); height: calc(14px * var(--rn-s)); flex: none; }
|
||||
.rn-support-btn svg { width: calc(18px * var(--rn-s)); height: calc(18px * var(--rn-s)); flex: none; }
|
||||
.rn-close svg { width: calc(18px * var(--rn-s)); height: calc(18px * var(--rn-s)); }
|
||||
@@ -34,6 +34,81 @@ export const RETIRED_NOTICE_IDS = [
|
||||
] as const;
|
||||
|
||||
export const SYSTEM_NOTICES: SystemNotice[] = [
|
||||
// ── 4.0.0 release — what shipped, and a note from the maintainer ────────────
|
||||
// Carries `release`, so it renders as the two-column release modal rather than
|
||||
// the generic notice body. Shown once, not per-version: the copy is about this
|
||||
// release. The next one gets its own entry with its own id.
|
||||
{
|
||||
id: 'release-4-0-0',
|
||||
display: 'modal',
|
||||
severity: 'info',
|
||||
titleKey: 'system_notice.release_400.headline',
|
||||
bodyKey: 'system_notice.release_400.intro',
|
||||
release: {
|
||||
version: '4.0.0',
|
||||
eyebrowKey: 'system_notice.release_400.eyebrow',
|
||||
tagKey: 'system_notice.release_400.tag',
|
||||
headlineKey: 'system_notice.release_400.headline',
|
||||
introKey: 'system_notice.release_400.intro',
|
||||
features: [
|
||||
{
|
||||
iconName: 'Smartphone',
|
||||
titleKey: 'system_notice.release_400.feature_mobile_title',
|
||||
bodyKey: 'system_notice.release_400.feature_mobile_body',
|
||||
},
|
||||
{
|
||||
iconName: 'BookOpen',
|
||||
titleKey: 'system_notice.release_400.feature_studio_title',
|
||||
bodyKey: 'system_notice.release_400.feature_studio_body',
|
||||
badgeKey: 'system_notice.release_400.feature_studio_badge',
|
||||
},
|
||||
{
|
||||
iconName: 'CalendarDays',
|
||||
titleKey: 'system_notice.release_400.feature_vacay_title',
|
||||
bodyKey: 'system_notice.release_400.feature_vacay_body',
|
||||
},
|
||||
{
|
||||
iconName: 'Image',
|
||||
titleKey: 'system_notice.release_400.feature_places_title',
|
||||
bodyKey: 'system_notice.release_400.feature_places_body',
|
||||
},
|
||||
],
|
||||
// No stat row and no notes button here: the left column reads better short.
|
||||
footnoteKey: 'system_notice.release_400.footnote',
|
||||
note: {
|
||||
eyebrowKey: 'system_notice.release_400.note_eyebrow',
|
||||
titleKey: 'system_notice.release_400.note_title',
|
||||
bodyKey: 'system_notice.release_400.note_body',
|
||||
promiseLabelKey: 'system_notice.release_400.promise_label',
|
||||
promiseTextKey: 'system_notice.release_400.promise_text',
|
||||
bodyAfterKey: 'system_notice.release_400.note_body_after',
|
||||
closingKey: 'system_notice.release_400.note_closing',
|
||||
signatureKey: 'system_notice.release_400.note_signature',
|
||||
},
|
||||
supportTextKey: 'system_notice.release_400.support_text',
|
||||
},
|
||||
cta: {
|
||||
kind: 'link',
|
||||
labelKey: 'system_notice.release_400.cta_bmc',
|
||||
href: 'https://buymeacoffee.com/mauriceboe',
|
||||
},
|
||||
secondaryCta: {
|
||||
kind: 'link',
|
||||
labelKey: 'system_notice.release_400.cta_kofi',
|
||||
href: 'https://ko-fi.com/mauriceboe',
|
||||
},
|
||||
dismissible: true,
|
||||
// Desktop-only, like the thank-you modal it replaces: the two-column layout
|
||||
// has no phone form, and the mobile release lands with its own onboarding.
|
||||
desktopOnly: true,
|
||||
// Same reasoning as the thank-you notice below: it asks the reader to fund
|
||||
// the project, and on a managed install they already pay whoever runs it.
|
||||
conditions: [{ kind: 'managed', is: false }],
|
||||
publishedAt: '2026-08-22T00:00:00Z',
|
||||
priority: 110,
|
||||
minVersion: '4.0.0',
|
||||
},
|
||||
|
||||
// ── Thank-you + support the project — shown once per install AND once per upgrade ──
|
||||
// `recurring: 'per-version'` re-surfaces it whenever the app version moves up.
|
||||
{
|
||||
@@ -69,6 +144,11 @@ export const SYSTEM_NOTICES: SystemNotice[] = [
|
||||
publishedAt: '2026-06-27T00:00:00Z',
|
||||
priority: 100,
|
||||
recurring: 'per-version',
|
||||
// From 4.0.0 on, the release modal carries the same thank-you and the same
|
||||
// two support links, so this one would be the second half of a message the
|
||||
// reader just read. Retired by version rather than deleted: installs still
|
||||
// on 3.x keep it.
|
||||
maxVersion: '4.0.0',
|
||||
},
|
||||
|
||||
// ── 3.0.14 admin notice — whitespace migration collision ───────────────────
|
||||
|
||||
@@ -28,6 +28,47 @@ export type NoticeCta =
|
||||
| { kind: 'link'; labelKey: string; href: string } // external URL, opens in a new tab
|
||||
| { kind: 'action'; labelKey: string; actionId: string; dismissOnAction?: boolean };
|
||||
|
||||
export interface NoticeReleaseFeature {
|
||||
iconName: string;
|
||||
titleKey: string;
|
||||
bodyKey: string;
|
||||
badgeKey?: string;
|
||||
}
|
||||
|
||||
export interface NoticeReleaseStat {
|
||||
/** The figure itself ("~150") — shown as-is, never translated. */
|
||||
value: string;
|
||||
labelKey: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The release layout: the release on the left, a note from the maintainer on
|
||||
* the right. A notice carrying this renders through the dedicated two-column
|
||||
* modal instead of the generic notice body.
|
||||
*/
|
||||
export interface NoticeRelease {
|
||||
version: string;
|
||||
eyebrowKey: string;
|
||||
tagKey: string;
|
||||
headlineKey: string;
|
||||
introKey: string;
|
||||
features: NoticeReleaseFeature[];
|
||||
stats?: NoticeReleaseStat[];
|
||||
notes?: { labelKey: string; href: string };
|
||||
footnoteKey?: string;
|
||||
note: {
|
||||
eyebrowKey: string;
|
||||
titleKey: string;
|
||||
bodyKey: string;
|
||||
promiseLabelKey: string;
|
||||
promiseTextKey: string;
|
||||
bodyAfterKey: string;
|
||||
closingKey: string;
|
||||
signatureKey: string;
|
||||
};
|
||||
supportTextKey: string;
|
||||
}
|
||||
|
||||
export interface SystemNotice {
|
||||
id: string;
|
||||
display: Display;
|
||||
@@ -40,6 +81,8 @@ export interface SystemNotice {
|
||||
highlights?: Array<{ labelKey: string; iconName?: string }>;
|
||||
cta?: NoticeCta;
|
||||
secondaryCta?: NoticeCta;
|
||||
/** Set to render the two-column release modal rather than the generic body. */
|
||||
release?: NoticeRelease;
|
||||
// Hide this notice on small/mobile viewports (evaluated client-side).
|
||||
desktopOnly?: boolean;
|
||||
dismissible: boolean;
|
||||
|
||||
@@ -53,5 +53,40 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.thank_you_support.cta_bmc': 'Buy Me a Coffee',
|
||||
'system_notice.thank_you_support.cta_kofi': 'ادعمني على Ko-fi',
|
||||
'system_notice.pager.counter': '{current} / {total}', // en-fallback
|
||||
'system_notice.release_400.eyebrow': 'تم تثبيت التحديث',
|
||||
'system_notice.release_400.tag': 'إصدار',
|
||||
'system_notice.release_400.headline': 'أكبر إصدار حظي به TREK على الإطلاق.',
|
||||
'system_notice.release_400.intro':
|
||||
'يحصل TREK على هاتف، وعلى كتاب. تسعة عشر شخصًا كتبوا هذا الإصدار — ومعه نحو مئة وخمسين بلاغ خطأ تم إصلاحها.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK على الهاتف',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'كل ما دون 768px صار واجهة مستقلة — شريط زجاجي، ولوحات منزلقة خاصة به، ومخطط رحلة خاص به. افتح TREK على هاتفك.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'تحوّل PDF الخاص بـ Journey إلى مصمّم ألبومات صور. يرتّب الألبوم حين تطلب منه، ثم يبتعد عن طريقك.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay يتعلّم الباقي',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'أنصاف الأيام، وأيام التعويض والمرونة، والعطل المدرسية على الشبكة — وسنة إجازات لا يلزم أن تبدأ في يناير.',
|
||||
'system_notice.release_400.feature_places_title': 'الأماكن تعرّف عن نفسها، والملفات تنتقل',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'تُملأ الصور والوصف تلقائيًا قبل أن تحفظ المكان. ولم تعد ملفاتك المرفوعة مضطرة للبقاء على القرص الذي يعمل عليه TREK.',
|
||||
'system_notice.release_400.footnote':
|
||||
'وهذه أربعة منها فقط. يحمل 4.0.0 مئات التغييرات الأخرى، من Collections و Atlas إلى الخادم بأكمله تحتها.',
|
||||
'system_notice.release_400.note_eyebrow': 'كلمة من صاحب المشروع',
|
||||
'system_notice.release_400.note_title': 'شكرًا لاستخدامك TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
'بدأ TREK كأداة صغيرة لرحلاتي الخاصة، كتبتها في وقت فراغي. وما زال كذلك: أمسيات، وعطل نهاية الأسبوع، والساعات المتبقية بجانب عمل بدوام كامل.\n\nلفترة كنت وحدي. لم يعد الأمر كذلك — تسعة عشر شخصًا أطلقوا هذا الإصدار، وآلاف منكم جاؤوا بنجوم ومشكلات وترجمات وطلبات دمج. أنا ممتن لكل جزء من ذلك.',
|
||||
'system_notice.release_400.promise_label': 'الوعد',
|
||||
'system_notice.release_400.promise_text':
|
||||
'الجانب مفتوح المصدر من TREK يبقى مجانيًا، إلى الأبد. لا باقات مدفوعة، لا اشتراكات، لا شروط خفية. أعدكم.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'استغرق 4.0.0 أسابيع من الليالي المتأخرة — تطبيق هاتف، ومصمّم ألبومات، وترحيل الخادم، معظمه كُتب بين منتصف الليل والثانية. ليست شكوى: أحب بناء هذا. إنها فقط الإجابة الصادقة عن كيف يخرج إصدار بهذا الحجم من مشروع في وقت الفراغ.',
|
||||
'system_notice.release_400.note_closing': 'شكرًا لوجودك هنا.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'الدعم هو ما يبقي هذا مستمرًا — الخوادم والنطاقات والليالي المتأخرة التي تتحول إلى إصدارات كهذا. إذا كان TREK يساوي شيئًا لك، ففنجان قهوة هو أقصر طريق لإبقائه مستمرًا.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'ادعمني على Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -54,5 +54,41 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.v3014_whitespace_collision.title': 'Ação necessária: conflito de conta de usuário',
|
||||
'system_notice.v3014_whitespace_collision.body':
|
||||
'A atualização 3.0.14 detectou um ou mais conflitos de nome de usuário ou e-mail causados por espaços em branco no início ou fim dos valores armazenados. As contas afetadas foram renomeadas automaticamente. Verifique os logs do servidor por linhas começando com **[migration] WHITESPACE COLLISION** para identificar quais contas precisam de revisão.',
|
||||
// 4.0.0 release modal — the release on the left, the note from the maintainer on the right
|
||||
'system_notice.release_400.eyebrow': 'Atualização instalada',
|
||||
'system_notice.release_400.tag': 'Versão',
|
||||
'system_notice.release_400.headline': 'A maior versão que o TREK já teve.',
|
||||
'system_notice.release_400.intro':
|
||||
'O TREK ganha um celular e um livro. Dezenove pessoas escreveram esta — e com ela foram uns cento e cinquenta bugs relatados.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK no celular',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Tudo abaixo de 768px agora é uma interface própria — um dock de vidro, seus próprios painéis, seu próprio planejador. Abra o TREK no celular.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'O PDF do Journey virou um designer de álbuns de fotos. Ele monta o álbum quando você pede e depois sai do caminho.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay aprende o resto',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Meios dias, banco de horas e dias flexíveis, férias escolares na grade — e um ano de férias que não precisa começar em janeiro.',
|
||||
'system_notice.release_400.feature_places_title': 'Lugares que se mostram, arquivos que saem',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Fotos e descrição se preenchem sozinhas antes de você salvar um lugar. E seus uploads não precisam mais viver no disco onde o TREK roda.',
|
||||
'system_notice.release_400.footnote':
|
||||
'E estes são quatro deles. A 4.0.0 traz centenas de outras mudanças, de Collections e Atlas até todo o servidor por baixo.',
|
||||
'system_notice.release_400.note_eyebrow': 'Uma nota do mantenedor',
|
||||
'system_notice.release_400.note_title': 'Obrigado por usar o TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
'O TREK começou como uma ferramentinha para as minhas próprias viagens, escrita no tempo livre. E continua sendo: noites, fins de semana, as horas ao lado de um trabalho em tempo integral.\n\nPor um tempo era só eu. Não mais — dezenove pessoas entregaram esta versão, e milhares de pessoas chegaram com estrelas, issues, traduções e pull requests. Sou grato por cada parte disso.',
|
||||
'system_notice.release_400.promise_label': 'A promessa',
|
||||
'system_notice.release_400.promise_text':
|
||||
'O lado open source do TREK continua gratuito, para sempre. Sem planos pagos, sem assinaturas, sem pegadinhas. Prometido.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'A 4.0.0 levou semanas de noites longas — um app de celular, um designer de álbuns, uma migração de servidor, quase tudo escrito entre meia-noite e duas. Não é reclamação: eu amo construir isso. É só a resposta honesta de como uma versão desse tamanho sai de um projeto de tempo livre.',
|
||||
'system_notice.release_400.note_closing': 'Obrigado por estar aqui.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'O apoio é o que mantém isso de pé — servidores, domínios e as noites longas que viram versões como esta. Se o TREK vale algo para você, um café é o jeito mais direto de manter isso vivo.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Apoiar no Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -55,5 +55,40 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.thank_you_support.highlight_community': 'Construït conjuntament amb la comunitat',
|
||||
'system_notice.thank_you_support.cta_bmc': 'Buy Me a Coffee',
|
||||
'system_notice.thank_you_support.cta_kofi': 'Dona suport a Ko-fi',
|
||||
'system_notice.release_400.eyebrow': 'Actualització instal·lada',
|
||||
'system_notice.release_400.tag': 'Versió',
|
||||
'system_notice.release_400.headline': 'La versió més gran que TREK ha tingut mai.',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK guanya un telèfon, i un llibre. Dinou persones han escrit aquesta versió — i uns cent cinquanta errors reportats han desaparegut pel camí.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK es fa mòbil',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Tot el que hi ha per sota de 768px té ara la seva pròpia interfície — un dock de vidre, els seus fulls, el seu planificador. Obre TREK al telèfon.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
"El PDF de Journey s'ha convertit en un dissenyador de llibres de fotos. Compon el llibre quan l'hi demanes i després s'aparta.",
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay aprèn la resta',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Mitjos dies, dies de compensació i flexibles, vacances escolars a la graella — i un any de vacances que no ha de començar al gener.',
|
||||
'system_notice.release_400.feature_places_title': 'Els llocs es mostren, els fitxers marxen',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
"Les imatges i una descripció s'omplen soles abans que desis un lloc. I les teves pujades ja no han de viure al disc on s'executa TREK.",
|
||||
'system_notice.release_400.footnote':
|
||||
'I aquestes en són quatre. La 4.0.0 porta diversos centenars de canvis més, des de Collections i Atlas fins a tot el servidor de sota.',
|
||||
'system_notice.release_400.note_eyebrow': 'Una nota del mantenidor',
|
||||
'system_notice.release_400.note_title': 'Gràcies per utilitzar TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
"TREK va començar com una petita eina per als meus propis viatges, escrita al meu temps lliure. Encara ho és: vespres, caps de setmana, les hores al costat d'una feina a jornada completa.\n\nDurant un temps només hi era jo. Ja no — dinou persones han fet aquesta versió, i milers de vosaltres heu arribat amb estrelles, issues, traduccions i pull requests. Estic agraït per tot plegat.",
|
||||
'system_notice.release_400.promise_label': 'La promesa',
|
||||
'system_notice.release_400.promise_text':
|
||||
'La part de codi obert de TREK segueix sent gratuïta, per sempre. Sense nivells de pagament, sense subscripcions, sense lletra petita. Promès.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
"La 4.0.0 ha costat setmanes de nits llargues — una app de mòbil, un dissenyador de llibres, una migració del servidor, la major part escrita entre mitjanit i les dues. No és una queixa: m'encanta construir això. És només la resposta honesta a com surt una versió d'aquesta mida d'un projecte de temps lliure.",
|
||||
'system_notice.release_400.note_closing': 'Gràcies per ser aquí.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'El suport és el que fa que això funcioni — servidors, dominis i les nits llargues que es converteixen en versions com aquesta. Si TREK val alguna cosa per a tu, un cafè és la manera més directa de mantenir-ho en marxa.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Dona suport a Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -54,5 +54,41 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.v3014_whitespace_collision.title': 'Vyžadována akce: konflikt uživatelského účtu',
|
||||
'system_notice.v3014_whitespace_collision.body':
|
||||
'Aktualizace 3.0.14 zjistila jeden nebo více konfliktů uživatelského jména nebo e-mailu způsobených mezerami na začátku nebo konci uložených hodnot. Dotčené účty byly automaticky přejmenovány. Zkontrolujte protokoly serveru na řádky začínající **[migration] WHITESPACE COLLISION** a zjistěte, které účty vyžadují kontrolu.',
|
||||
// 4.0.0 release modal — the release on the left, the note from the maintainer on the right
|
||||
'system_notice.release_400.eyebrow': 'Aktualizace nainstalována',
|
||||
'system_notice.release_400.tag': 'Vydání',
|
||||
'system_notice.release_400.headline': 'Největší vydání, jaké kdy TREK měl.',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK dostává telefon a knihu. Tohle vydání psalo devatenáct lidí — a šlo s ním asi sto padesát nahlášených chyb.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK jde do mobilu',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Všechno pod 768px je teď vlastní rozhraní — skleněný dok, vlastní panely, vlastní plánovač cesty. Otevřete TREK v telefonu.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'Z PDF v Journey se stal návrhář fotoknihy. Rozvrhne knihu, když si řeknete, a pak jde stranou.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay umí i zbytek',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Půldny, náhradní volno a flexi dny, školní prázdniny v mřížce — a rok dovolené, který nemusí začínat v lednu.',
|
||||
'system_notice.release_400.feature_places_title': 'Místa se ukážou, soubory se odstěhují',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Obrázky a popis se doplní samy, ještě než místo uložíte. A nahrané soubory už nemusí ležet na disku, kde běží TREK.',
|
||||
'system_notice.release_400.footnote':
|
||||
'A tohle jsou čtyři z nich. 4.0.0 nese několik stovek dalších změn, od Collections a Atlas až po celý server pod nimi.',
|
||||
'system_notice.release_400.note_eyebrow': 'Slovo od autora',
|
||||
'system_notice.release_400.note_title': 'Děkuji, že používáte TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREK začal jako malý nástroj pro mé vlastní cesty, psaný ve volném čase. Pořád jím je: večery, víkendy, hodiny vedle práce na plný úvazek.\n\nChvíli jsem na to byl sám. Už ne — tohle vydání dotáhlo devatenáct lidí a tisíce z vás přišly s hvězdičkami, issues, překlady a pull requesty. Jsem vděčný za každý kousek.',
|
||||
'system_notice.release_400.promise_label': 'Slib',
|
||||
'system_notice.release_400.promise_text':
|
||||
'Open source část TREK zůstane zdarma, navždy. Žádné placené verze, žádná předplatná, žádné háčky. Slibuji.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0 stálo týdny pozdních nocí — aplikace do telefonu, návrhář knih, migrace serveru, většina psaná mezi půlnocí a druhou. Není to stížnost: baví mě to stavět. Je to jen upřímná odpověď na to, jak z projektu ve volném čase vznikne takhle velké vydání.',
|
||||
'system_notice.release_400.note_closing': 'Děkuji, že jste tady.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'Podpora je to, co tohle drží v chodu — servery, domény a pozdní noci, ze kterých vznikají vydání jako tohle. Pokud pro vás TREK něco znamená, káva je nejpřímější způsob, jak ho udržet v běhu.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Podpořit na Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -55,5 +55,41 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.v3014_whitespace_collision.title': 'Aktion erforderlich: Benutzerkontokonflikt',
|
||||
'system_notice.v3014_whitespace_collision.body':
|
||||
'Das 3.0.14-Upgrade hat einen oder mehrere Konflikte bei Benutzernamen oder E-Mail-Adressen festgestellt, die durch führende oder nachgestellte Leerzeichen in gespeicherten Konten verursacht wurden. Betroffene Konten wurden automatisch umbenannt. Prüfe die Serverprotokolle auf Zeilen, die mit **[migration] WHITESPACE COLLISION** beginnen, um die betroffenen Konten zu identifizieren.',
|
||||
// 4.0.0-Release-Modal — links das Release, rechts das Wort vom Maintainer
|
||||
'system_notice.release_400.eyebrow': 'Update installiert',
|
||||
'system_notice.release_400.tag': 'Release',
|
||||
'system_notice.release_400.headline': 'Das größte Release, das TREK je hatte.',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK bekommt ein Handy und ein Buch. Neunzehn Leute haben daran geschrieben — und rund hundertfünfzig gemeldete Bugs sind mitgekommen.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK wird mobil',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Alles unter 768px ist jetzt eine eigene Oberfläche — ein Glas-Dock, eigene Sheets, ein eigener Trip-Planer. Öffne TREK auf deinem Handy.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'Aus dem Journey-PDF wurde ein Fotobuch-Designer. Er setzt das Buch, wenn du ihn darum bittest, und hält sich danach raus.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay lernt den Rest',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Halbe Tage, Überstunden- und Gleittage, Schulferien im Raster — und ein Urlaubsjahr, das nicht im Januar anfangen muss.',
|
||||
'system_notice.release_400.feature_places_title': 'Orte zeigen sich, Dateien ziehen aus',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Bilder und eine Beschreibung füllen sich von selbst, bevor du einen Ort speicherst. Und deine Uploads müssen nicht mehr auf der Platte liegen, auf der TREK läuft.',
|
||||
'system_notice.release_400.footnote':
|
||||
'Und das sind vier davon. 4.0.0 bringt mehrere hundert weitere Änderungen mit, von Collections und Atlas bis zum ganzen Server darunter.',
|
||||
'system_notice.release_400.note_eyebrow': 'Ein Wort vom Maintainer',
|
||||
'system_notice.release_400.note_title': 'Danke, dass du TREK nutzt.',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREK hat als kleines Tool für meine eigenen Reisen angefangen, geschrieben in meiner Freizeit. Das ist es immer noch: Abende, Wochenenden, die Stunden neben einem Vollzeitjob.\n\nEine Weile war ich allein damit. Jetzt nicht mehr — neunzehn Leute haben dieses Release gebaut, und Tausende von euch kamen dazu, mit Sternen, Issues, Übersetzungen und Pull Requests. Ich bin für jedes Stück davon dankbar.',
|
||||
'system_notice.release_400.promise_label': 'Das Versprechen',
|
||||
'system_notice.release_400.promise_text':
|
||||
'Die Open-Source-Seite von TREK bleibt kostenlos, für immer. Keine Paid Tiers, keine Abos, kein Haken. Versprochen.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0 hat Wochen an späten Nächten gekostet — eine Handy-App, ein Buch-Designer, eine Server-Migration, das meiste zwischen Mitternacht und zwei geschrieben. Keine Klage: Ich baue das gern. Es ist nur die ehrliche Antwort darauf, wie ein Release dieser Größe aus einem Freizeitprojekt kommt.',
|
||||
'system_notice.release_400.note_closing': 'Danke, dass du dabei bist.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'Unterstützung ist das, was das hier am Laufen hält — Server, Domains und die späten Nächte, aus denen Releases wie dieses werden. Wenn TREK dir etwas wert ist, ist ein Kaffee der direkteste Weg, es weiterlaufen zu lassen.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Ko-fi unterstützen',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -50,6 +50,42 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.thank_you_support.highlight_community': 'Built together with the community',
|
||||
'system_notice.thank_you_support.cta_bmc': 'Buy Me a Coffee',
|
||||
'system_notice.thank_you_support.cta_kofi': 'Support on Ko-fi',
|
||||
// 4.0.0 release modal — the release on the left, the note from the maintainer on the right
|
||||
'system_notice.release_400.eyebrow': 'Update installed',
|
||||
'system_notice.release_400.tag': 'Release',
|
||||
'system_notice.release_400.headline': 'The biggest release TREK has ever had.',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK gets a phone, and a book. Nineteen people wrote this one — and about a hundred and fifty reported bugs went with it.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK goes mobile',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Everything under 768px is its own interface now — a glass dock, its own sheets, its own trip planner. Open TREK on your phone.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'The Journey PDF became a photo book designer. It lays the book out when you ask it to, then gets out of the way.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay learns the rest',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Half days, comp and flex days, school holidays on the grid — and a leave year that does not have to start in January.',
|
||||
'system_notice.release_400.feature_places_title': 'Places show themselves, files move out',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Pictures and a description fill themselves in before you save a place. And your uploads no longer have to live on the disk TREK runs on.',
|
||||
'system_notice.release_400.footnote':
|
||||
'And these are four of them. 4.0.0 carries several hundred further changes, from Collections and Atlas to the whole server underneath.',
|
||||
'system_notice.release_400.note_eyebrow': 'A note from the maintainer',
|
||||
'system_notice.release_400.note_title': 'Thank you for using TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREK started as a little tool for my own trips, written in my spare time. It still is: evenings, weekends, the hours next to a full-time job.\n\nFor a while it was only me. Not anymore — nineteen people shipped this release, and thousands of you arrived with stars, issues, translations and pull requests. I am grateful for every part of it.',
|
||||
'system_notice.release_400.promise_label': 'The promise',
|
||||
'system_notice.release_400.promise_text':
|
||||
'The open-source side of TREK stays free, forever. No paid tiers, no subscriptions, no catch. Promised.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0 took weeks of late nights — a phone app, a book designer, a server migration, most of it written between midnight and two. Not a complaint: I love building this. It is just the honest answer to how a release this size comes out of a spare-time project.',
|
||||
'system_notice.release_400.note_closing': 'Thank you for being here.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'Support is what keeps this running — servers, domains, and the late nights that turn into releases like this one. If TREK is worth something to you, a coffee is the most direct way to keep it going.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Support on Ko-fi',
|
||||
'system_notice.pager.prev': 'Previous notice',
|
||||
'system_notice.pager.next': 'Next notice',
|
||||
'system_notice.pager.counter': '{current} / {total}',
|
||||
|
||||
@@ -54,5 +54,41 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.v3014_whitespace_collision.title': 'Acción requerida: conflicto de cuenta de usuario',
|
||||
'system_notice.v3014_whitespace_collision.body':
|
||||
'La actualización 3.0.14 detectó uno o más conflictos de nombre de usuario o correo electrónico causados por espacios en blanco al inicio o al final de los valores almacenados. Las cuentas afectadas se renombraron automáticamente. Revisa los registros del servidor en busca de líneas que empiecen por **[migration] WHITESPACE COLLISION** para identificar qué cuentas necesitan revisión.',
|
||||
// 4.0.0 release modal — the release on the left, the note from the maintainer on the right
|
||||
'system_notice.release_400.eyebrow': 'Actualización instalada',
|
||||
'system_notice.release_400.tag': 'Versión',
|
||||
'system_notice.release_400.headline': 'La mayor versión que TREK ha tenido jamás.',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK recibe un móvil y un libro. La escribieron diecinueve personas — y con ella se fueron unos ciento cincuenta bugs.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK en el móvil',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Todo por debajo de 768px es ya una interfaz propia — un dock de cristal, sus propios paneles, su propio planificador. Abre TREK en tu móvil.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'El PDF de Journey se convirtió en un diseñador de fotolibros. Maqueta el libro cuando se lo pides y luego se aparta.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay aprende el resto',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Medios días, días de compensación y flexibles, vacaciones escolares en la rejilla — y un año que no tiene que empezar en enero.',
|
||||
'system_notice.release_400.feature_places_title': 'Lugares que se muestran, archivos que salen',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Las fotos y la descripción se rellenan solas antes de guardar un lugar. Y tus archivos ya no tienen que vivir en el disco donde corre TREK.',
|
||||
'system_notice.release_400.footnote':
|
||||
'Y estas son cuatro. La 4.0.0 trae varios cientos de cambios más, desde Collections y Atlas hasta todo el servidor por debajo.',
|
||||
'system_notice.release_400.note_eyebrow': 'Una nota del desarrollador',
|
||||
'system_notice.release_400.note_title': 'Gracias por usar TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREK empezó como una pequeña herramienta para mis propios viajes, escrita en mi tiempo libre. Y lo sigue siendo: tardes, fines de semana, las horas junto a un trabajo a jornada completa.\n\nDurante un tiempo fui solo yo. Ya no — diecinueve personas sacaron esta versión, y miles de personas llegaron con estrellas, issues, traducciones y pull requests. Estoy agradecido por cada parte.',
|
||||
'system_notice.release_400.promise_label': 'La promesa',
|
||||
'system_notice.release_400.promise_text':
|
||||
'La parte open source de TREK sigue siendo gratis, para siempre. Sin planes de pago, sin suscripciones, sin trampa. Prometido.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'La 4.0.0 costó semanas de noches en vela — una app de móvil, un diseñador de libros, una migración del servidor, casi todo escrito entre medianoche y las dos. No es una queja: me encanta hacer esto. Es solo la respuesta honesta a cómo sale una versión así de un proyecto de tiempo libre.',
|
||||
'system_notice.release_400.note_closing': 'Gracias por estar aquí.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'El apoyo es lo que mantiene esto en marcha — servidores, dominios y las noches en vela que acaban en versiones como esta. Si TREK vale algo para ti, un café es la forma más directa de que siga.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Apóyame en Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -54,5 +54,40 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.v3014_whitespace_collision.title': 'Action requise : conflit de compte utilisateur',
|
||||
'system_notice.v3014_whitespace_collision.body':
|
||||
"La mise à niveau 3.0.14 a détecté un ou plusieurs conflits de nom d'utilisateur ou d'adresse e-mail causés par des espaces en début ou en fin de valeur dans les comptes enregistrés. Les comptes concernés ont été renommés automatiquement. Consultez les journaux du serveur pour les lignes commençant par **[migration] WHITESPACE COLLISION** afin d'identifier les comptes nécessitant une vérification.",
|
||||
'system_notice.release_400.eyebrow': 'Mise à jour installée',
|
||||
'system_notice.release_400.tag': 'Version',
|
||||
'system_notice.release_400.headline': 'La plus grande version de TREK à ce jour.',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK gagne un téléphone, et un livre. Dix-neuf personnes ont écrit cette version — et environ cent cinquante bugs signalés ont disparu au passage.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK passe au mobile',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Tout ce qui est sous 768px a désormais sa propre interface — un dock en verre, ses feuilles, son planificateur. Ouvre TREK sur ton téléphone.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
"Le PDF de Journey est devenu un concepteur de livre photo. Il met le livre en page quand tu le demandes, puis s'efface.",
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay apprend le reste',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Demi-journées, récup et RTT, vacances scolaires dans la grille — et une année de congés qui ne commence pas forcément en janvier.',
|
||||
'system_notice.release_400.feature_places_title': 'Les lieux se présentent, les fichiers déménagent',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Photos et description se remplissent toutes seules avant que tu enregistres un lieu. Et tes fichiers ne sont plus obligés de vivre sur le disque où tourne TREK.',
|
||||
'system_notice.release_400.footnote':
|
||||
"Et ce ne sont que quatre d'entre eux. 4.0.0 apporte plusieurs centaines d'autres changements, de Collections et Atlas jusqu'au serveur en dessous.",
|
||||
'system_notice.release_400.note_eyebrow': 'Un mot du mainteneur',
|
||||
'system_notice.release_400.note_title': "Merci d'utiliser TREK.",
|
||||
'system_notice.release_400.note_body':
|
||||
"TREK a commencé comme un petit outil pour mes propres voyages, écrit sur mon temps libre. Ça l'est toujours : le soir, le week-end, les heures à côté d'un travail à temps plein.\n\nPendant un temps, il n'y avait que moi. Plus maintenant — dix-neuf personnes ont livré cette version, et vous êtes des milliers à être arrivés avec des étoiles, des issues, des traductions et des pull requests. Je suis reconnaissant pour tout ça.",
|
||||
'system_notice.release_400.promise_label': 'La promesse',
|
||||
'system_notice.release_400.promise_text':
|
||||
"Le côté open source de TREK reste gratuit, pour toujours. Pas de formules payantes, pas d'abonnements, aucun piège. Promis.",
|
||||
'system_notice.release_400.note_body_after':
|
||||
"4.0.0 a demandé des semaines de nuits blanches — une app mobile, un concepteur de livre, une migration serveur, l'essentiel écrit entre minuit et deux heures. Ce n'est pas une plainte : j'adore construire ça. C'est juste la réponse honnête à comment une version de cette taille sort d'un projet de temps libre.",
|
||||
'system_notice.release_400.note_closing': "Merci d'être là.",
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'Le soutien est ce qui fait tourner tout ça — serveurs, domaines, et les nuits blanches qui deviennent des versions comme celle-ci. Si TREK vaut quelque chose pour toi, un café est le moyen le plus direct de continuer.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Soutenir sur Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -56,5 +56,40 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.pager.counter': '{current} / {total}',
|
||||
'system_notice.pager.goto': 'Μετάβαση στην ειδοποίηση {n}',
|
||||
'system_notice.pager.position': 'Ειδοποίηση {current} από {total}',
|
||||
'system_notice.release_400.eyebrow': 'Ενημερώθηκε',
|
||||
'system_notice.release_400.tag': 'Έκδοση',
|
||||
'system_notice.release_400.headline': 'Η μεγαλύτερη έκδοση που είχε ποτέ το TREK.',
|
||||
'system_notice.release_400.intro':
|
||||
'Το TREK αποκτά τηλέφωνο, και βιβλίο. Αυτή την έκδοση την έγραψαν δεκαεννέα άτομα — και μαζί της έφυγαν περίπου εκατόν πενήντα σφάλματα που αναφέρατε.',
|
||||
'system_notice.release_400.feature_mobile_title': 'Το TREK σε κινητό',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Οτιδήποτε κάτω από 768px έχει πλέον δικό του περιβάλλον — ένα γυάλινο dock, δικά του πάνελ, δικό του σχεδιαστή ταξιδιού. Ανοίξτε το TREK στο κινητό σας.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'Το PDF του Journey έγινε σχεδιαστής βιβλίου φωτογραφιών. Στήνει το βιβλίο όταν του το ζητήσετε, και μετά κάνει στην άκρη.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Το Vacay μαθαίνει τα υπόλοιπα',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Μισές ημέρες, ρεπό και ευέλικτες ημέρες, σχολικές αργίες στο πλέγμα — και ένα έτος αδειών που δεν χρειάζεται να ξεκινά τον Ιανουάριο.',
|
||||
'system_notice.release_400.feature_places_title': 'Οι τοποθεσίες συστήνονται, τα αρχεία φεύγουν',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Εικόνες και περιγραφή συμπληρώνονται μόνες τους πριν αποθηκεύσετε μια τοποθεσία. Και τα αρχεία σας δεν χρειάζεται πια να μένουν στον δίσκο όπου τρέχει το TREK.',
|
||||
'system_notice.release_400.footnote':
|
||||
'Και αυτές είναι τέσσερις. Η 4.0.0 φέρνει αρκετές εκατοντάδες ακόμη αλλαγές, από τα Collections και το Atlas μέχρι όλο τον server από κάτω.',
|
||||
'system_notice.release_400.note_eyebrow': 'Μια σημείωση από τον δημιουργό',
|
||||
'system_notice.release_400.note_title': 'Ευχαριστώ που χρησιμοποιείτε το TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
'Το TREK ξεκίνησε ως ένα μικρό εργαλείο για τα δικά μου ταξίδια, γραμμένο στον ελεύθερό μου χρόνο. Έτσι είναι ακόμα: βράδια, σαββατοκύριακα, οι ώρες δίπλα σε μια δουλειά πλήρους απασχόλησης.\n\nΓια κάποιο διάστημα ήμουν μόνο εγώ. Όχι πια — δεκαεννέα άτομα έβγαλαν αυτή την έκδοση, και χιλιάδες από εσάς ήρθατε με αστέρια, αναφορές, μεταφράσεις και pull requests. Είμαι ευγνώμων για κάθε κομμάτι της.',
|
||||
'system_notice.release_400.promise_label': 'Η υπόσχεση',
|
||||
'system_notice.release_400.promise_text':
|
||||
'Η ανοιχτού κώδικα πλευρά του TREK μένει δωρεάν, για πάντα. Καμία έκδοση επί πληρωμή, καμία συνδρομή, καμία παγίδα. Το υπόσχομαι.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'Η 4.0.0 πήρε εβδομάδες από ξενύχτια — μια εφαρμογή για κινητό, έναν σχεδιαστή βιβλίου, μια μετάβαση του server, τα περισσότερα γραμμένα ανάμεσα στα μεσάνυχτα και τις δύο. Δεν είναι παράπονο: μου αρέσει να το φτιάχνω. Είναι απλώς η ειλικρινής απάντηση στο πώς βγαίνει μια τόσο μεγάλη έκδοση από ένα έργο του ελεύθερου χρόνου.',
|
||||
'system_notice.release_400.note_closing': 'Σας ευχαριστώ που είστε εδώ.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'Η στήριξη είναι αυτό που κρατά όλο αυτό ζωντανό — servers, domains και οι ξενύχτιες που γίνονται εκδόσεις σαν κι αυτή. Αν το TREK αξίζει κάτι για εσάς, ένας καφές είναι ο πιο άμεσος τρόπος να συνεχίσει.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Στηρίξτε στο Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -54,5 +54,40 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.v3014_whitespace_collision.title': 'Szükséges beavatkozás: felhasználói fiókütközés',
|
||||
'system_notice.v3014_whitespace_collision.body':
|
||||
'A 3.0.14-es frissítés egy vagy több felhasználónév- vagy e-mail-ütközést észlelt, amelyeket a tárolt értékek elején vagy végén lévő szóközök okoztak. Az érintett fiókok automatikusan át lettek nevezve. Ellenőrizze a szervernaplókat a **[migration] WHITESPACE COLLISION** kezdetű soroknál a felülvizsgálatot igénylő fiókok azonosításához.',
|
||||
'system_notice.release_400.eyebrow': 'Frissítés telepítve',
|
||||
'system_notice.release_400.tag': 'Kiadás',
|
||||
'system_notice.release_400.headline': 'A TREK eddigi legnagyobb kiadása.',
|
||||
'system_notice.release_400.intro':
|
||||
'A TREK kap egy telefont és egy könyvet. Ezt tizenkilencen írták — és nagyjából százötven bejelentett hiba ment vele.',
|
||||
'system_notice.release_400.feature_mobile_title': 'A TREK mobilon',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Minden 768px alatt már saját felület — üveg dokk, saját panelek, saját utazástervező. Nyisd meg a TREK-et a telefonodon.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'A Journey PDF fotókönyv-tervezővé vált. Megszerkeszti a könyvet, ha kéred, aztán félreáll.',
|
||||
'system_notice.release_400.feature_vacay_title': 'A Vacay megtanulja a többit',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Fél napok, csúsztatott és rugalmas napok, iskolai szünetek a naptárban — és szabadságév, aminek nem kell januárban kezdődnie.',
|
||||
'system_notice.release_400.feature_places_title': 'Helyek megmutatkoznak, fájlok költöznek',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Képek és leírás maguktól kitöltődnek, mielőtt mentenél egy helyet. A feltöltéseidnek pedig már nem kell azon a lemezen lakniuk, amin a TREK fut.',
|
||||
'system_notice.release_400.footnote':
|
||||
'És ez négy közülük. A 4.0.0 több száz további változást hoz, a Collections-től és az Atlastól az egész alatta futó szerverig.',
|
||||
'system_notice.release_400.note_eyebrow': 'Egy szó a fejlesztőtől',
|
||||
'system_notice.release_400.note_title': 'Köszönöm, hogy a TREK-et használod.',
|
||||
'system_notice.release_400.note_body':
|
||||
'A TREK egy kis eszközként indult a saját utazásaimhoz, a szabadidőmben írva. Ma is az: esték, hétvégék, a teljes állás melletti órák.\n\nEgy ideig csak én voltam. Már nem — ezt a kiadást tizenkilencen szállították, és több ezren érkeztetek csillagokkal, issue-kkal, fordításokkal és pull requestekkel. Mindegyikért hálás vagyok.',
|
||||
'system_notice.release_400.promise_label': 'Az ígéret',
|
||||
'system_notice.release_400.promise_text':
|
||||
'A TREK nyílt forráskódú oldala ingyenes marad, örökre. Nincsenek fizetős csomagok, nincsenek előfizetések, nincs átverés. Ígérem.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'A 4.0.0 heteknyi késő éjszakába került — egy telefonos felület, egy könyvtervező, egy szerverköltözés, java része éjfél és kettő között írva. Nem panasz: szeretem építeni. Csak az őszinte válasz arra, hogyan születik ekkora kiadás egy szabadidős projektből.',
|
||||
'system_notice.release_400.note_closing': 'Köszönöm, hogy itt vagy.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'A támogatás tartja életben — szerverek, domainek, és a késő éjszakák, amikből ilyen kiadások lesznek. Ha a TREK ér neked valamit, egy kávé a legközvetlenebb módja, hogy tovább menjen.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Támogass a Ko-fi-n',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -54,5 +54,40 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.v3014_whitespace_collision.title': 'Tindakan diperlukan: konflik akun pengguna',
|
||||
'system_notice.v3014_whitespace_collision.body':
|
||||
'Pembaruan 3.0.14 mendeteksi satu atau lebih konflik nama pengguna atau email yang disebabkan oleh spasi di awal atau akhir nilai yang tersimpan. Akun yang terpengaruh telah diganti nama secara otomatis. Periksa log server untuk baris yang dimulai dengan **[migration] WHITESPACE COLLISION** guna mengidentifikasi akun mana yang perlu ditinjau.',
|
||||
'system_notice.release_400.eyebrow': 'Pembaruan terpasang',
|
||||
'system_notice.release_400.tag': 'Rilis',
|
||||
'system_notice.release_400.headline': 'Rilis terbesar yang pernah ada di TREK.',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK kini punya ponsel, dan sebuah buku. Sembilan belas orang menulis rilis ini — dan sekitar seratus lima puluh laporan bug ikut diperbaiki.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK kini mobile',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Semua di bawah 768px kini antarmuka tersendiri — dock kaca, sheet sendiri, perencana perjalanan sendiri. Buka TREK di ponsel Anda.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'PDF Journey berubah jadi perancang buku foto. Ia menata bukunya saat Anda minta, lalu menyingkir.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay belajar sisanya',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Setengah hari, hari pengganti dan fleksibel, libur sekolah di kalender — dan tahun cuti yang tak harus mulai Januari.',
|
||||
'system_notice.release_400.feature_places_title': 'Tempat tampil sendiri, file pindah keluar',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Gambar dan deskripsi terisi sendiri sebelum Anda menyimpan tempat. Dan unggahan Anda tak harus lagi tinggal di disk tempat TREK berjalan.',
|
||||
'system_notice.release_400.footnote':
|
||||
'Dan ini baru empat di antaranya. 4.0.0 membawa beberapa ratus perubahan lain, dari Collections dan Atlas sampai seluruh server di baliknya.',
|
||||
'system_notice.release_400.note_eyebrow': 'Catatan dari maintainer',
|
||||
'system_notice.release_400.note_title': 'Terima kasih telah memakai TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREK dimulai sebagai alat kecil untuk perjalanan saya sendiri, ditulis di waktu luang. Sampai sekarang masih begitu: malam hari, akhir pekan, jam-jam di sela pekerjaan penuh waktu.\n\nDulu hanya saya sendiri. Sekarang tidak lagi — sembilan belas orang merilis versi ini, dan ribuan dari kalian datang dengan bintang, issue, terjemahan dan pull request. Saya bersyukur atas setiap bagiannya.',
|
||||
'system_notice.release_400.promise_label': 'Janjinya',
|
||||
'system_notice.release_400.promise_text':
|
||||
'Sisi open source TREK tetap gratis, selamanya. Tanpa paket berbayar, tanpa langganan, tanpa syarat tersembunyi. Saya janji.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0 menghabiskan berminggu-minggu malam larut — aplikasi ponsel, perancang buku, migrasi server, sebagian besar ditulis antara pukul dua belas dan dua. Bukan keluhan: saya senang membangun ini. Ini cuma jawaban jujur bagaimana rilis sebesar ini keluar dari proyek waktu luang.',
|
||||
'system_notice.release_400.note_closing': 'Terima kasih telah berada di sini.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'Dukungan itulah yang membuat semua ini berjalan — server, domain, dan malam-malam larut yang berubah jadi rilis seperti ini. Jika TREK berarti bagimu, secangkir kopi adalah cara paling langsung untuk menjaganya tetap jalan.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Dukung di Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -54,5 +54,40 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.v3014_whitespace_collision.title': 'Azione richiesta: conflitto di account utente',
|
||||
'system_notice.v3014_whitespace_collision.body':
|
||||
"L'aggiornamento 3.0.14 ha rilevato uno o più conflitti di nome utente o e-mail causati da spazi iniziali o finali nei valori memorizzati. Gli account interessati sono stati rinominati automaticamente. Controlla i log del server per le righe che iniziano con **[migration] WHITESPACE COLLISION** per identificare quali account richiedono revisione.",
|
||||
'system_notice.release_400.eyebrow': 'Aggiornato',
|
||||
'system_notice.release_400.tag': 'Versione',
|
||||
'system_notice.release_400.headline': 'La versione più grande di sempre di TREK.',
|
||||
'system_notice.release_400.intro':
|
||||
"TREK riceve un telefono, e un libro. Questa l'hanno scritta in diciannove — e con lei se ne sono andati circa centocinquanta bug segnalati.",
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK diventa mobile',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
"Tutto sotto i 768px ora è un'interfaccia a sé — una dock in vetro, i suoi pannelli, il suo pianificatore di viaggi. Apri TREK dal telefono.",
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'Il PDF di Journey è diventato un editor di libri fotografici. Impagina il libro quando glielo chiedi, poi si toglie di mezzo.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay impara il resto',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Mezze giornate, recuperi e giorni flessibili, vacanze scolastiche nel calendario — e un anno di ferie che non deve iniziare a gennaio.',
|
||||
'system_notice.release_400.feature_places_title': 'I luoghi si mostrano, i file traslocano',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Immagini e descrizione si compilano da sole prima che tu salvi un luogo. E i tuoi caricamenti non devono più stare sul disco dove gira TREK.',
|
||||
'system_notice.release_400.footnote':
|
||||
'E queste sono quattro. La 4.0.0 porta diverse centinaia di altre modifiche, da Collections e Atlas fino a tutto il server sotto.',
|
||||
'system_notice.release_400.note_eyebrow': 'Una nota dal maintainer',
|
||||
'system_notice.release_400.note_title': 'Grazie per usare TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
"TREK è nato come un piccolo strumento per i miei viaggi, scritto nel tempo libero. Lo è ancora: sere, weekend, le ore accanto a un lavoro a tempo pieno.\n\nPer un po' sono stato solo io. Non più — questa versione l'hanno portata in diciannove, e migliaia di voi sono arrivati con stelle, issue, traduzioni e pull request. Sono grato per ogni parte di tutto questo.",
|
||||
'system_notice.release_400.promise_label': 'La promessa',
|
||||
'system_notice.release_400.promise_text':
|
||||
'La parte open source di TREK resta gratuita, per sempre. Nessun piano a pagamento, nessun abbonamento, nessuna fregatura. Promesso.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
"La 4.0.0 è costata settimane di notti tarde — un'app per il telefono, un editor di libri, una migrazione del server, quasi tutto scritto tra mezzanotte e le due. Non è una lamentela: mi piace costruirlo. È solo la risposta onesta a come una versione così grande esca da un progetto del tempo libero.",
|
||||
'system_notice.release_400.note_closing': 'Grazie per essere qui.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'Il sostegno è ciò che tiene in piedi tutto questo — server, domini e le notti tarde che diventano versioni come questa. Se TREK vale qualcosa per te, un caffè è il modo più diretto per farlo continuare.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Supporta su Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -52,5 +52,40 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.pager.counter': '{current} / {total}',
|
||||
'system_notice.pager.goto': '通知{n}へ',
|
||||
'system_notice.pager.position': '{total}件中{current}件目',
|
||||
'system_notice.release_400.eyebrow': 'アップデート完了',
|
||||
'system_notice.release_400.tag': 'リリース',
|
||||
'system_notice.release_400.headline': 'TREK史上、最大のリリースです。',
|
||||
'system_notice.release_400.intro':
|
||||
'TREKにスマホと、本が加わりました。19人で書き上げ、報告されていたバグ約150件も一緒に片づきました。',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREKがモバイルへ',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'768px未満は専用のインターフェースです。ガラス調のドック、専用のシート、専用の旅行プランナー。スマホでTREKを開いてください。',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'JourneyのPDFがフォトブックのデザイナーになりました。頼めばレイアウトを組み、あとは邪魔をしません。',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacayが残りを覚える',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'半休、代休とフレックス、カレンダーに並ぶ学校の休暇。そして1月始まりでなくてもいい休暇年度。',
|
||||
'system_notice.release_400.feature_places_title': '場所は自ら名乗り、ファイルは外へ',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'場所を保存する前に、写真と説明がひとりでに埋まります。アップロードしたファイルも、TREKが動くディスクに置かなくてよくなりました。',
|
||||
'system_notice.release_400.footnote':
|
||||
'これはそのうちの4つです。4.0.0にはCollectionsやAtlasから土台のサーバーまで、数百の変更が入っています。',
|
||||
'system_notice.release_400.note_eyebrow': '開発者より',
|
||||
'system_notice.release_400.note_title': 'TREKを使ってくれてありがとう。',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREKは自分の旅のために、空いた時間で作った小さなツールとして始まりました。今もそれは変わりません。夜と週末、本業の合間の時間です。\n\nしばらくは私一人でした。もう違います。このリリースは19人で送り出し、何千もの人がスター、Issue、翻訳、プルリクエストを持って集まってくれました。そのすべてに感謝しています。',
|
||||
'system_notice.release_400.promise_label': '約束',
|
||||
'system_notice.release_400.promise_text':
|
||||
'TREKのオープンソースはずっと無料です。有料プランも、サブスクも、隠れた仕掛けもありません。約束します。',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0には何週間もの夜なべがかかりました。スマホの画面、フォトブックのデザイナー、サーバーの移行。多くは深夜0時から2時のあいだに書きました。愚痴ではありません。作るのが好きです。この規模のリリースが片手間のプロジェクトからどう出るか、正直に答えればこうです。',
|
||||
'system_notice.release_400.note_closing': 'ここにいてくれて、ありがとう。',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'これを続けさせてくれているのは応援です。サーバー代、ドメイン代、そして今回のようなリリースになる夜なべ。TREKに価値を感じてもらえたなら、コーヒー一杯がいちばん直接的な支えになります。',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Ko-fiで応援する',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -54,5 +54,40 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.pager.counter': '{current} / {total}',
|
||||
'system_notice.pager.goto': '{n}번 공지로 이동',
|
||||
'system_notice.pager.position': '공지 {current}/{total}',
|
||||
'system_notice.release_400.eyebrow': '업데이트 완료',
|
||||
'system_notice.release_400.tag': '릴리스',
|
||||
'system_notice.release_400.headline': 'TREK 역사상 가장 큰 릴리스입니다.',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK에 휴대폰과 책이 생겼습니다. 열아홉 명이 함께 만들었고, 보고된 버그 약 150개가 함께 정리되었습니다.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK, 모바일로',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'768px 아래는 이제 그 자체로 하나의 인터페이스입니다 — 글라스 독, 전용 시트, 전용 여행 플래너. 휴대폰에서 TREK을 열어보세요.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'Journey의 PDF가 사진 책 디자이너가 되었습니다. 부탁하면 책을 배치해 주고, 그다음엔 물러나 있습니다.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay, 나머지까지',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'반차, 보상 휴가와 유연 근무일, 달력에 올라온 학교 방학 — 그리고 1월에 시작하지 않아도 되는 휴가 연도.',
|
||||
'system_notice.release_400.feature_places_title': '장소는 스스로 채우고, 파일은 밖으로',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'장소를 저장하기 전에 사진과 설명이 알아서 채워집니다. 그리고 업로드한 파일은 더 이상 TREK이 도는 디스크에 있지 않아도 됩니다.',
|
||||
'system_notice.release_400.footnote':
|
||||
'그리고 이건 그중 네 가지입니다. 4.0.0에는 Collections와 Atlas부터 그 아래 서버 전체까지 수백 가지 변경이 더 담겨 있습니다.',
|
||||
'system_notice.release_400.note_eyebrow': '개발자의 한마디',
|
||||
'system_notice.release_400.note_title': 'TREK을 사용해 주셔서 감사합니다.',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREK은 제 여행을 위해 여가 시간에 만든 작은 도구로 시작했습니다. 지금도 그렇습니다. 저녁, 주말, 그리고 풀타임 직장 옆의 시간들.\n\n한동안은 저 혼자였습니다. 이제는 아닙니다 — 열아홉 명이 이번 릴리스를 함께 내보냈고, 수천 명이 별과 이슈, 번역과 풀 리퀘스트를 들고 찾아와 주셨습니다. 그 모든 것에 감사합니다.',
|
||||
'system_notice.release_400.promise_label': '약속',
|
||||
'system_notice.release_400.promise_text':
|
||||
'TREK의 오픈 소스 쪽은 영원히 무료입니다. 유료 등급도, 구독도, 숨겨진 조건도 없습니다. 약속드릴게요.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0에는 늦은 밤 몇 주가 들어갔습니다 — 휴대폰 화면, 책 디자이너, 서버 마이그레이션, 대부분 자정과 새벽 두 시 사이에 썼습니다. 불평은 아닙니다. 이걸 만드는 게 좋습니다. 다만 이 정도 규모의 릴리스가 여가 프로젝트에서 어떻게 나오는지에 대한 솔직한 답입니다.',
|
||||
'system_notice.release_400.note_closing': '함께해 주셔서 감사합니다.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'이걸 계속 굴러가게 하는 건 후원입니다 — 서버와 도메인, 그리고 이런 릴리스가 되는 늦은 밤들. TREK이 여러분에게 가치가 있다면, 커피 한 잔이 이어가는 가장 직접적인 방법입니다.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Ko-fi에서 후원하기',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -54,5 +54,41 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.v3014_whitespace_collision.title': 'Actie vereist: gebruikersaccountconflict',
|
||||
'system_notice.v3014_whitespace_collision.body':
|
||||
'De 3.0.14-upgrade heeft één of meer conflicten in gebruikersnaam of e-mailadres gedetecteerd, veroorzaakt door spaties aan het begin of einde van opgeslagen waarden. Getroffen accounts zijn automatisch hernoemd. Controleer de serverlogboeken op regels die beginnen met **[migration] WHITESPACE COLLISION** om te achterhalen welke accounts moeten worden beoordeeld.',
|
||||
// 4.0.0-releasemodal — links de release, rechts het woord van de maintainer
|
||||
'system_notice.release_400.eyebrow': 'Update geïnstalleerd',
|
||||
'system_notice.release_400.tag': 'Release',
|
||||
'system_notice.release_400.headline': 'De grootste release die TREK ooit heeft gehad.',
|
||||
'system_notice.release_400.intro':
|
||||
"TREK krijgt een telefoon en een boek. Negentien mensen schreven hieraan mee — en zo'n honderdvijftig gemelde bugs gingen mee.",
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK wordt mobiel',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Alles onder 768px is nu een eigen interface — een glazen dock, eigen sheets, een eigen reisplanner. Open TREK op je telefoon.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'De Journey-PDF werd een fotoboekontwerper. Hij maakt de opmaak wanneer je erom vraagt en gaat daarna uit de weg.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay leert de rest',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Halve dagen, compensatie- en flexdagen, schoolvakanties in het raster — en een verlofjaar dat niet in januari hoeft te beginnen.',
|
||||
'system_notice.release_400.feature_places_title': 'Plaatsen tonen zichzelf, bestanden verhuizen',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
"Foto's en een beschrijving vullen zichzelf in voordat je een plaats opslaat. En je uploads hoeven niet langer op de schijf te staan waarop TREK draait.",
|
||||
'system_notice.release_400.footnote':
|
||||
'En dit zijn er vier. 4.0.0 bevat nog enkele honderden wijzigingen, van Collections en Atlas tot de hele server eronder.',
|
||||
'system_notice.release_400.note_eyebrow': 'Een woord van de maintainer',
|
||||
'system_notice.release_400.note_title': 'Bedankt voor het gebruik van TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREK begon als een klein hulpmiddel voor mijn eigen reizen, geschreven in mijn vrije tijd. Dat is het nog steeds: avonden, weekenden, de uren naast een fulltime baan.\n\nEen tijdlang was ik de enige. Nu niet meer — negentien mensen maakten deze release, en duizenden van jullie kwamen erbij met sterren, issues, vertalingen en pull requests. Ik ben dankbaar voor elk stuk ervan.',
|
||||
'system_notice.release_400.promise_label': 'De belofte',
|
||||
'system_notice.release_400.promise_text':
|
||||
'De open source-kant van TREK blijft gratis, voor altijd. Geen betaalde versies, geen abonnementen, geen addertjes. Beloofd.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0 kostte weken aan late avonden — een mobiele app, een fotoboekontwerper, een servermigratie, het meeste geschreven tussen middernacht en twee uur. Geen klacht: ik bouw dit graag. Het is gewoon het eerlijke antwoord op hoe een release van deze omvang uit een vrijetijdsproject komt.',
|
||||
'system_notice.release_400.note_closing': 'Bedankt dat je er bent.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'Steun is wat dit draaiende houdt — servers, domeinen en de late avonden die uitmonden in releases als deze. Als TREK iets voor je waard is, is een kopje koffie de meest directe manier om het door te laten gaan.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Steun op Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -54,5 +54,41 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.v3014_whitespace_collision.title': 'Wymagane działanie: konflikt konta użytkownika',
|
||||
'system_notice.v3014_whitespace_collision.body':
|
||||
'Aktualizacja 3.0.14 wykryła jeden lub więcej konfliktów nazwy użytkownika lub adresu e-mail spowodowanych spacjami na początku lub końcu przechowywanych wartości. Dotknięte konta zostały automatycznie przemianowane. Sprawdź logi serwera pod kątem wierszy zaczynających się od **[migration] WHITESPACE COLLISION**, aby zidentyfikować konta wymagające przeglądu.',
|
||||
// 4.0.0 release modal — the release on the left, the note from the maintainer on the right
|
||||
'system_notice.release_400.eyebrow': 'Aktualizacja zainstalowana',
|
||||
'system_notice.release_400.tag': 'Wydanie',
|
||||
'system_notice.release_400.headline': 'Największe wydanie w historii TREK.',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK dostaje telefon i książkę. To wydanie napisało dziewiętnaście osób — a poszło z nim około stu pięćdziesięciu zgłoszonych błędów.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK na telefonie',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Wszystko poniżej 768px ma teraz własny interfejs — szklany dok, własne panele, własny planer podróży. Otwórz TREK na telefonie.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'PDF z Journey stał się projektantem fotoksiążki. Układa książkę, kiedy go o to poprosisz, a potem schodzi z drogi.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay uczy się reszty',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Pół dnia, dni wolne za nadgodziny i dni flex, ferie szkolne na siatce — i rok urlopowy, który nie musi zaczynać się w styczniu.',
|
||||
'system_notice.release_400.feature_places_title': 'Miejsca się pokazują, pliki wyprowadzają',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Zdjęcia i opis uzupełniają się same, zanim zapiszesz miejsce. A Twoje pliki nie muszą już leżeć na dysku, na którym działa TREK.',
|
||||
'system_notice.release_400.footnote':
|
||||
'A to tylko cztery z nich. 4.0.0 niesie kilkaset kolejnych zmian, od Collections i Atlas po cały serwer pod spodem.',
|
||||
'system_notice.release_400.note_eyebrow': 'Słowo od autora',
|
||||
'system_notice.release_400.note_title': 'Dziękuję, że korzystasz z TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREK zaczął się jako małe narzędzie na moje własne podróże, pisane po godzinach. Nadal takie jest: wieczory, weekendy, godziny obok pracy na pełny etat.\n\nPrzez jakiś czas byłem w tym sam. Już nie — to wydanie dowiozło dziewiętnaście osób, a tysiące z was przyszły z gwiazdkami, zgłoszeniami, tłumaczeniami i pull requestami. Jestem wdzięczny za każdy kawałek tego.',
|
||||
'system_notice.release_400.promise_label': 'Obietnica',
|
||||
'system_notice.release_400.promise_text':
|
||||
'Otwartoźródłowa część TREK pozostaje darmowa, na zawsze. Bez płatnych wersji, bez subskrypcji, bez haczyków. Obiecuję.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0 kosztowało tygodnie nocnych sesji — aplikacja na telefon, projektant książek, migracja serwera, większość pisana między północą a drugą. To nie skarga: uwielbiam to budować. To po prostu szczera odpowiedź na to, jak z projektu po godzinach wychodzi wydanie tej wielkości.',
|
||||
'system_notice.release_400.note_closing': 'Dziękuję, że tu jesteś.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'Wsparcie jest tym, co to wszystko utrzymuje — serwery, domeny i nocne sesje, które zamieniają się w wydania takie jak to. Jeśli TREK jest dla Ciebie coś wart, kawa to najprostszy sposób, żeby to podtrzymać.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Wesprzyj na Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -54,5 +54,41 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.v3014_whitespace_collision.title': 'Требуется действие: конфликт учётных записей',
|
||||
'system_notice.v3014_whitespace_collision.body':
|
||||
'Обновление 3.0.14 обнаружило один или несколько конфликтов имён пользователей или адресов электронной почты, вызванных ведущими или завершающими пробелами в сохранённых значениях. Затронутые учётные записи были автоматически переименованы. Проверьте логи сервера на строки, начинающиеся с **[migration] WHITESPACE COLLISION**, чтобы определить учётные записи, требующие проверки.',
|
||||
// 4.0.0 release modal — the release on the left, the note from the maintainer on the right
|
||||
'system_notice.release_400.eyebrow': 'Обновление установлено',
|
||||
'system_notice.release_400.tag': 'Релиз',
|
||||
'system_notice.release_400.headline': 'Самый большой релиз в истории TREK.',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK получает телефон и книгу. Этот релиз написали девятнадцать человек — и вместе с ним закрылись около ста пятидесяти присланных багов.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK на телефоне',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Всё, что уже 768px, теперь отдельный интерфейс — стеклянный док, свои шторки, свой планировщик поездок. Откройте TREK на телефоне.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'PDF из Journey превратился в конструктор фотокниги. Он собирает книгу, когда вы попросите, и дальше не мешает.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay освоил остальное',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Полдня, отгулы и гибкие дни, школьные каникулы в сетке — и отпускной год, который не обязан начинаться в январе.',
|
||||
'system_notice.release_400.feature_places_title': 'Места показывают себя, файлы съезжают',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Фото и описание подставляются сами ещё до того, как вы сохраните место. А загруженные файлы больше не обязаны лежать на диске, где работает TREK.',
|
||||
'system_notice.release_400.footnote':
|
||||
'И это только четыре из них. В 4.0.0 ещё несколько сотен изменений — от Collections и Atlas до всего сервера под ними.',
|
||||
'system_notice.release_400.note_eyebrow': 'Слово от мейнтейнера',
|
||||
'system_notice.release_400.note_title': 'Спасибо, что пользуетесь TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREK начинался как маленький инструмент для моих собственных поездок, написанный в свободное время. Таким он и остался: вечера, выходные, часы рядом с основной работой.\n\nКакое-то время я был один. Уже нет — этот релиз выпустили девятнадцать человек, а тысячи из вас пришли со звёздами, issue, переводами и пул-реквестами. Я благодарен за каждую часть этого.',
|
||||
'system_notice.release_400.promise_label': 'Обещание',
|
||||
'system_notice.release_400.promise_text':
|
||||
'Открытая часть TREK остаётся бесплатной навсегда. Никаких платных тарифов, никаких подписок, никаких подвохов. Обещаю.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0 стоил недель поздних ночей — приложение для телефона, конструктор книги, миграция сервера, и почти всё это написано между полуночью и двумя. Это не жалоба: мне нравится это делать. Просто честный ответ на то, как релиз такого размера выходит из проекта в свободное время.',
|
||||
'system_notice.release_400.note_closing': 'Спасибо, что вы здесь.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'Поддержка — это то, на чём всё держится: серверы, домены и те поздние ночи, из которых получаются такие релизы. Если TREK для вас чего-то стоит, кофе — самый прямой способ помочь.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Поддержать на Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -54,5 +54,41 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.pager.counter': '{current} / {total}',
|
||||
'system_notice.pager.goto': 'Gå till meddelandet {n}',
|
||||
'system_notice.pager.position': 'Meddlenade {current} av {total}',
|
||||
// 4.0.0 release modal — the release on the left, the note from the maintainer on the right
|
||||
'system_notice.release_400.eyebrow': 'Uppdatering klar',
|
||||
'system_notice.release_400.tag': 'Utgåva',
|
||||
'system_notice.release_400.headline': 'Den största utgåvan TREK har haft.',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK får en telefon, och en bok. Nitton personer skrev den här — och ungefär hundrafemtio rapporterade fel följde med.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK blir mobilt',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Allt under 768px är ett eget gränssnitt nu — en dock i glas, egna paneler, en egen resplanerare. Öppna TREK i telefonen.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'Journey-PDF:en blev en fotoboksdesigner. Den lägger ut boken när du ber om det, och håller sig sedan undan.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay lär sig resten',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Halvdagar, komp- och flexdagar, skollov i rutnätet — och ett semesterår som inte måste börja i januari.',
|
||||
'system_notice.release_400.feature_places_title': 'Platser visar sig, filer flyttar ut',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Bilder och en beskrivning fyller i sig själva innan du sparar en plats. Och dina uppladdningar behöver inte längre ligga på disken TREK kör på.',
|
||||
'system_notice.release_400.footnote':
|
||||
'Och det här är fyra av dem. 4.0.0 innehåller flera hundra ytterligare ändringar, från Collections och Atlas till hela servern under.',
|
||||
'system_notice.release_400.note_eyebrow': 'Ett ord från utvecklaren',
|
||||
'system_notice.release_400.note_title': 'Tack för att du använder TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREK började som ett litet verktyg för mina egna resor, skrivet på fritiden. Det är det fortfarande: kvällar, helger, timmarna vid sidan av ett heltidsjobb.\n\nEtt tag var det bara jag. Inte längre — nitton personer levererade den här utgåvan, och tusentals av er kom med stjärnor, ärenden, översättningar och pull requests. Jag är tacksam för varenda del av det.',
|
||||
'system_notice.release_400.promise_label': 'Löftet',
|
||||
'system_notice.release_400.promise_text':
|
||||
'Den öppna delen av TREK är gratis, för alltid. Inga betalnivåer, inga prenumerationer, inga förbehåll. Lovat.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0 tog veckor av sena nätter — en mobilapp, en fotoboksdesigner, en servermigrering, det mesta skrivet mellan midnatt och två. Inget gnäll: jag älskar att bygga det här. Det är bara det ärliga svaret på hur en utgåva av den här storleken kommer ur ett fritidsprojekt.',
|
||||
'system_notice.release_400.note_closing': 'Tack för att du är här.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'Stödet är det som håller igång det här — servrar, domäner och de sena nätter som blir till utgåvor som den här. Om TREK är värt något för dig är en kaffe det mest direkta sättet att hålla det vid liv.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Stöd på Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -54,5 +54,40 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.pager.counter': '{current} / {total}',
|
||||
'system_notice.pager.goto': '{n}. bildirime git',
|
||||
'system_notice.pager.position': '{total} Bildirimden {current}.',
|
||||
'system_notice.release_400.eyebrow': 'Güncelleme yüklendi',
|
||||
'system_notice.release_400.tag': 'Sürüm',
|
||||
'system_notice.release_400.headline': "TREK'in bugüne kadarki en büyük sürümü.",
|
||||
'system_notice.release_400.intro':
|
||||
'TREK bir telefon ve bir kitap kazanıyor. Bunu on dokuz kişi yazdı — ve yaklaşık yüz elli bildirilen hata da onunla birlikte gitti.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK mobile geçiyor',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
"768px altındaki her şey artık kendi arayüzü — cam bir dock, kendi panelleri, kendi seyahat planlayıcısı. TREK'i telefonunda aç.",
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
"Journey PDF'i bir fotoğraf kitabı tasarımcısına dönüştü. İstediğinde kitabı kendisi düzenler, sonra yoldan çekilir.",
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay gerisini öğreniyor',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Yarım günler, telafi ve esnek günler, takvimde okul tatilleri — ve ocakta başlamak zorunda olmayan bir izin yılı.',
|
||||
'system_notice.release_400.feature_places_title': 'Yerler kendini gösterir, dosyalar taşınır',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
"Bir yeri kaydetmeden önce resimler ve açıklama kendiliğinden dolar. Ve yüklemelerinin artık TREK'in çalıştığı diskte durması gerekmiyor.",
|
||||
'system_notice.release_400.footnote':
|
||||
"Ve bunlar onlardan sadece dördü. 4.0.0, Collections ve Atlas'tan alttaki sunucunun tamamına kadar birkaç yüz değişiklik daha taşıyor.",
|
||||
'system_notice.release_400.note_eyebrow': 'Geliştiriciden bir not',
|
||||
'system_notice.release_400.note_title': "TREK'i kullandığın için teşekkürler.",
|
||||
'system_notice.release_400.note_body':
|
||||
"TREK, boş zamanlarımda kendi seyahatlerim için yazdığım küçük bir araç olarak başladı. Hâlâ da öyle: akşamlar, hafta sonları, tam zamanlı bir işin yanındaki saatler.\n\nBir süre yalnızca bendim. Artık değil — bu sürümü on dokuz kişi çıkardı ve binlerceniz yıldızlarla, issue'larla, çevirilerle ve pull request'lerle geldiniz. Her biri için minnettarım.",
|
||||
'system_notice.release_400.promise_label': 'Söz',
|
||||
'system_notice.release_400.promise_text':
|
||||
"TREK'in açık kaynak tarafı sonsuza dek ücretsiz kalıyor. Ücretli paket yok, abonelik yok, gizli şart yok. Söz.",
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0, haftalarca süren geç geceler demekti — bir telefon uygulaması, bir kitap tasarımcısı, bir sunucu taşıması, çoğu gece yarısı ile ikisi arasında yazıldı. Şikâyet değil: bunu geliştirmeyi seviyorum. Sadece bu büyüklükte bir sürümün boş zaman projesinden nasıl çıktığının dürüst cevabı.',
|
||||
'system_notice.release_400.note_closing': 'Burada olduğun için teşekkür ederim.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'Bunu ayakta tutan şey destek — sunucular, alan adları ve böyle sürümlere dönüşen geç geceler. TREK senin için bir şey ifade ediyorsa, bir kahve bunu sürdürmenin en doğrudan yolu.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': "Ko-fi'de Destek Ol",
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -54,5 +54,41 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.v3014_whitespace_collision.title': 'Потрібна дія: конфлікт облікового запису користувача',
|
||||
'system_notice.v3014_whitespace_collision.body':
|
||||
'Оновлення 3.0.14 виявило один або кілька конфліктів імен користувачів або електронних адрес, спричинених початковими або кінцевими пробілами в збережених облікових записах. Уражені облікові записи було автоматично перейменовано. Перевірте журнали сервера на рядки, що починаються з **[migration] WHITESPACE COLLISION**, щоб визначити, які облікові записи потребують перевірки.',
|
||||
// 4.0.0 release modal — the release on the left, the note from the maintainer on the right
|
||||
'system_notice.release_400.eyebrow': 'Оновлення встановлено',
|
||||
'system_notice.release_400.tag': 'Реліз',
|
||||
'system_notice.release_400.headline': 'Найбільший реліз в історії TREK.',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK отримує телефон і книгу. Цей реліз написали дев’ятнадцять людей — і разом з ним закрилися близько ста п’ятдесяти надісланих багів.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK на телефоні',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Усе, що вужче за 768px, тепер окремий інтерфейс — скляний док, власні шторки, власний планувальник поїздок. Відкрийте TREK на телефоні.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'PDF із Journey став конструктором фотокниги. Він збирає книгу, коли ви попросите, і далі не заважає.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay освоїв решту',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Півдня, відгули та гнучкі дні, шкільні канікули в сітці — і відпускний рік, який не мусить починатися в січні.',
|
||||
'system_notice.release_400.feature_places_title': 'Місця показують себе, файли з’їжджають',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Фото й опис підставляються самі ще до того, як ви збережете місце. А завантажені файли більше не мусять лежати на диску, де працює TREK.',
|
||||
'system_notice.release_400.footnote':
|
||||
'І це лише чотири з них. У 4.0.0 ще кілька сотень змін — від Collections і Atlas до всього сервера під ними.',
|
||||
'system_notice.release_400.note_eyebrow': 'Слово від мейнтейнера',
|
||||
'system_notice.release_400.note_title': 'Дякую, що користуєтесь TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREK починався як маленький інструмент для моїх власних поїздок, написаний у вільний час. Таким і залишився: вечори, вихідні, години поруч з основною роботою.\n\nЯкийсь час я був сам. Уже ні — цей реліз випустили дев’ятнадцять людей, а тисячі з вас прийшли із зірками, issue, перекладами та пул-реквестами. Я вдячний за кожну частину цього.',
|
||||
'system_notice.release_400.promise_label': 'Обіцянка',
|
||||
'system_notice.release_400.promise_text':
|
||||
'Відкрита частина TREK залишається безкоштовною назавжди. Жодних платних тарифів, жодних підписок, жодних підводних каменів. Обіцяю.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0 коштував тижнів пізніх ночей — застосунок для телефона, конструктор книги, міграція сервера, і майже все це написано між північчю і другою. Це не скарга: мені подобається це робити. Просто чесна відповідь на те, як реліз такого розміру виходить із проєкту у вільний час.',
|
||||
'system_notice.release_400.note_closing': 'Дякую, що ви тут.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'Підтримка — це те, на чому все тримається: сервери, домени і ті пізні ночі, з яких виходять такі релізи. Якщо TREK для вас чогось вартий, кава — найпряміший спосіб допомогти.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Підтримати на Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -55,5 +55,41 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.pager.counter': '{current} / {total}',
|
||||
'system_notice.pager.goto': 'Vào thông báo {n}',
|
||||
'system_notice.pager.position': 'Thông báo {current} của {total}',
|
||||
// 4.0.0 release modal — the release on the left, the note from the maintainer on the right
|
||||
'system_notice.release_400.eyebrow': 'Đã cập nhật',
|
||||
'system_notice.release_400.tag': 'Phiên bản',
|
||||
'system_notice.release_400.headline': 'Bản phát hành lớn nhất TREK từng có.',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK có điện thoại, và một cuốn sách. Mười chín người đã viết nên bản này — cùng khoảng một trăm năm mươi lỗi được báo cáo.',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK lên di động',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'Mọi thứ dưới 768px giờ là giao diện riêng — thanh dock kính, các bảng riêng, công cụ lập kế hoạch riêng. Mở TREK trên điện thoại.',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'Bản PDF của Journey đã thành một công cụ thiết kế sách ảnh. Nó tự dàn trang khi bạn yêu cầu, rồi lui ra một bên.',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay học nốt',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'Nửa ngày, ngày bù và ngày linh hoạt, kỳ nghỉ học trên lưới — và một năm phép không nhất thiết bắt đầu từ tháng Một.',
|
||||
'system_notice.release_400.feature_places_title': 'Địa điểm tự hiện ra, tệp dọn ra ngoài',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'Ảnh và mô tả tự điền vào trước khi bạn lưu một địa điểm. Và các tệp tải lên không còn phải nằm trên ổ đĩa chạy TREK nữa.',
|
||||
'system_notice.release_400.footnote':
|
||||
'Và đây mới là bốn trong số đó. 4.0.0 mang theo vài trăm thay đổi khác, từ Collections và Atlas cho tới toàn bộ máy chủ bên dưới.',
|
||||
'system_notice.release_400.note_eyebrow': 'Lời nhắn từ người bảo trì',
|
||||
'system_notice.release_400.note_title': 'Cảm ơn bạn đã sử dụng TREK.',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREK bắt đầu như một công cụ nhỏ cho chuyến đi của riêng tôi, viết trong thời gian rảnh. Đến giờ vẫn vậy: buổi tối, cuối tuần, những giờ bên cạnh công việc toàn thời gian.\n\nĐã có lúc chỉ mình tôi. Giờ thì không nữa — mười chín người đã làm nên bản phát hành này, và hàng nghìn bạn đã đến với sao, báo lỗi, bản dịch và pull request. Tôi biết ơn tất cả.',
|
||||
'system_notice.release_400.promise_label': 'Lời hứa',
|
||||
'system_notice.release_400.promise_text':
|
||||
'Phần mã nguồn mở của TREK vẫn miễn phí, mãi mãi. Không tầng trả phí, không đăng ký, không ràng buộc. Tôi hứa.',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0 tốn nhiều tuần thức khuya — một ứng dụng điện thoại, một công cụ thiết kế sách, một lần chuyển máy chủ, phần lớn viết từ nửa đêm đến hai giờ. Không phải than phiền: tôi thích làm việc này. Đó chỉ là câu trả lời thật lòng cho việc một bản cỡ này ra đời từ một dự án lúc rảnh.',
|
||||
'system_notice.release_400.note_closing': 'Cảm ơn bạn đã ở đây.',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'Sự ủng hộ là thứ giữ cho mọi thứ chạy — máy chủ, tên miền, và những đêm khuya biến thành các bản phát hành như thế này. Nếu TREK có giá trị với bạn, một ly cà phê là cách trực tiếp nhất để duy trì nó.',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': 'Hỗ trợ trên Ko-fi',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -53,5 +53,40 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.v3014_whitespace_collision.title': '需要操作:使用者帳戶衝突',
|
||||
'system_notice.v3014_whitespace_collision.body':
|
||||
'3.0.14 版本升級偵測到一個或多個由儲存帳戶中前後空白字元引發的使用者名稱或電子郵件衝突。受影響的帳戶已自動重新命名。請檢查伺服器日誌中以 **[migration] WHITESPACE COLLISION** 開頭的行,以確認哪些帳戶需要審查。',
|
||||
// 4.0.0 release modal — the release on the left, the note from the maintainer on the right
|
||||
'system_notice.release_400.eyebrow': '更新已安裝',
|
||||
'system_notice.release_400.tag': '發布',
|
||||
'system_notice.release_400.headline': 'TREK 有史以來最大的一次更新。',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK 有了手機端,也有了相簿書。這一版由十九個人寫成——隨之解決的還有大約一百五十個回報過的 bug。',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK 上手機',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'768px 以下現在是一套獨立的介面——毛玻璃底欄、自己的彈出面板、自己的行程規劃器。用手機開啟 TREK 試試。',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'Journey 的 PDF 變成了一個相簿書設計器。你讓它排版,它就把書排好,然後退到一邊。',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay 補齊了剩下的',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'半天、補休與彈性假、日曆上的學校假期——還有不必從一月開始的休假年度。',
|
||||
'system_notice.release_400.feature_places_title': '地點自己亮相,檔案搬出去',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'在你儲存一個地點之前,圖片和描述會自己填好。上傳的檔案也不必再放在 TREK 所在的磁碟上。',
|
||||
'system_notice.release_400.footnote':
|
||||
'這只是其中四項。4.0.0 還帶來數百項其他改動,從 Collections、Atlas 一直到底層的整個伺服器。',
|
||||
'system_notice.release_400.note_eyebrow': '來自維護者的話',
|
||||
'system_notice.release_400.note_title': '感謝你使用 TREK。',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREK 最初只是我為自己的旅行、用業餘時間做的一個小工具。現在依然如此:晚上、週末,全職工作之外的那些時間。\n\n有一陣子只有我一個人。現在不是了——十九個人一起做出了這一版,還有成千上萬的你們帶著星標、issue、翻譯和 pull request 來到這裡。這一切我都心懷感激。',
|
||||
'system_notice.release_400.promise_label': '承諾',
|
||||
'system_notice.release_400.promise_text': 'TREK 的開源部分永遠免費。沒有付費方案,沒有訂閱,沒有任何套路。我保證。',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0 花掉了好幾個星期的深夜——一個手機端、一個相簿書設計器、一次伺服器遷移,大多寫在午夜到兩點之間。這不是抱怨:我喜歡做這件事。只是想誠實地說明,這麼大的一個版本是怎麼從一個業餘專案裡出來的。',
|
||||
'system_notice.release_400.note_closing': '謝謝你來到這裡。',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'是這些支持讓它繼續跑著——伺服器、網域,還有那些變成這樣一個版本的深夜。如果 TREK 對你有價值,請我喝杯咖啡是最直接的支持方式。',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': '在 Ko-fi 上支持我',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -52,5 +52,40 @@ const system_notice: TranslationStrings = {
|
||||
'system_notice.v3014_whitespace_collision.title': '需要操作:用户账户冲突',
|
||||
'system_notice.v3014_whitespace_collision.body':
|
||||
'3.0.14 版本升级检测到一个或多个由存储账户中首尾空白字符引发的用户名或邮箱冲突。受影响的账户已自动重命名。请检查服务器日志中以 **[migration] WHITESPACE COLLISION** 开头的行,以确认哪些账户需要审查。',
|
||||
// 4.0.0 release modal — the release on the left, the note from the maintainer on the right
|
||||
'system_notice.release_400.eyebrow': '更新已安装',
|
||||
'system_notice.release_400.tag': '发布',
|
||||
'system_notice.release_400.headline': 'TREK 有史以来最大的一次更新。',
|
||||
'system_notice.release_400.intro':
|
||||
'TREK 有了手机端,也有了相册书。这一版由十九个人写成——随之解决的还有大约一百五十个报告过的 bug。',
|
||||
'system_notice.release_400.feature_mobile_title': 'TREK 上手机',
|
||||
'system_notice.release_400.feature_mobile_body':
|
||||
'768px 以下现在是一套独立的界面——毛玻璃底栏、自己的弹出面板、自己的行程规划器。用手机打开 TREK 试试。',
|
||||
'system_notice.release_400.feature_studio_title': 'TREK Studio',
|
||||
'system_notice.release_400.feature_studio_badge': 'Beta',
|
||||
'system_notice.release_400.feature_studio_body':
|
||||
'Journey 的 PDF 变成了一个相册书设计器。你让它排版,它就把书排好,然后退到一边。',
|
||||
'system_notice.release_400.feature_vacay_title': 'Vacay 补齐了剩下的',
|
||||
'system_notice.release_400.feature_vacay_body':
|
||||
'半天、调休和弹性假、日历上的学校假期——还有不必从一月开始的假期年度。',
|
||||
'system_notice.release_400.feature_places_title': '地点自己亮相,文件搬出去',
|
||||
'system_notice.release_400.feature_places_body':
|
||||
'在你保存一个地点之前,图片和描述会自己填好。上传的文件也不必再放在 TREK 所在的磁盘上。',
|
||||
'system_notice.release_400.footnote':
|
||||
'这只是其中四项。4.0.0 还带来数百项其他改动,从 Collections、Atlas 一直到底层的整个服务端。',
|
||||
'system_notice.release_400.note_eyebrow': '来自维护者的话',
|
||||
'system_notice.release_400.note_title': '感谢你使用 TREK。',
|
||||
'system_notice.release_400.note_body':
|
||||
'TREK 最初只是我为自己的旅行、用业余时间做的一个小工具。现在依然如此:晚上、周末,全职工作之外的那些时间。\n\n有一阵子只有我一个人。现在不是了——十九个人一起做出了这一版,还有成千上万的你们带着星标、issue、翻译和 pull request 来到这里。这一切我都心怀感激。',
|
||||
'system_notice.release_400.promise_label': '承诺',
|
||||
'system_notice.release_400.promise_text': 'TREK 的开源部分永远免费。没有付费档位,没有订阅,没有套路。我保证。',
|
||||
'system_notice.release_400.note_body_after':
|
||||
'4.0.0 花掉了好几个星期的深夜——一个手机端、一个相册书设计器、一次服务端迁移,大多写在午夜到两点之间。这不是抱怨:我喜欢做这件事。只是想诚实地说明,这么大的一个版本是怎么从一个业余项目里出来的。',
|
||||
'system_notice.release_400.note_closing': '谢谢你来到这里。',
|
||||
'system_notice.release_400.note_signature': '— Maurice',
|
||||
'system_notice.release_400.support_text':
|
||||
'是这些支持让它继续跑着——服务器、域名,还有那些变成这样一个版本的深夜。如果 TREK 对你有价值,请我喝杯咖啡是最直接的支持方式。',
|
||||
'system_notice.release_400.cta_bmc': 'Buy me a coffee',
|
||||
'system_notice.release_400.cta_kofi': '在 Ko-fi 上支持',
|
||||
};
|
||||
export default system_notice;
|
||||
|
||||
@@ -52,6 +52,73 @@ describe('systemNoticeDtoSchema', () => {
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('accepts a release notice and keeps its optional pieces optional', () => {
|
||||
const base = {
|
||||
id: 'release-4-0-0',
|
||||
display: 'modal' as const,
|
||||
severity: 'info' as const,
|
||||
titleKey: 't',
|
||||
bodyKey: 'b',
|
||||
dismissible: true,
|
||||
release: {
|
||||
version: '4.0.0',
|
||||
eyebrowKey: 'e',
|
||||
tagKey: 'tag',
|
||||
headlineKey: 'h',
|
||||
introKey: 'i',
|
||||
features: [
|
||||
{ iconName: 'Smartphone', titleKey: 'f1t', bodyKey: 'f1b' },
|
||||
{ iconName: 'BookOpen', titleKey: 'f2t', bodyKey: 'f2b', badgeKey: 'beta' },
|
||||
],
|
||||
note: {
|
||||
eyebrowKey: 'ne',
|
||||
titleKey: 'nt',
|
||||
bodyKey: 'nb',
|
||||
promiseLabelKey: 'pl',
|
||||
promiseTextKey: 'pt',
|
||||
bodyAfterKey: 'nba',
|
||||
closingKey: 'nc',
|
||||
signatureKey: 'ns',
|
||||
},
|
||||
supportTextKey: 's',
|
||||
},
|
||||
};
|
||||
expect(systemNoticeDtoSchema.safeParse(base).success).toBe(true);
|
||||
|
||||
const withExtras = {
|
||||
...base,
|
||||
release: {
|
||||
...base.release,
|
||||
stats: [{ value: '~150', labelKey: 'bugs' }],
|
||||
notes: { labelKey: 'notes', href: 'https://example.test' },
|
||||
footnoteKey: 'fn',
|
||||
},
|
||||
};
|
||||
expect(systemNoticeDtoSchema.safeParse(withExtras).success).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects a release block missing the maintainer note', () => {
|
||||
expect(
|
||||
systemNoticeDtoSchema.safeParse({
|
||||
id: 'release-x',
|
||||
display: 'modal',
|
||||
severity: 'info',
|
||||
titleKey: 't',
|
||||
bodyKey: 'b',
|
||||
dismissible: true,
|
||||
release: {
|
||||
version: '4.0.0',
|
||||
eyebrowKey: 'e',
|
||||
tagKey: 'tag',
|
||||
headlineKey: 'h',
|
||||
introKey: 'i',
|
||||
features: [],
|
||||
supportTextKey: 's',
|
||||
},
|
||||
}).success,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('rejects an unknown display value and a malformed CTA', () => {
|
||||
expect(
|
||||
systemNoticeDtoSchema.safeParse({
|
||||
|
||||
@@ -42,6 +42,52 @@ const noticeCtaSchema = z.discriminatedUnion('kind', [
|
||||
}),
|
||||
]);
|
||||
|
||||
/** One "what changed" row in the release panel: icon, headline, prose, optional badge. */
|
||||
const noticeReleaseFeatureSchema = z.object({
|
||||
iconName: z.string(),
|
||||
titleKey: z.string(),
|
||||
bodyKey: z.string(),
|
||||
badgeKey: z.string().optional(),
|
||||
});
|
||||
|
||||
/** A number worth stating next to the release — the figure itself is not translated. */
|
||||
const noticeReleaseStatSchema = z.object({
|
||||
value: z.string(),
|
||||
labelKey: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* The release layout: a two-column modal, the release on the left and a note
|
||||
* from the maintainer on the right. A notice carrying this renders through
|
||||
* ReleaseNoticeModal instead of the generic notice body, so every string it
|
||||
* shows lives here rather than inside the component.
|
||||
*/
|
||||
const noticeReleaseSchema = z.object({
|
||||
/** Shown as the display figure ("4.0.0") — a version string, not a semver gate. */
|
||||
version: z.string(),
|
||||
eyebrowKey: z.string(),
|
||||
tagKey: z.string(),
|
||||
headlineKey: z.string(),
|
||||
introKey: z.string(),
|
||||
features: z.array(noticeReleaseFeatureSchema),
|
||||
stats: z.array(noticeReleaseStatSchema).optional(),
|
||||
notes: z.object({ labelKey: z.string(), href: z.string() }).optional(),
|
||||
footnoteKey: z.string().optional(),
|
||||
note: z.object({
|
||||
eyebrowKey: z.string(),
|
||||
titleKey: z.string(),
|
||||
/** Markdown paragraphs above the promise box. */
|
||||
bodyKey: z.string(),
|
||||
promiseLabelKey: z.string(),
|
||||
promiseTextKey: z.string(),
|
||||
/** Markdown paragraphs below the promise box. */
|
||||
bodyAfterKey: z.string(),
|
||||
closingKey: z.string(),
|
||||
signatureKey: z.string(),
|
||||
}),
|
||||
supportTextKey: z.string(),
|
||||
});
|
||||
|
||||
/** The client-facing notice (server-evaluated; conditions/versioning stripped). */
|
||||
export const systemNoticeDtoSchema = z.object({
|
||||
id: z.string(),
|
||||
@@ -57,5 +103,6 @@ export const systemNoticeDtoSchema = z.object({
|
||||
secondaryCta: noticeCtaSchema.optional(),
|
||||
desktopOnly: z.boolean().optional(),
|
||||
dismissible: z.boolean(),
|
||||
release: noticeReleaseSchema.optional(),
|
||||
});
|
||||
export type SystemNoticeDto = z.infer<typeof systemNoticeDtoSchema>;
|
||||
|
||||
Reference in New Issue
Block a user