Skip to content

Commit 8848b1e

Browse files
feat: add codes and data to warnings to allow conditional logging
1 parent ff509ba commit 8848b1e

File tree

3 files changed

+93
-33
lines changed

3 files changed

+93
-33
lines changed

src/TransWithoutContext.js

+67-30
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Fragment, isValidElement, cloneElement, createElement, Children } from 'react';
22
import HTML from 'html-parse-stringify';
3-
import { isObject, isString, warn, warnOnce } from './utils.js';
3+
import { ERR_CODES, isObject, isString, warn, warnOnce } from './utils.js';
44
import { getDefaults } from './defaults.js';
55
import { getI18n } from './i18nInstance.js';
66

@@ -45,7 +45,10 @@ export const nodesToString = (children, i18nOptions, i18n, i18nKey) => {
4545
// actual e.g. lorem
4646
// expected e.g. lorem
4747
stringNode += `${child}`;
48-
} else if (isValidElement(child)) {
48+
return;
49+
}
50+
51+
if (isValidElement(child)) {
4952
const { props, type } = child;
5053
const childPropsCount = Object.keys(props).length;
5154
const shouldKeepChild = keepArray.indexOf(type) > -1;
@@ -55,53 +58,77 @@ export const nodesToString = (children, i18nOptions, i18n, i18nKey) => {
5558
// actual e.g. lorem <br/> ipsum
5659
// expected e.g. lorem <br/> ipsum
5760
stringNode += `<${type}/>`;
58-
} else if (
59-
(!childChildren && (!shouldKeepChild || childPropsCount)) ||
60-
props.i18nIsDynamicList
61-
) {
61+
return;
62+
}
63+
64+
if ((!childChildren && (!shouldKeepChild || childPropsCount)) || props.i18nIsDynamicList) {
6265
// actual e.g. lorem <hr className="test" /> ipsum
6366
// expected e.g. lorem <0></0> ipsum
6467
// or
6568
// we got a dynamic list like
6669
// e.g. <ul i18nIsDynamicList>{['a', 'b'].map(item => ( <li key={item}>{item}</li> ))}</ul>
6770
// expected e.g. "<0></0>", not e.g. "<0><0>a</0><1>b</1></0>"
6871
stringNode += `<${childIndex}></${childIndex}>`;
69-
} else if (shouldKeepChild && childPropsCount === 1 && isString(childChildren)) {
72+
return;
73+
}
74+
75+
if (shouldKeepChild && childPropsCount === 1 && isString(childChildren)) {
7076
// actual e.g. dolor <strong>bold</strong> amet
7177
// expected e.g. dolor <strong>bold</strong> amet
7278
stringNode += `<${type}>${childChildren}</${type}>`;
73-
} else {
74-
// regular case mapping the inner children
75-
const content = nodesToString(childChildren, i18nOptions, i18n, i18nKey);
76-
stringNode += `<${childIndex}>${content}</${childIndex}>`;
79+
return;
7780
}
78-
} else if (child === null) {
79-
warn(i18n, `Trans: the passed in value is invalid - seems you passed in a null child.`);
80-
} else if (isObject(child)) {
81+
82+
// regular case mapping the inner children
83+
const content = nodesToString(childChildren, i18nOptions, i18n, i18nKey);
84+
stringNode += `<${childIndex}>${content}</${childIndex}>`;
85+
86+
return;
87+
}
88+
89+
if (child === null) {
90+
warn(i18n, `Trans: The passed in value is invalid - seems you passed in a null child.`, {
91+
code: ERR_CODES.TRANS_NULL_VALUE,
92+
i18nKey,
93+
});
94+
return;
95+
}
96+
97+
if (isObject(child)) {
8198
// e.g. lorem {{ value, format }} ipsum
8299
const { format, ...clone } = child;
83100
const keys = Object.keys(clone);
84101

85102
if (keys.length === 1) {
86103
const value = format ? `${keys[0]}, ${format}` : keys[0];
87104
stringNode += `{{${value}}}`;
88-
} else {
89-
// not a valid interpolation object (can only contain one value plus format)
90-
warn(
91-
i18n,
92-
`react-i18next: the passed in object contained more than one variable - the object should look like {{ value, format }} where format is optional.`,
93-
child,
94-
i18nKey,
95-
);
105+
return;
96106
}
97-
} else {
107+
108+
// not a valid interpolation object (can only contain one value plus format)
98109
warn(
99110
i18n,
100-
`Trans: the passed in value is invalid - seems you passed in a variable like {number} - please pass in variables for interpolation as full objects like {{number}}.`,
101-
child,
102-
i18nKey,
111+
`Trans: The passed in object contained more than one variable - the object should look like {{ value, format }} where format is optional.`,
112+
{
113+
code: ERR_CODES.TRANS_INVALID_OBJ,
114+
i18nKey,
115+
child,
116+
},
103117
);
118+
119+
return;
104120
}
121+
122+
// e.g. lorem {number} ipsum
123+
warn(
124+
i18n,
125+
`Trans: Passed in a variable like {number} - please pass in variables for interpolation as full objects like {{number}}.`,
126+
{
127+
code: ERR_CODES.TRANS_INVALID_VAR,
128+
i18nKey,
129+
child,
130+
},
131+
);
105132
});
106133

