Skip to content

Commit 907b1dd

Browse files
committed
1 parent 480b97b commit 907b1dd

File tree

5 files changed

+55
-20
lines changed

5 files changed

+55
-20
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
### 16.3.2
2+
3+
- fix: avoid "Uncaught TypeError: Cannot redefine property: \_\_original"
4+
15
### 16.3.1
26

37
- revert fix: Incosistent behaviour of Trans and t. Trans set defaultValue when t call doesn't set the field. [1876](https://github.com/i18next/react-i18next/issues/1876) [f22d478](https://github.com/i18next/react-i18next/commit/f22d4787187e6cfc54d57f5fbede1c816ea19565)

react-i18next.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3499,13 +3499,17 @@
34993499
}, [i18n, props.lng, namespaces, ready, useSuspense, loadCount]);
35003500
const finalI18n = i18n || {};
35013501
const ret = React.useMemo(() => {
3502-
const i18nWrapper = Object.create(Object.getPrototypeOf(finalI18n), Object.getOwnPropertyDescriptors(finalI18n));
3503-
Object.defineProperty(i18nWrapper, '__original', {
3504-
value: finalI18n,
3505-
writable: false,
3506-
enumerable: false,
3507-
configurable: false
3508-
});
3502+
const descriptors = Object.getOwnPropertyDescriptors(finalI18n);
3503+
if (descriptors.__original) delete descriptors.__original;
3504+
const i18nWrapper = Object.create(Object.getPrototypeOf(finalI18n), descriptors);
3505+
if (!Object.prototype.hasOwnProperty.call(i18nWrapper, '__original')) {
3506+
Object.defineProperty(i18nWrapper, '__original', {
3507+
value: finalI18n,
3508+
writable: false,
3509+
enumerable: false,
3510+
configurable: false
3511+
});
3512+
}
35093513
const arr = [t, i18nWrapper, ready];
35103514
arr.t = t;
35113515
arr.i18n = i18nWrapper;

react-i18next.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/useTranslation.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -129,18 +129,20 @@ export const useTranslation = (ns, props = {}) => {
129129
const finalI18n = i18n || {};
130130

131131
const ret = useMemo(() => {
132-
const i18nWrapper = Object.create(
133-
Object.getPrototypeOf(finalI18n),
134-
Object.getOwnPropertyDescriptors(finalI18n),
135-
);
136-
137-
// Store reference to the original instance for tests/debugging
138-
Object.defineProperty(i18nWrapper, '__original', {
139-
value: finalI18n,
140-
writable: false,
141-
enumerable: false,
142-
configurable: false,
143-
});
132+
// Copy descriptors but avoid carrying over any existing "__original"
133+
const descriptors = Object.getOwnPropertyDescriptors(finalI18n);
134+
if (descriptors.__original) delete descriptors.__original;
135+
const i18nWrapper = Object.create(Object.getPrototypeOf(finalI18n), descriptors);
136+
137+
// Store reference to the original instance for tests/debugging if absent
138+
if (!Object.prototype.hasOwnProperty.call(i18nWrapper, '__original')) {
139+
Object.defineProperty(i18nWrapper, '__original', {
140+
value: finalI18n,
141+
writable: false,
142+
enumerable: false,
143+
configurable: false,
144+
});
145+
}
144146

145147
const arr = [t, i18nWrapper, ready];
146148
arr.t = t;

test/useTranslation.spec.jsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,29 @@ describe('useTranslation', () => {
254254
expect(container.textContent).toBeTruthy();
255255
});
256256
});
257+
258+
describe('useTranslation __original safety', () => {
259+
it('does not throw when the source i18n has a non-configurable __original property', async () => {
260+
const mockI18n = createInstance();
261+
await mockI18n.init({
262+
lng: 'en',
263+
resources: { en: { translation: { key1: 'test' } } },
264+
react: { useSuspense: false },
265+
});
266+
267+
// Simulate a non-configurable __original on the source instance
268+
Object.defineProperty(mockI18n, '__original', {
269+
value: 'existing',
270+
writable: false,
271+
enumerable: false,
272+
configurable: false,
273+
});
274+
275+
// Should not throw; wrapper creation should handle the existing non-configurable descriptor
276+
const { result } = renderHook(() => useTranslation('translation', { i18n: mockI18n }));
277+
278+
// The returned wrapper should expose __original pointing to the original instance
279+
expect(result.current.i18n.__original).toBe(mockI18n);
280+
});
281+
});
257282
});

0 commit comments

Comments
 (0)