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 8a7ff5d42f
commit 9e3844e94a
25 changed files with 346 additions and 225 deletions
+6 -4
View File
@@ -1,4 +1,5 @@
import { useSettings } from "@/store/settings";
import { useAppName } from "@/lib/brand";
import { Switch } from "@/ui/misc";
import { browserTimeZone, listTimeZones } from "@/lib/dates";
import { toast } from "@/ui/toast";
@@ -255,6 +256,7 @@ export function GeneralSettings() {
* it can and points at the browser's own settings for the rest.
*/
function MailHandlerSettings() {
const appName = useAppName();
const support = mailtoHandlerSupport();
const [requested, setRequested] = useState(mailtoHandlerRequested);
@@ -262,7 +264,7 @@ function MailHandlerSettings() {
try {
registerMailtoHandler();
setRequested(true);
toast.success(t("Your browser will ask whether to open mail links in ihasmail"));
toast.success(t("Your browser will ask whether to open mail links in {app}", { app: appName }));
} catch (err) {
toast.error(t("Your browser refused the request: {error}", { error: (err as Error).message }));
}
@@ -275,7 +277,7 @@ function MailHandlerSettings() {
};
if (support === "unsupported") {
return <p className="hint">{tNode("This browser cannot register apps for {scheme} links. Safari, in particular, has no such API — you can still make ihasmail the default from your operating system if you install it as an app.", { scheme: <code>mailto:</code> })}</p>;
return <p className="hint">{tNode("This browser cannot register apps for {scheme} links. Safari, in particular, has no such API — you can still make {app} the default from your operating system if you install it as an app.", { scheme: <code>mailto:</code> }, { app: appName })}</p>;
}
if (support === "insecure") {
return <p className="hint">{tNode("Registering for {scheme} links requires a secure (HTTPS) connection.", { scheme: <code>mailto:</code> })}</p>;
@@ -284,7 +286,7 @@ function MailHandlerSettings() {
return (
<>
<p className="hint">
{tNode("Open {scheme} links — in web pages, documents and other apps — in ihasmail instead of a desktop mail client. Your browser will ask you to confirm, and you can change it later in its own settings (Chrome: Settings Privacy and security Site settings Protocol handlers; Firefox: Settings General Applications).", { scheme: <code>mailto:</code> })}
{tNode("Open {scheme} links — in web pages, documents and other apps — in {app} instead of a desktop mail client. Your browser will ask you to confirm, and you can change it later in its own settings (Chrome: Settings Privacy and security Site settings Protocol handlers; Firefox: Settings General Applications).", { scheme: <code>mailto:</code> }, { app: appName })}
</p>
<div className="row wrap">
<button className="btn btn-primary" onClick={ask}>{requested ? "Ask again" : "Make ihasmail the default mail app"}</button>
@@ -294,7 +296,7 @@ function MailHandlerSettings() {
{!isInstalledApp() && (
<p className="hint mt-8">
{t("For a system-wide default, install ihasmail as an app first (in Chrome: the install icon in the address bar). Your operating system can then offer ihasmail directly wherever it asks which mail app to use.")}
{t("For a system-wide default, install {app} as an app first (in Chrome: the install icon in the address bar). Your operating system can then offer {app} directly wherever it asks which mail app to use.", { app: appName })}
</p>
)}
</>