Simplify i18n: remove old react-intl artifacts, rename formatMessage to t, replace FormattedMessage with t.rich().

- Rewrite messages.ts to plain string key maps (remove MessageDescriptor)
- Rewrite useMessages hook to expose t from useTranslations() directly
- Rename formatMessage → t across 193 consumer files
- Replace custom FormattedMessage component with next-intl t.rich()
- Update 52 language files to use rich text tags (<b>, <a>)
- Remove all direct imports from @/components/messages in favor of useMessages()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mike CaoandClaude Opus 4.6 committed 2026-02-07 11:19:04 -08:00
1 parent 80cad6ea65
commit 50edb71687
247 files changed
+1660 -2194

No files matched your search

+3 -3
View File
@@ -21,20 +21,20 @@ export function ConfirmationForm({
onConfirm,
onClose,
}: ConfirmationFormProps) {
const { formatMessage, labels, getErrorMessage } = useMessages();
const { t, labels, getErrorMessage } = useMessages();
return (
<Form onSubmit={onConfirm} error={getErrorMessage(error)}>
<Box marginY="4">{message}</Box>
<FormButtons>
<Button onPress={onClose}>{formatMessage(labels.cancel)}</Button>
<Button onPress={onClose}>{t(labels.cancel)}</Button>
<FormSubmitButton
data-test="button-confirm"
isLoading={isLoading}
variant={buttonVariant}
isDisabled={false}
>
{buttonLabel || formatMessage(labels.ok)}
{buttonLabel || t(labels.ok)}
</FormSubmitButton>
</FormButtons>
</Form>
+2 -2
View File
@@ -37,7 +37,7 @@ export function DataGrid({
renderEmpty = () => <Empty />,
children,
}: DataGridProps) {
const { formatMessage, labels } = useMessages();
const { t, labels } = useMessages();
const { data, error, isLoading, isFetching } = query;
const { router, updateParams, query: queryParams } = useNavigation();
const [search, setSearch] = useState(queryParams?.search || data?.search || '');
@@ -70,7 +70,7 @@ export function DataGrid({
onSearch={handleSearch}
delay={searchDelay || DEFAULT_SEARCH_DELAY}
autoFocus={autoFocus}
placeholder={formatMessage(labels.search)}
placeholder={t(labels.search)}
/>
{renderActions?.()}
</Row>
+2 -2
View File
@@ -6,7 +6,7 @@ export interface EmptyProps {
}
export function Empty({ message }: EmptyProps) {
const { formatMessage, messages } = useMessages();
const { t, messages } = useMessages();
return (
<Row
@@ -18,7 +18,7 @@ export function Empty({ message }: EmptyProps) {
minHeight="70px"
flexGrow={1}
>
{message || formatMessage(messages.noDataAvailable)}
{message || t(messages.noDataAvailable)}
</Row>
);
}
+2 -2
View File
@@ -9,7 +9,7 @@ const logError = (error: Error, info: ErrorInfo) => {
};
export function ErrorBoundary({ children }: { children: ReactNode }) {
const { formatMessage, messages } = useMessages();
const { t, messages } = useMessages();
const fallbackRender = ({ error, resetErrorBoundary }) => {
return (
@@ -22,7 +22,7 @@ export function ErrorBoundary({ children }: { children: ReactNode }) {
justifyContent="center"
alignItems="center"
>
<h1>{formatMessage(messages.error)}</h1>
<h1>{t(messages.error)}</h1>
<h3>{error.message}</h3>
<pre>{error.stack}</pre>
<Button onClick={resetErrorBoundary}>OK</Button>
+2 -2
View File
@@ -3,14 +3,14 @@ import { useMessages } from '@/components/hooks';
import { AlertTriangle } from '@/components/icons';
export function ErrorMessage() {
const { formatMessage, messages } = useMessages();
const { t, messages } = useMessages();
return (
<Row alignItems="center" justifyContent="center" gap>
<Icon>
<AlertTriangle />
</Icon>
<Text>{formatMessage(messages.error)}</Text>
<Text>{t(messages.error)}</Text>
</Row>
);
}
+2 -2
View File
@@ -14,7 +14,7 @@ export interface FilterLinkProps extends HTMLAttributes<HTMLDivElement> {
export function FilterLink({ type, value, label, externalUrl, icon }: FilterLinkProps) {
const [showLink, setShowLink] = useState(false);
const { formatMessage, labels } = useMessages();
const { t, labels } = useMessages();
const { updateParams, query } = useNavigation();
const active = query[type] !== undefined;
const selected = query[type] === value;
@@ -29,7 +29,7 @@ export function FilterLink({ type, value, label, externalUrl, icon }: FilterLink
onMouseOut={() => setShowLink(false)}
>
{icon}
{!value && `(${label || formatMessage(labels.unknown)})`}
{!value && `(${label || t(labels.unknown)})`}
{value && (
<Text title={label || value} truncate>
<Link href={updateParams({ [type]: `eq.${value}` })} replace>
+2 -2
View File
@@ -17,10 +17,10 @@ export function PageBody({
isLoading?: boolean;
children?: ReactNode;
} & ColumnProps) {
const { formatMessage, messages } = useMessages();
const { t, messages } = useMessages();
if (error) {
return <AlertBanner title={formatMessage(messages.error)} variant="error" />;
return <AlertBanner title={t(messages.error)} variant="error" />;
}
if (isLoading) {
+3 -3
View File
@@ -11,7 +11,7 @@ export interface PagerProps {
}
export function Pager({ page, pageSize, count, onPageChange }: PagerProps) {
const { formatMessage, labels } = useMessages();
const { t, labels } = useMessages();
const maxPage = pageSize && count ? Math.ceil(+count / +pageSize) : 0;
const lastPage = page === maxPage;
const firstPage = page === 1;
@@ -34,10 +34,10 @@ export function Pager({ page, pageSize, count, onPageChange }: PagerProps) {
return (
<Row alignItems="center" justifyContent="space-between" gap="3" flexGrow={1}>
<Text>{formatMessage(labels.numberOfRecords, { x: count.toLocaleString() })}</Text>
<Text>{t(labels.numberOfRecords, { x: count.toLocaleString() })}</Text>
<Row alignItems="center" justifyContent="flex-end" gap="3">
<Text>
{formatMessage(labels.pageOf, {
{t(labels.pageOf, {
current: page.toLocaleString(),
total: maxPage.toLocaleString(),
})}
+2 -2
View File
@@ -36,7 +36,7 @@ export function Panel({
width,
...props
}: PanelProps) {
const { formatMessage, labels } = useMessages();
const { t, labels } = useMessages();
const [isFullscreen, setIsFullscreen] = useState(false);
const handleFullscreen = () => {
@@ -62,7 +62,7 @@ export function Panel({
<Button size="sm" variant="quiet" onPress={handleFullscreen}>
<Icon>{isFullscreen ? <X /> : <Maximize />}</Icon>
</Button>
<Tooltip>{formatMessage(labels.maximize)}</Tooltip>
<Tooltip>{t(labels.maximize)}</Tooltip>
</TooltipTrigger>
</Row>
)}
@@ -25,7 +25,7 @@ export function TypeConfirmationForm({
onConfirm?: () => void;
onClose?: () => void;
}) {
const { formatMessage, labels, messages, getErrorMessage } = useMessages();
const { t, labels, messages, getErrorMessage } = useMessages();
if (!confirmationValue) {
return null;
}
@@ -33,21 +33,21 @@ export function TypeConfirmationForm({
return (
<Form onSubmit={onConfirm} error={getErrorMessage(error)}>
<p>
{formatMessage(messages.actionConfirmation, {
{t(messages.actionConfirmation, {
confirmation: confirmationValue,
})}
</p>
<FormField
label={formatMessage(labels.confirm)}
label={t(labels.confirm)}
name="confirm"
rules={{ validate: value => value === confirmationValue }}
>
<TextField autoComplete="off" />
</FormField>
<FormButtons>
<Button onPress={onClose}>{formatMessage(labels.cancel)}</Button>
<Button onPress={onClose}>{t(labels.cancel)}</Button>
<FormSubmitButton isLoading={isLoading} variant={buttonVariant}>
{buttonLabel || formatMessage(labels.ok)}
{buttonLabel || t(labels.ok)}
</FormSubmitButton>
</FormButtons>
</Form>