Translate Hook
All-in-one function to translate text.
useTranslations() returns t, a function for translating plain strings in your UI.
import { useTranslations } from "@fuma-translate/react";
function HomePage() {
const t = useTranslations();
return <h1>{t("Hello")}</h1>;
}- looks up the translations from
TranslationProvider. - falls back to the original text when no translation is defined.
- handles variable placeholders.
Variables
Pass dynamic values with variables. Placeholder names in the string must match the keys you provide:
t("Hello {user}", { variables: { user: "Ada" } });
// => "Hello Ada"Translators can reorder placeholders in the translated string.
Notes
Use note to disambiguate the same text in different contexts. The compiler encodes it into the translation key as text(note):
t("Close", { note: "dialog button" });
// key: "Close(dialog button)"You can also set a hook-level note. All t() calls from that hook inherit it:
function Dialog() {
const t = useTranslations({ note: "text in a dialog" });
return <h1>{t("Hello")}</h1>;
// key: "Hello(text in a dialog)"
}Per-call note values are appended after the hook-level note.
Translation Keys
The compiler extracts keys from static t("...") calls.
export type Translations = {
Hello: string;
"Hello {user}": string;
"Close(dialog button)": string;
};Translation keys must be statically analyzable. Dynamic keys such as t(key) or t(`Hello ${name}`) will fail at compile time.
Comment Annotations
Use a // @fuma-translate comment when you need the compiler to extract a translation from a compatible wrapper function or component.
The annotation must be immediately above the call or JSX element:
function Label({ text, note }: { text: string; note?: string }) {
return <span data-note={note}>{text}</span>;
}
function legacyTranslate(text: string, options?: { note?: string }) {
return text;
}
export function Sidebar() {
// @fuma-translate
return legacyTranslate("Open menu", { note: "sidebar button" });
}
export function Header() {
return (
// @fuma-translate
<Label text="Open menu" note="header button" />
);
}Inside a JSX fragment, use a JSX comment:
export function Footer() {
return (
<>
{/* @fuma-translate */}
<Label text="Contact us" />
</>
);
}As Component
Use <T /> when you want JSX syntax instead of calling t() or t.jsx() manually.
import { T } from "@fuma-translate/react";
function WelcomeMessage() {
return <T text="Hello {user}" variables={{ user: <strong>Ada</strong> }} />;
}<T /> accepts the same rich text options as t.jsx():
function LoginHint() {
return (
<T
text="Click <login>here</login> or <signup/>."
tags={{
login: (children) => <a href="/login">{children}</a>,
signup: () => <a href="/signup">create an account</a>,
}}
/>
);
}Add note to disambiguate reused text:
<T text="Close" note="dialog button" />
// key: "Close(dialog button)"The compiler only extracts <T /> components imported from @fuma-translate/react. Aliased imports are supported:
import { T as Translate } from "@fuma-translate/react";
<Translate text="Read docs" />;text and note must be static strings, and spread attributes are not supported.
React Server Components
<T /> can be rendered from React Server Components. When passing rich text tags across the RSC boundary, prefer React elements instead of render functions:
import { T } from "@fuma-translate/react";
export default function Page() {
return (
<T
text="Read <docs>the docs</docs>"
tags={{
docs: <a href="/docs" />,
}}
/>
);
}JSX
Use t.jsx() when a translation needs React/JSX nodes, such as links, buttons, bold text, or other markup. It works like t(), but returns JSX nodes instead of a plain string.
Variables as React nodes
{variable} placeholders accept React nodes in variables:
function WelcomeMessage() {
const t = useTranslations();
return (
<p>
{t.jsx("Hello {user}", {
variables: {
user: <strong>Ada</strong>,
},
})}
</p>
);
}Translators can reorder {user} in the translated string. The runtime substitutes each placeholder with the matching React node.
Placeholder tags
For rich text, write placeholder tags in the message and map each tag to a render function in tags:
function LoginHint() {
const t = useTranslations();
return (
<p>
{t.jsx("Click <login>here</login> or <signup/>.", {
tags: {
login: (children) => <a href="/login">{children}</a>,
signup: () => <a href="/signup">create an account</a>,
},
})}
</p>
);
}- Paired tags:
<login>here</login>passes the inner content to the render function aschildren. - Self-closing tags:
<signup/>calls the render function with no children, useful for fixed elements like buttons.
Tags can be nested:
t.jsx("Read <bold><link>docs</link></bold>", {
tags: {
bold: (children) => <b>{children}</b>,
link: (children) => <a href="/docs">{children}</a>,
},
});If a tag has no matching entry in tags, its children are rendered as plain text.
Escaping
Prefix { or < with a backslash to render them literally:
t.jsx("Use \\{brackets} and \\<tags>");
// renders: Use {brackets} and <tags>