diff --git a/.changeset/silver-mirrors-repeat.md b/.changeset/silver-mirrors-repeat.md
new file mode 100644
index 00000000000..a845151cc84
--- /dev/null
+++ b/.changeset/silver-mirrors-repeat.md
@@ -0,0 +1,2 @@
+---
+---
diff --git a/packages/shared/src/utils/fastDeepMerge.ts b/packages/shared/src/utils/fastDeepMerge.ts
index 1aa08d7e0d9..617f097df3c 100644
--- a/packages/shared/src/utils/fastDeepMerge.ts
+++ b/packages/shared/src/utils/fastDeepMerge.ts
@@ -17,7 +17,7 @@ export const fastDeepMergeAndReplace = (
target[key] = new (Object.getPrototypeOf(source[key]).constructor)();
}
fastDeepMergeAndReplace(source[key], target[key]);
- } else if (Object.prototype.hasOwnProperty.call(source, key)) {
+ } else if (Object.prototype.hasOwnProperty.call(source, key) && source[key] !== undefined) {
target[key] = source[key];
}
}
diff --git a/packages/ui/src/localization/__tests__/parseLocalization.test.tsx b/packages/ui/src/localization/__tests__/parseLocalization.test.tsx
index 1843c2eb41e..bb0da39f4ad 100644
--- a/packages/ui/src/localization/__tests__/parseLocalization.test.tsx
+++ b/packages/ui/src/localization/__tests__/parseLocalization.test.tsx
@@ -37,4 +37,65 @@ describe('Localization parsing and replacing', () => {
const localizedValue = result.current.t(localizationKeys('backButton'));
expect(localizedValue).toBe('test');
});
+
+ it('falls back to English when user locale has undefined value for a key', async () => {
+ const { wrapper: Wrapper } = await createFixtures();
+ const wrapperBefore = ({ children }) => (
+
+
+ {children}
+
+
+ );
+
+ const { result } = renderHook(() => useLocalizations(), { wrapper: wrapperBefore });
+
+ // undefined value should fall back to English
+ const backButtonValue = result.current.t(localizationKeys('backButton'));
+ expect(backButtonValue).toBe(defaultResource.backButton);
+
+ // Non-undefined value should use the translation
+ const formButtonValue = result.current.t(localizationKeys('formButtonPrimary'));
+ expect(formButtonValue).toBe('Translated');
+ });
+
+ it('falls back to English for nested keys with undefined values', async () => {
+ const { wrapper: Wrapper } = await createFixtures();
+ const wrapperBefore = ({ children }) => (
+
+
+ {children}
+
+
+ );
+
+ const { result } = renderHook(() => useLocalizations(), { wrapper: wrapperBefore });
+
+ // undefined nested value should fall back to English (tokens get replaced by t())
+ const titleValue = result.current.t(localizationKeys('signIn.start.title'));
+ // The English default is 'Sign in to {{applicationName}}', tokens get replaced
+ expect(titleValue).toContain('Sign in to');
+
+ // Non-undefined nested value should use the translation
+ const subtitleValue = result.current.t(localizationKeys('signIn.start.subtitle'));
+ expect(subtitleValue).toBe('Custom subtitle');
+ });
});