Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit e5d3198

Browse files
authored
Merge pull request #3339 from matrix-org/t3chguy/substitute_handle_global
Iterate over all instances of variable/tag for _t substitutions
2 parents f4ca91d + 7d511fb commit e5d3198

File tree

2 files changed

+60
-31
lines changed

2 files changed

+60
-31
lines changed

src/languageHandler.js

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -179,57 +179,75 @@ export function replaceByRegexes(text, mapping) {
179179

180180
for (const regexpString in mapping) {
181181
// TODO: Cache regexps
182-
const regexp = new RegExp(regexpString);
182+
const regexp = new RegExp(regexpString, "g");
183183

184184
// Loop over what output we have so far and perform replacements
185185
// We look for matches: if we find one, we get three parts: everything before the match, the replaced part,
186186
// and everything after the match. Insert all three into the output. We need to do this because we can insert objects.
187-
// Otherwise there would be no need for the splitting and we could do simple replcement.
187+
// Otherwise there would be no need for the splitting and we could do simple replacement.
188188
let matchFoundSomewhere = false; // If we don't find a match anywhere we want to log it
189189
for (const outputIndex in output) {
190190
const inputText = output[outputIndex];
191191
if (typeof inputText !== 'string') { // We might have inserted objects earlier, don't try to replace them
192192
continue;
193193
}
194194

195-
const match = inputText.match(regexp);
196-
if (!match) {
197-
continue;
198-
}
199-
matchFoundSomewhere = true;
195+
// process every match in the string
196+
// starting with the first
197+
let match = regexp.exec(inputText);
200198

201-
const capturedGroups = match.slice(2);
199+
if (!match) continue;
200+
matchFoundSomewhere = true;
202201

203-
// The textual part before the match
202+
// The textual part before the first match
204203
const head = inputText.substr(0, match.index);
205204

206-
// The textual part after the match
207-
const tail = inputText.substr(match.index + match[0].length);
208-
209-
let replaced;
210-
// If substitution is a function, call it
211-
if (mapping[regexpString] instanceof Function) {
212-
replaced = mapping[regexpString].apply(null, capturedGroups);
213-
} else {
214-
replaced = mapping[regexpString];
215-
}
205+
const parts = [];
206+
// keep track of prevMatch
207+
let prevMatch;
208+
while (match) {
209+
// store prevMatch
210+
prevMatch = match;
211+
const capturedGroups = match.slice(2);
212+
213+
let replaced;
214+
// If substitution is a function, call it
215+
if (mapping[regexpString] instanceof Function) {
216+
replaced = mapping[regexpString].apply(null, capturedGroups);
217+
} else {
218+
replaced = mapping[regexpString];
219+
}
216220

217-
if (typeof replaced === 'object') {
218-
shouldWrapInSpan = true;
219-
}
221+
if (typeof replaced === 'object') {
222+
shouldWrapInSpan = true;
223+
}
220224

221-
output.splice(outputIndex, 1); // Remove old element
225+
// Here we also need to check that it actually is a string before comparing against one
226+
// The head and tail are always strings
227+
if (typeof replaced !== 'string' || replaced !== '') {
228+
parts.push(replaced);
229+
}
222230

223-
// Insert in reverse order as splice does insert-before and this way we get the final order correct
224-
if (tail !== '') {
225-
output.splice(outputIndex, 0, tail);
231+
// try the next match
232+
match = regexp.exec(inputText);
233+
234+
// add the text between prevMatch and this one
235+
// or the end of the string if prevMatch is the last match
236+
let tail;
237+
if (match) {
238+
const startIndex = prevMatch.index + prevMatch[0].length;
239+
tail = inputText.substr(startIndex, match.index - startIndex);
240+
} else {
241+
tail = inputText.substr(prevMatch.index + prevMatch[0].length);
242+
}
243+
if (tail) {
244+
parts.push(tail);
245+
}
226246
}
227247

228-
// Here we also need to check that it actually is a string before comparing against one
229-
// The head and tail are always strings
230-
if (typeof replaced !== 'string' || replaced !== '') {
231-
output.splice(outputIndex, 0, replaced);
232-
}
248+
// Insert in reverse order as splice does insert-before and this way we get the final order correct
249+
// remove the old element at the same time
250+
output.splice(outputIndex, 1, ...parts);
233251

234252
if (head !== '') { // Don't push empty nodes, they are of no use
235253
output.splice(outputIndex, 0, head);

test/i18n-test/languageHandler-test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,15 @@ describe('languageHandler', function() {
7070
const text = '%(var1)s %(var2)s';
7171
expect(languageHandler._t(text, { var2: 'val2', var1: 'val1' })).toBe('val1 val2');
7272
});
73+
74+
it('multiple replacements of the same variable', function() {
75+
const text = '%(var1)s %(var1)s';
76+
expect(languageHandler.substitute(text, { var1: 'val1' })).toBe('val1 val1');
77+
});
78+
79+
it('multiple replacements of the same tag', function() {
80+
const text = '<a>Click here</a> to join the discussion! <a>or here</a>';
81+
expect(languageHandler.substitute(text, {}, { 'a': (sub) => `x${sub}x` }))
82+
.toBe('xClick herex to join the discussion! xor herex');
83+
});
7384
});

0 commit comments

Comments
 (0)