Call the app by its name in every sentence that names it (#406)

APP_NAME renames an instance, but only the sign-in page, the title bar and
a few headings used it. Two dozen sentences wrote "ihasmail" into
themselves, so a renamed instance still told people to keep an ihasmail
tab open and offered to open mail links "in ihasmail".

Those sentences now take the name as {app}, which also lets a translator
put it where their language wants it. brand.ts grew useAppName() for
components and currentAppName() for the few places that build strings
outside React.

Left as they are: the Files folder "ihasmail", the Sieve script
"ihasmail" and ihasmail.org. Those name things a person can go and look
at, and renaming them would rename real data.

All nine catalogues keep their translations: the name inside each one
became the placeholder. Three of the strings had no translation before
and still fall back to English.

A test walks the sources and the catalogues so a new sentence can't
hard-code the name again.
This commit is contained in:
jcoffey
2026-09-19 14:27:34 -07:00
committed by GitHub
parent bc366ac047
commit 07b39eb9b6
25 changed files with 346 additions and 225 deletions
+3 -1
View File
@@ -1,4 +1,5 @@
import { useMemo, useState } from "react";
import { useAppName } from "@/lib/brand";
import { Clock } from "lucide-react";
import { Dialog } from "@/ui/dialog";
import { DateTimeField } from "@/ui/datefield";
@@ -33,6 +34,7 @@ export function ScheduleDialog({ open, maxMs, initial, onClose, onPick }: {
onClose: () => void;
onPick: (at: Date) => void;
}) {
const appName = useAppName();
const [value, setValue] = useState(() => toInputDateTime(initial ? new Date(initial) : roundToNext(new Date(Date.now() + 3_600_000), 15)));
const at = fromInputDateTime(value);
const error = scheduleError(at, new Date(), maxMs);
@@ -58,7 +60,7 @@ export function ScheduleDialog({ open, maxMs, initial, onClose, onPick }: {
<p className="hint" style={{ color: "var(--danger)" }}>{error}</p>
) : (
<p className="hint">
{`${t("The message waits on the server, so it goes out whether or not ihasmail is open.")}${maxMs > 0 ? ` ${t("This server holds a message for up to {span}.", { span: describeSpan(maxMs) })}` : ""}`}
{`${t("The message waits on the server, so it goes out whether or not {app} is open.", { app: appName })}${maxMs > 0 ? ` ${t("This server holds a message for up to {span}.", { span: describeSpan(maxMs) })}` : ""}`}
</p>
)}
</Dialog>