1
1
import { Fragment , isValidElement , cloneElement , createElement , Children } from 'react' ;
2
2
import HTML from 'html-parse-stringify' ;
3
- import { isObject , isString , warn , warnOnce } from './utils.js' ;
3
+ import { ERR_CODES , isObject , isString , warnOnce } from './utils.js' ;
4
4
import { getDefaults } from './defaults.js' ;
5
5
import { getI18n } from './i18nInstance.js' ;
6
6
@@ -29,7 +29,7 @@ const mergeProps = (source, target) => {
29
29
return newTarget ;
30
30
} ;
31
31
32
- export const nodesToString = ( children , i18nOptions , i18n , i18nKey ) => {
32
+ export const nodesToString = ( children , i18nOptions , i18n , i18nKey , _parentWarnings ) => {
33
33
if ( ! children ) return '' ;
34
34
let stringNode = '' ;
35
35
@@ -39,13 +39,17 @@ export const nodesToString = (children, i18nOptions, i18n, i18nKey) => {
39
39
? ( i18nOptions . transKeepBasicHtmlNodesFor ?? [ ] )
40
40
: [ ] ;
41
41
42
+ const warnings = Array . isArray ( _parentWarnings ) ? _parentWarnings : [ ] ;
42
43
// e.g. lorem <br/> ipsum {{ messageCount, format }} dolor <strong>bold</strong> amet
43
44
childrenArray . forEach ( ( child , childIndex ) => {
44
45
if ( isString ( child ) ) {
45
46
// actual e.g. lorem
46
47
// expected e.g. lorem
47
48
stringNode += `${ child } ` ;
48
- } else if ( isValidElement ( child ) ) {
49
+ return ;
50
+ }
51
+
52
+ if ( isValidElement ( child ) ) {
49
53
const { props, type } = child ;
50
54
const childPropsCount = Object . keys ( props ) . length ;
51
55
const shouldKeepChild = keepArray . indexOf ( type ) > - 1 ;
@@ -55,55 +59,79 @@ export const nodesToString = (children, i18nOptions, i18n, i18nKey) => {
55
59
// actual e.g. lorem <br/> ipsum
56
60
// expected e.g. lorem <br/> ipsum
57
61
stringNode += `<${ type } />` ;
58
- } else if (
59
- ( ! childChildren && ( ! shouldKeepChild || childPropsCount ) ) ||
60
- props . i18nIsDynamicList
61
- ) {
62
+ return ;
63
+ }
64
+
65
+ if ( ( ! childChildren && ( ! shouldKeepChild || childPropsCount ) ) || props . i18nIsDynamicList ) {
62
66
// actual e.g. lorem <hr className="test" /> ipsum
63
67
// expected e.g. lorem <0></0> ipsum
64
68
// or
65
69
// we got a dynamic list like
66
70
// e.g. <ul i18nIsDynamicList>{['a', 'b'].map(item => ( <li key={item}>{item}</li> ))}</ul>
67
71
// expected e.g. "<0></0>", not e.g. "<0><0>a</0><1>b</1></0>"
68
72
stringNode += `<${ childIndex } ></${ childIndex } >` ;
69
- } else if ( shouldKeepChild && childPropsCount === 1 && isString ( childChildren ) ) {
73
+ return ;
74
+ }
75
+
76
+ if ( shouldKeepChild && childPropsCount === 1 && isString ( childChildren ) ) {
70
77
// actual e.g. dolor <strong>bold</strong> amet
71
78
// expected e.g. dolor <strong>bold</strong> amet
72
79
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 } >` ;
80
+ return ;
77
81
}
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 ) ) {
82
+
83
+ // regular case mapping the inner children
84
+ const content = nodesToString ( childChildren , i18nOptions , i18n , i18nKey , warnings ) ;
85
+ stringNode += `<${ childIndex } >${ content } </${ childIndex } >` ;
86
+
87
+ return ;
88
+ }
89
+
90
+ if ( child === null ) {
91
+ warnings . push ( {
92
+ code : ERR_CODES . TRANS_NULL_VALUE ,
93
+ message : `The passed in value is invalid - seems you passed in a null child.` ,
94
+ } ) ;
95
+ return ;
96
+ }
97
+
98
+ if ( isObject ( child ) ) {
81
99
// e.g. lorem {{ value, format }} ipsum
82
100
const { format, ...clone } = child ;
83
101
const keys = Object . keys ( clone ) ;
84
102
85
103
if ( keys . length === 1 ) {
86
104
const value = format ? `${ keys [ 0 ] } , ${ format } ` : keys [ 0 ] ;
87
105
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
- ) ;
106
+ return ;
96
107
}
97
- } else {
98
- warn (
99
- 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}}.` ,
108
+
109
+ // not a valid interpolation object (can only contain one value plus format)
110
+ warnings . push ( {
111
+ code : ERR_CODES . TRANS_INVALID_OBJ ,
112
+ message : `The passed in object contained more than one variable - the object should look like {{ value, format }} where format is optional.` ,
101
113
child,
102
- i18nKey ,
103
- ) ;
114
+ } ) ;
115
+
116
+ return ;
104
117
}
118
+
119
+ // e.g. lorem {number} ipsum
120
+ warnings . push ( {
121
+ code : ERR_CODES . TRANS_INVALID_VAR ,
122
+ message : `Passed in a variable like {number} - please pass in variables for interpolation as full objects like {{number}}.` ,
123
+ child,
124
+ } ) ;
105
125
} ) ;
106
126
127
+ // only warn once for a key and group warnings if there are more than 1.
128
+ if ( warnings . length && ! _parentWarnings ) {
129
+ warnOnce ( i18n , `Trans:key:${ i18nKey } : Interpolation errors - check the passed childrens.` , {
130
+ i18nKey,
131
+ warnings,
132
+ } ) ;
133
+ }
134
+
107
135
return stringNode ;
108
136
} ;
109
137
@@ -336,7 +364,7 @@ const generateObjectComponents = (components, translation) => {
336
364
return componentMap ;
337
365
} ;
338
366
339
- const generateComponents = ( components , translation , i18n ) => {
367
+ const generateComponents = ( components , translation , i18n , i18nKey ) => {
340
368
if ( ! components ) return null ;
341
369
342
370
// components could be either an array or an object
@@ -351,7 +379,15 @@ const generateComponents = (components, translation, i18n) => {
351
379
352
380
// if components is not an array or an object, warn the user
353
381
// and return null
354
- warnOnce ( i18n , '<Trans /> component prop expects an object or an array' ) ;
382
+ warnOnce ( i18n , `Trans:key:${ i18nKey } : "components" prop expects an object or an array` , {
383
+ i18nKey,
384
+ warnings : [
385
+ {
386
+ code : ERR_CODES . TRANS_INVALID_COMPONENTS ,
387
+ message : `<Trans /> "components" prop expects an object or an array, received ${ typeof components } ` ,
388
+ } ,
389
+ ] ,
390
+ } ) ;
355
391
return null ;
356
392
} ;
357
393
@@ -374,7 +410,19 @@ export function Trans({
374
410
const i18n = i18nFromProps || getI18n ( ) ;
375
411
376
412
if ( ! i18n ) {
377
- warnOnce ( i18n , 'You will need to pass in an i18next instance by using i18nextReactModule' ) ;
413
+ warnOnce (
414
+ i18n ,
415
+ 'Trans: You will need to pass in an i18next instance by using i18nextReactModule' ,
416
+ {
417
+ i18nKey,
418
+ warnings : [
419
+ {
420
+ code : ERR_CODES . NO_I18NEXT_INSTANCE ,
421
+ message : 'You will need to pass in an i18next instance by using i18nextReactModule' ,
422
+ } ,
423
+ ] ,
424
+ } ,
425
+ ) ;
378
426
return children ;
379
427
}
380
428
@@ -417,7 +465,7 @@ export function Trans({
417
465
} ;
418
466
const translation = key ? t ( key , combinedTOpts ) : defaultValue ;
419
467
420
- const generatedComponents = generateComponents ( components , translation , i18n ) ;
468
+ const generatedComponents = generateComponents ( components , translation , i18n , i18nKey ) ;
421
469
422
470
const content = renderNodes (
423
471
generatedComponents || children ,
0 commit comments