Clarifications
parent
cdd03dd1c5
commit
788be67c75
|
@ -52,6 +52,10 @@ export default function SenderProfile(props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_SenderProfile" dir="auto" onClick={props.onClick}>
|
<div className="mx_SenderProfile" dir="auto" onClick={props.onClick}>
|
||||||
|
// The text surrounding the user name must be wrapped in order for it to have the correct opacity.
|
||||||
|
// It is not possible to wrap the whole thing, because the user name might contain flair which should
|
||||||
|
// be shown at full opacity. Sadly CSS does not make it possible to "reset" opacity so we have to do it
|
||||||
|
// in parts like this. Sometimes CSS makes me a sad panda :-(
|
||||||
{ content.props.children[0] ?
|
{ content.props.children[0] ?
|
||||||
<span className='mx_SenderProfile_aux'>{ content.props.children[0] }</span> : ''
|
<span className='mx_SenderProfile_aux'>{ content.props.children[0] }</span> : ''
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ export function _td(s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper for counterpart's translation function so that it handles nulls and undefineds properly
|
// Wrapper for counterpart's translation function so that it handles nulls and undefineds properly
|
||||||
//Takes the same arguments as counterpart.translate()
|
// Takes the same arguments as counterpart.translate()
|
||||||
function safe_counterpart_translate(...args) {
|
function safe_counterpart_translate(...args) {
|
||||||
// Horrible hack to avoid https://github.com/vector-im/riot-web/issues/4191
|
// Horrible hack to avoid https://github.com/vector-im/riot-web/issues/4191
|
||||||
// The interpolation library that counterpart uses does not support undefined/null
|
// The interpolation library that counterpart uses does not support undefined/null
|
||||||
|
@ -66,14 +66,20 @@ function safe_counterpart_translate(...args) {
|
||||||
* @param {object} variables Variable substitutions, e.g { foo: 'bar' }
|
* @param {object} variables Variable substitutions, e.g { foo: 'bar' }
|
||||||
* @param {object} tags Tag substitutions e.g. { 'a': (sub) => <a>{sub}</a> }
|
* @param {object} tags Tag substitutions e.g. { 'a': (sub) => <a>{sub}</a> }
|
||||||
*
|
*
|
||||||
* The values to substitute with can be either simple strings, or functions that return the value to use in
|
* In both variables and tags, the values to substitute with can be either simple strings, React components,
|
||||||
* the substitution (e.g. return a React component). In case of a tag replacement, the function receives as
|
* or functions that return the value to use in the substitution (e.g. return a React component). In case of
|
||||||
* the argument the text inside the element corresponding to the tag.
|
* a tag replacement, the function receives as the argument the text inside the element corresponding to the tag.
|
||||||
|
*
|
||||||
|
* Use tag substitutions if you need to translate text between tags (e.g. "<a>Click here!</a>"), otherwise
|
||||||
|
* you will end up with literal "<a>" in your output, rather than HTML. Note that you can also use variable
|
||||||
|
* substitution to insert React components, but you can't use it to translate text between tags.
|
||||||
*
|
*
|
||||||
* @return a React <span> component if any non-strings were used in substitutions, otherwise a string
|
* @return a React <span> component if any non-strings were used in substitutions, otherwise a string
|
||||||
*/
|
*/
|
||||||
export function _t(text, variables, tags) {
|
export function _t(text, variables, tags) {
|
||||||
// Don't do subsitutions in counterpart. We hanle it ourselves so we can replace with React components
|
// Don't do subsitutions in counterpart. We handle it ourselves so we can replace with React components
|
||||||
|
// However, still pass the variables to counterpart so that it can choose the correct plural if count is given
|
||||||
|
// It is enough to pass the count variable, but in the future counterpart might make use of other information too
|
||||||
const args = Object.assign({ interpolate: false }, variables);
|
const args = Object.assign({ interpolate: false }, variables);
|
||||||
|
|
||||||
// The translation returns text so there's no XSS vector here (no unsafe HTML, no code execution)
|
// The translation returns text so there's no XSS vector here (no unsafe HTML, no code execution)
|
||||||
|
@ -123,15 +129,18 @@ export function substitute(text, variables, tags) {
|
||||||
export function replaceByRegexes(text, mapping) {
|
export function replaceByRegexes(text, mapping) {
|
||||||
const output = [text];
|
const output = [text];
|
||||||
|
|
||||||
let wrap = false; // Remember if the output needs to be wrapped later
|
// If we insert any components we need to wrap the output in a span. React doesn't like just an array of components.
|
||||||
|
let shouldWrapInSpan = false;
|
||||||
|
|
||||||
for (const regexpString in mapping) {
|
for (const regexpString in mapping) {
|
||||||
|
// TODO: Cache regexps
|
||||||
const regexp = new RegExp(regexpString);
|
const regexp = new RegExp(regexpString);
|
||||||
|
|
||||||
// convert the last element in 'output' into 3 elements (pre-text, sub function, post-text).
|
// convert the last element in 'output' into 3 elements (pre-text, sub function, post-text).
|
||||||
// Rinse and repeat for other patterns (using post-text).
|
// Rinse and repeat for other patterns (using post-text).
|
||||||
const inputText = output.pop();
|
const inputText = output.pop();
|
||||||
const match = inputText.match(regexp);
|
const match = inputText.match(regexp);
|
||||||
if(!match) {
|
if (!match) {
|
||||||
output.push(inputText); // Push back input
|
output.push(inputText); // Push back input
|
||||||
continue; // Missing matches is entirely possible, because translation might change things
|
continue; // Missing matches is entirely possible, because translation might change things
|
||||||
}
|
}
|
||||||
|
@ -160,7 +169,7 @@ export function replaceByRegexes(text, mapping) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(typeof replaced === 'object') {
|
if(typeof replaced === 'object') {
|
||||||
wrap = true;
|
shouldWrapInSpan = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tail = inputText.substr(match.index + match[0].length);
|
const tail = inputText.substr(match.index + match[0].length);
|
||||||
|
@ -169,10 +178,7 @@ export function replaceByRegexes(text, mapping) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(wrap) {
|
if(shouldWrapInSpan) {
|
||||||
// this is a bit of a fudge to avoid the 'Each child in an array or iterator
|
|
||||||
// should have a unique "key" prop' error: we explicitly pass the generated
|
|
||||||
// nodes into React.createElement as children of a <span>.
|
|
||||||
return React.createElement('span', null, ...output);
|
return React.createElement('span', null, ...output);
|
||||||
} else {
|
} else {
|
||||||
return output.join('');
|
return output.join('');
|
||||||
|
|
|
@ -54,6 +54,12 @@ describe('languageHandler', function() {
|
||||||
.toEqual((<span>You are now ignoring <i>foo</i></span>));
|
.toEqual((<span>You are now ignoring <i>foo</i></span>));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('variable substitution with plain React component', function() {
|
||||||
|
const text = 'You are now ignoring %(userId)s';
|
||||||
|
expect(languageHandler._t(text, { userId: <i>foo</i> }))
|
||||||
|
.toEqual((<span>You are now ignoring <i>foo</i></span>));
|
||||||
|
});
|
||||||
|
|
||||||
it('tag substitution with React component', function() {
|
it('tag substitution with React component', function() {
|
||||||
const text = 'Press <StartChatButton> to start a chat with someone';
|
const text = 'Press <StartChatButton> to start a chat with someone';
|
||||||
expect(languageHandler._t(text, {}, { 'StartChatButton': () => <i>foo</i> }))
|
expect(languageHandler._t(text, {}, { 'StartChatButton': () => <i>foo</i> }))
|
||||||
|
|
Loading…
Reference in New Issue