@@ -18,6 +18,7 @@ limitations under the License.
18
18
import request from 'browser-request' ;
19
19
import counterpart from 'counterpart' ;
20
20
import q from 'q' ;
21
+ import sanitizeHtml from "sanitize-html" ;
21
22
22
23
import UserSettingsStore from './UserSettingsStore' ;
23
24
@@ -37,6 +38,80 @@ export function _t(...args) {
37
38
return counterpart . translate ( ...args ) ;
38
39
}
39
40
41
+ /*
42
+ * Translates stringified JSX into translated JSX. E.g
43
+ * _tJsx(
44
+ * "click <a href=''>here</a> now",
45
+ * /<a href=''>(.*?)<\/a>/,
46
+ * (sub) => { return <a href=''>{ sub }</a>; }
47
+ * );
48
+ *
49
+ * @param {string } jsxText The untranslated stringified JSX e.g "click <a href=''>here</a> now".
50
+ * This will be translated by passing the string through to _t(...)
51
+ *
52
+ * @param {RegExp|RegExp[] } patterns A regexp to match against the translated text.
53
+ * The captured groups from the regexp will be fed to 'sub'.
54
+ * Only the captured groups will be included in the output, the match itself is discarded.
55
+ * If multiple RegExps are provided, the function at the same position will be called. The
56
+ * match will always be done from left to right, so the 2nd RegExp will be matched against the
57
+ * remaining text from the first RegExp.
58
+ *
59
+ * @param {Function|Function[] } subs A function which will be called
60
+ * with multiple args, each arg representing a captured group of the matching regexp.
61
+ * This function must return a JSX node.
62
+ *
63
+ * @return A list of strings/JSX nodes.
64
+ */
65
+ export function _tJsx ( jsxText , patterns , subs ) {
66
+ // convert everything to arrays
67
+ if ( patterns instanceof RegExp ) {
68
+ patterns = [ patterns ] ;
69
+ }
70
+ if ( subs instanceof Function ) {
71
+ subs = [ subs ] ;
72
+ }
73
+ // sanity checks
74
+ if ( subs . length !== patterns . length || subs . length < 1 ) {
75
+ throw new Error ( `_tJsx: programmer error. expected number of RegExps == number of Functions: ${ subs . length } != ${ patterns . length } ` ) ;
76
+ }
77
+ for ( let i = 0 ; i < subs . length ; i ++ ) {
78
+ if ( ! patterns [ i ] instanceof RegExp ) {
79
+ throw new Error ( `_tJsx: programmer error. expected RegExp for text: ${ jsxText } ` ) ;
80
+ }
81
+ if ( ! subs [ i ] instanceof Function ) {
82
+ throw new Error ( `_tJsx: programmer error. expected Function for text: ${ jsxText } ` ) ;
83
+ }
84
+ }
85
+
86
+ // tJsxText may be unsafe if malicious translators try to inject HTML.
87
+ // Run this through sanitize-html and bail if the output isn't identical
88
+ const tJsxText = _t ( jsxText ) ;
89
+ const sanitized = sanitizeHtml ( tJsxText , { allowedTags : sanitizeHtml . defaults . allowedTags . concat ( [ 'span' ] ) } ) ;
90
+ if ( tJsxText !== sanitized ) {
91
+ throw new Error ( `_tJsx: translator error. untrusted HTML supplied. '${ tJsxText } ' != '${ sanitized } '` ) ;
92
+ }
93
+
94
+ let output = [ tJsxText ] ;
95
+ for ( let i = 0 ; i < patterns . length ; i ++ ) {
96
+ // convert the last element in 'output' into 3 elements (pre-text, sub function, post-text).
97
+ // Rinse and repeat for other patterns (using post-text).
98
+ let inputText = output . pop ( ) ;
99
+ let match = inputText . match ( patterns [ i ] ) ;
100
+ if ( ! match ) {
101
+ throw new Error ( `_tJsx: translator error. expected translation to match regexp: ${ patterns [ i ] } ` ) ;
102
+ }
103
+ let capturedGroups = match . slice ( 1 ) ;
104
+
105
+ // Return the raw translation before the *match* followed by the return value of sub() followed
106
+ // by the raw translation after the *match* (not captured group).
107
+ output . push ( inputText . substr ( 0 , match . index ) ) ;
108
+ output . push ( subs [ i ] . apply ( null , capturedGroups ) ) ;
109
+ output . push ( inputText . substr ( match . index + match [ 0 ] . length ) ) ;
110
+ }
111
+
112
+ return output ;
113
+ }
114
+
40
115
// Allow overriding the text displayed when no translation exists
41
116
// Currently only use din unit tests to avoid having to load
42
117
// the translations in riot-web
0 commit comments