diff --git a/.changeset/entitlement-dialog-i18n-and-envelope.md b/.changeset/entitlement-dialog-i18n-and-envelope.md new file mode 100644 index 0000000000..59c83456b6 --- /dev/null +++ b/.changeset/entitlement-dialog-i18n-and-envelope.md @@ -0,0 +1,24 @@ +--- +"@object-ui/app-shell": patch +"@object-ui/i18n": patch +--- + +Localize the environment entitlement dialog and read cloud's nested error envelope. + +The free-plan "Development environments are a paid feature" prompt was built from +English string literals in `entitlements.ts` — including the lowercase `your free +plan` sentence users reported (cloud#959). Both spec builders now take a translator +and resolve `environment.entitlement.*`; all ten locale packs carry the strings. +`entitlements.ts` stays dependency-free: `t` is passed in, not imported, and +defaults to the English copy with local `{{token}}` interpolation. + +The dialog now renders the Console's own copy rather than the server's prose — a +control plane upgrades independently and only localizes these messages from +cloud#959 on, so preferring the server string left the reactive path English +against every older deployment. + +Also fixes the reactive dialog not firing at all: cloud#948 moved coded errors into +a nested envelope (`{ success, error: { code, … } }`), and +`entitlementDialogFromError` read `code` off the top level — returning `null` for +every entitlement 403, so the upgrade dialog degraded to a generic red error toast. +Both shapes are read now. diff --git a/packages/app-shell/src/environment/EnvironmentEntitlementDialog.tsx b/packages/app-shell/src/environment/EnvironmentEntitlementDialog.tsx index 46516db67c..dade77c70f 100644 --- a/packages/app-shell/src/environment/EnvironmentEntitlementDialog.tsx +++ b/packages/app-shell/src/environment/EnvironmentEntitlementDialog.tsx @@ -25,6 +25,7 @@ import { AlertDialogCancel, Button, } from '@object-ui/components'; +import { useObjectTranslation } from '@object-ui/i18n'; import type { EntitlementCta, EntitlementDialogSpec } from './entitlements'; export interface EntitlementDialogState { @@ -78,6 +79,9 @@ interface Props { export function EnvironmentEntitlementDialog({ state, apiBase, onOpenChange }: Props) { const spec = state.spec; + // Title / body / CTA labels arrive already localized on the spec; the dismiss + // button is the dialog's own chrome, so it resolves its label here. + const { t } = useObjectTranslation(); return ( { if (!open) onOpenChange(false); }}> @@ -86,7 +90,7 @@ export function EnvironmentEntitlementDialog({ state, apiBase, onOpenChange }: P {spec?.message} - Close + {t('common.close', { defaultValue: 'Close' })} {spec?.secondaryCta && ( onOpenChange(false)} /> )} diff --git a/packages/app-shell/src/environment/EnvironmentListToolbar.tsx b/packages/app-shell/src/environment/EnvironmentListToolbar.tsx index 1b0b59a7f6..ca967220e8 100644 --- a/packages/app-shell/src/environment/EnvironmentListToolbar.tsx +++ b/packages/app-shell/src/environment/EnvironmentListToolbar.tsx @@ -93,7 +93,7 @@ export function EnvironmentListToolbar({ actions, entitlements, onUpgrade }: Pro // onUpgrade sets parent state. useEffect(() => { if (autoRunCreate && ctaKind === 'upgrade_for_development' && entitlements) { - onUpgrade(upgradeDialogSpec(entitlements)); + onUpgrade(upgradeDialogSpec(entitlements, t)); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [autoRunCreate, ctaKind]); @@ -114,7 +114,7 @@ export function EnvironmentListToolbar({ actions, entitlements, onUpgrade }: Pro )}