107134
return stringNode;
@@ -336,7 +363,7 @@ const generateObjectComponents = (components, translation) => {
336363
return componentMap;
337364
};
338365

339-
const generateComponents = (components, translation, i18n) => {
366+
const generateComponents = (components, translation, i18n, i18nKey) => {
340367
if (!components) return null;
341368

342369
// components could be either an array or an object
@@ -351,7 +378,10 @@ const generateComponents = (components, translation, i18n) => {
351378

352379
// if components is not an array or an object, warn the user
353380
// and return null
354-
warnOnce(i18n, '<Trans /> component prop expects an object or an array');
381+
warnOnce(i18n, `<Trans /> "components" prop expects an object or an array`, {
382+
i18nKey,
383+
code: ERR_CODES.TRANS_INVALID_COMPONENTS,
384+
});
355385
return null;
356386
};
357387

@@ -374,7 +404,14 @@ export function Trans({
374404
const i18n = i18nFromProps || getI18n();
375405

376406
if (!i18n) {
377-
warnOnce(i18n, 'You will need to pass in an i18next instance by using i18nextReactModule');
407+
warnOnce(
408+
i18n,
409+
'Trans: You will need to pass in an i18next instance by using i18nextReactModule',
410+
{
411+
i18nKey,
412+
code: ERR_CODES.NO_I18NEXT_INSTANCE,
413+
},
414+
);
378415
return children;
379416
}
380417

@@ -417,7 +454,7 @@ export function Trans({
417454
};
418455
const translation = key ? t(key, combinedTOpts) : defaultValue;
419456

420-
const generatedComponents = generateComponents(components, translation, i18n);
457+
const generatedComponents = generateComponents(components, translation, i18n, i18nKey);
421458

422459
const content = renderNodes(
423460
generatedComponents || children,

src/useTranslation.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
hasLoadedNamespace,
88
isString,
99
isObject,
10+
ERR_CODES,
1011
} from './utils.js';
1112

1213
const usePrevious = (value, ignore) => {
@@ -35,7 +36,13 @@ export const useTranslation = (ns, props = {}) => {
3536
const i18n = i18nFromProps || i18nFromContext || getI18n();
3637
if (i18n && !i18n.reportNamespaces) i18n.reportNamespaces = new ReportNamespaces();
3738
if (!i18n) {
38-
warnOnce(i18n, 'You will need to pass in an i18next instance by using initReactI18next');
39+
warnOnce(
40+
i18n,
41+
'useTranslation: You will need to pass in an i18next instance by using initReactI18next',
42+
{
43+
code: ERR_CODES.NO_I18NEXT_INSTANCE,
44+
},
45+
);
3946
const notReadyT = (k, optsOrDefaultValue) => {
4047
if (isString(optsOrDefaultValue)) return optsOrDefaultValue;
4148
if (isObject(optsOrDefaultValue) && isString(optsOrDefaultValue.defaultValue))
@@ -52,7 +59,10 @@ export const useTranslation = (ns, props = {}) => {
5259
if (i18n.options.react?.wait)
5360
warnOnce(
5461
i18n,
55-
'It seems you are still using the old wait option, you may migrate to the new useSuspense behaviour.',
62+
'useTranslation: It seems you are still using the old wait option, you may migrate to the new useSuspense behaviour.',
63+
{
64+
code: ERR_CODES.DEPRECATED_WAIT_OPTION,
65+
},
5666
);
5767

5868
const i18nOptions = { ...getDefaults(), ...i18n.options.react, ...props };

src/utils.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ export const warnOnce = (i18n, ...args) => {
1717
warn(i18n, ...args);
1818
};
1919

20+
export const ERR_CODES = {
21+
NO_I18NEXT_INSTANCE: 'NO_I18NEXT_INSTANCE',
22+
DEPRECATED_WAIT_OPTION: 'DEPRECATED_WAIT_OPTION',
23+
TRANS_NULL_VALUE: 'TRANS_NULL_VALUE',
24+
TRANS_INVALID_OBJ: 'TRANS_INVALID_OBJ',
25+
TRANS_INVALID_VAR: 'TRANS_INVALID_VAR',
26+
TRANS_INVALID_COMPONENTS: 'TRANS_INVALID_COMPONENTS',
27+
MISSING_LANGUAGES: 'MISSING_LANGUAGES',
28+
};
29+
2030
// not needed right now
2131
//
2232
// export const deprecated = (i18n, ...args) => {
@@ -60,7 +70,10 @@ export const loadLanguages = (i18n, lng, ns, cb) => {
6070

6171
export const hasLoadedNamespace = (ns, i18n, options = {}) => {
6272
if (!i18n.languages || !i18n.languages.length) {
63-
warnOnce(i18n, 'i18n.languages were undefined or empty', i18n.languages);
73+
warnOnce(i18n, 'i18n.languages were undefined or empty', {
74+
code: ERR_CODES.MISSING_LANGUAGES,
75+
languages: i18n.languages,
76+
});
6477
return true;
6578
}
6679

0 commit comments

Comments
 (0)