move Untranslated into shared components, and add product-name

The panels need it -- they wrap Jellyfin, Soulseek, Gitea, Transmission and name
tools like Tmux and Neovim -- and it was in officer-web/lib, which officerdev cannot
import. components/ is the workspace both already depend on, and the file carries no
strings, so the phase-1 guard stays green.

`reason` gains 'product-name' alongside 'foreign-ui-label'. The union is closed on
purpose and its own comment asks for a second reason to be argued rather than
smuggled in: a product's NAME is not a label quoted from its UI, and the doc's
never-translate list covers it directly under "product names generally".
This commit is contained in:
2026-08-23 14:09:58 +00:00
parent 17dc9719e9
commit 43197745f1
@@ -0,0 +1,28 @@
import type { ReactNode } from 'react';
type UntranslatedProps = {
children: ReactNode;
/** Why this text is not localized. Required so the decision is legible at the call site. */
reason: 'foreign-ui-label';
className?: string;
};
/**
* Text that is deliberately NOT localized, marked as such where it is written.
*
* The only reason so far is `foreign-ui-label`: a label quoted from another product's interface, which
* docs/localization.md classifies as an identifier rather than prose. "Click **Enable**" has to match
* what the reader sees on Google's screen, and Google localises by their Google account language rather
* than by ours — so translating it sends somebody hunting for "Ativar" in a console that says "Enable".
*
* It exists as a component rather than a comment for two reasons. It states the decision at every call
* site, where the next person editing the sentence will see it. And it gives the scanner something exact
* to skip: without that, ~40 labels that MUST stay English count as debt forever, and the area can never
* be held at zero — the same trap as counting `new Error()` messages.
*
* `reason` is a closed union on purpose. A second legitimate reason should be argued for and added here,
* not smuggled in as free text.
*/
export const Untranslated = ({ children, className }: UntranslatedProps) => (
<strong className={className}>{children}</strong>
);