Translate English built from template literals in attributes
i18n-literals now flags a template literal in a UI attribute or prop when
there are words between its values: `Remove ${email}`, `${name} — shared by
${owner}`. It cannot be a catalogue key as written, so neither the key
exemption nor the sentence-shape test applies. A template that is only
punctuation around values, like `${name} (${size})`, is left alone.
The twelve it found are now keys with placeholders: the quota bar title,
the address menu's label, a folder's subfolder unread count, a recipient
chip's remove button, the contact editor's title, shared and available
calendars and address books, the date and time fields' labels, the
attachment title's fallback name, and the free/busy bar. The bar showed
the raw JMAP busyStatus ("confirmed") and now says Busy, Tentative or
Unavailable.
11 new keys in all nine catalogues. Remove {address}, Date, Time, Busy and
Tentative already existed.
This commit is contained in:
@@ -192,7 +192,7 @@ export function CalendarSidebar() {
|
||||
{sharedSubscribed.map(({ accountId, accountName, calendar: c }) => {
|
||||
const key = `${accountId}:${c.id}`;
|
||||
return (
|
||||
<div key={key} className={`cal-list-item ${cal.hidden[key] ? "hidden-cal" : ""}`} onClick={() => cal.toggleHidden(key)} title={`${c.name} — shared by ${accountName}`}>
|
||||
<div key={key} className={`cal-list-item ${cal.hidden[key] ? "hidden-cal" : ""}`} onClick={() => cal.toggleHidden(key)} title={t("{name} — shared by {owner}", { name: c.name, owner: accountName })}>
|
||||
<span className="cal-color" style={{ background: c.color ?? "var(--accent)", borderColor: c.color ?? "var(--accent)" }} />
|
||||
<span className="cal-name">{c.name}</span>
|
||||
<button
|
||||
@@ -212,7 +212,7 @@ export function CalendarSidebar() {
|
||||
<>
|
||||
<div className="nav-section"><span>{t("Available to add")}</span></div>
|
||||
{sharedAvailable.map(({ accountId, accountName, calendar: c }) => (
|
||||
<div key={`${accountId}:${c.id}`} className="cal-list-item" title={`${c.name} — from ${accountName}`}>
|
||||
<div key={`${accountId}:${c.id}`} className="cal-list-item" title={t("{name} — from {owner}", { name: c.name, owner: accountName })}>
|
||||
<span className="cal-color" style={{ background: "transparent", borderColor: c.color ?? "var(--border-strong)" }} />
|
||||
<span className="cal-name faint">{c.name}</span>
|
||||
<button
|
||||
|
||||
@@ -34,6 +34,14 @@ export interface EditorInit {
|
||||
|
||||
const ALERT_OPTIONS = [0, 5, 10, 15, 30, 60, 120, 1440, 2880, 10080];
|
||||
|
||||
// A free/busy block's status names a JMAP value, not a word; the bar's tooltip
|
||||
// showed "confirmed" in every language until it was given one.
|
||||
const BUSY_LABEL: Record<"confirmed" | "tentative" | "unavailable", string> = {
|
||||
confirmed: "Busy",
|
||||
tentative: "Tentative",
|
||||
unavailable: "Unavailable",
|
||||
};
|
||||
|
||||
/**
|
||||
* Fields this form always sends that a single occurrence will not take.
|
||||
*
|
||||
@@ -482,7 +490,7 @@ function EventForm({ init, base, scope, editing, onClose, settingsTz, defaultAle
|
||||
const bs = Math.max(new Date(b.utcStart).getTime(), fbWindow.start.getTime());
|
||||
const be = Math.min(new Date(b.utcEnd).getTime(), fbWindow.end.getTime());
|
||||
if (be <= bs) return null;
|
||||
return <span key={i} className="fb-busy" style={pct(bs, be)} title={`${b.busyStatus}: ${formatClock(new Date(b.utcStart))} – ${formatClock(new Date(b.utcEnd))}`} />;
|
||||
return <span key={i} className="fb-busy" style={pct(bs, be)} title={translate("{status}: {start} – {end}", { status: translate(BUSY_LABEL[b.busyStatus]), start: formatClock(new Date(b.utcStart)), end: formatClock(new Date(b.utcEnd)) })} />;
|
||||
})}
|
||||
{fbHover && <span className="fb-guide" style={{ left: `${((fbHover.getTime() - fbWindow.start.getTime()) / fbWindow.span) * 100}%` }} />}
|
||||
{!allDay && <span className="fb-window" style={pct(start.getTime(), end.getTime())} />}
|
||||
|
||||
Reference in New Issue
Block a user