Skip to content

Commit 6f5f4d2

Browse files
committed
chore(shared,ui): Fix locale fallback behavior
1 parent 40e771d commit 6f5f4d2

File tree

3 files changed

+64
-1
lines changed

3 files changed

+64
-1
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

packages/shared/src/utils/fastDeepMerge.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const fastDeepMergeAndReplace = (
1717
target[key] = new (Object.getPrototypeOf(source[key]).constructor)();
1818
}
1919
fastDeepMergeAndReplace(source[key], target[key]);
20-
} else if (Object.prototype.hasOwnProperty.call(source, key)) {
20+
} else if (Object.prototype.hasOwnProperty.call(source, key) && source[key] !== undefined) {
2121
target[key] = source[key];
2222
}
2323
}

packages/ui/src/localization/__tests__/parseLocalization.test.tsx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,65 @@ describe('Localization parsing and replacing', () => {
3737
const localizedValue = result.current.t(localizationKeys('backButton'));
3838
expect(localizedValue).toBe('test');
3939
});
40+
41+
it('falls back to English when user locale has undefined value for a key', async () => {
42+
const { wrapper: Wrapper } = await createFixtures();
43+
const wrapperBefore = ({ children }) => (
44+
<Wrapper>
45+
<OptionsProvider
46+
value={{
47+
localization: {
48+
backButton: undefined, // Explicitly undefined should fall back to English
49+
formButtonPrimary: 'Translated', // Non-undefined should override
50+
},
51+
}}
52+
>
53+
{children}
54+
</OptionsProvider>
55+
</Wrapper>
56+
);
57+
58+
const { result } = renderHook(() => useLocalizations(), { wrapper: wrapperBefore });
59+
60+
// undefined value should fall back to English
61+
const backButtonValue = result.current.t(localizationKeys('backButton'));
62+
expect(backButtonValue).toBe(defaultResource.backButton);
63+
64+
// Non-undefined value should use the translation
65+
const formButtonValue = result.current.t(localizationKeys('formButtonPrimary'));
66+
expect(formButtonValue).toBe('Translated');
67+
});
68+
69+
it('falls back to English for nested keys with undefined values', async () => {
70+
const { wrapper: Wrapper } = await createFixtures();
71+
const wrapperBefore = ({ children }) => (
72+
<Wrapper>
73+
<OptionsProvider
74+
value={{
75+
localization: {
76+
signIn: {
77+
start: {
78+
title: undefined, // Should fall back to English
79+
subtitle: 'Custom subtitle', // Non-undefined should override
80+
},
81+
},
82+
},
83+
}}
84+
>
85+
{children}
86+
</OptionsProvider>
87+
</Wrapper>
88+
);
89+
90+
const { result } = renderHook(() => useLocalizations(), { wrapper: wrapperBefore });
91+
92+
// undefined nested value should fall back to English (tokens get replaced by t())
93+
const titleValue = result.current.t(localizationKeys('signIn.start.title'));
94+
// The English default is 'Sign in to {{applicationName}}', tokens get replaced
95+
expect(titleValue).toContain('Sign in to');
96+
97+
// Non-undefined nested value should use the translation
98+
const subtitleValue = result.current.t(localizationKeys('signIn.start.subtitle'));
99+
expect(subtitleValue).toBe('Custom subtitle');
100+
});
40101
});

0 commit comments

Comments
 (0)