-
-
Notifications
You must be signed in to change notification settings - Fork 832
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Largely coherent, yey for refactoring things 👍 😄
], | ||
{}, | ||
{ | ||
'span': () => <span>{ this.props.homeserverUrl }</span>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like it can be replaced with a normal substitution,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, but I think it might be better to save for later. Changing it to a normal variable would require changing it in every translation as well. which would make this PR balloon even more (or force every translator to re-translate the string).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough
} | ||
|
||
return ( | ||
<div className="mx_SenderProfile" dir="auto" onClick={props.onClick}> | ||
{ content } | ||
{ content.props.children[0] ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a little bit sad, especially because we're only doing this so that Flair can appear with a different opacity to the sender profile text and yet we don't show Flair (currently) when the aux text is actually used.
One way to do this is to not use opacity as a cheap way to get the text to be the colour we want. This feels out-of-scope, but maybe leave a "// XXX:
" comment explaining this.
src/languageHandler.js
Outdated
// instance. | ||
export function _t(...args) { | ||
// Wrapper for counterpart's translation function so that it handles nulls and undefineds properly | ||
//Takes the same arguments as counterpart.translate() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing space at start of comment
src/languageHandler.js
Outdated
* @return a React <span> component if any non-strings were used in substitutions, otherwise a string | ||
*/ | ||
export function _t(text, variables, tags) { | ||
// Don't do subsitutions in counterpart. We hanle it ourselves so we can replace with React components |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spelling
src/languageHandler.js
Outdated
if (!match) { | ||
throw new Error(`_tJsx: translator error. expected translation to match regexp: ${patterns[i]}`); | ||
const match = inputText.match(regexp); | ||
if(!match) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally, the style is if (
src/languageHandler.js
Outdated
const match = inputText.match(regexp); | ||
if(!match) { | ||
output.push(inputText); // Push back input | ||
continue; // Missing matches is entirely possible, because translation might change things |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the point was that the translations should contain the things to be replaced? Should we not still be throwing an error here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not true. You can pass in e.g. count
, but not display it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If count
is the exception, then maybe we should handle it as one and treat missing matches as errors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's also possible for others, but probably less common. E.g. in the "plural branch" you might display other variables than in the "singular branch".
Might make sense to log a warning though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Warning would be fine, but probably want to ignore missing count
because otherwise we log pointlessly. In doing so we might lose some warnings we do want for plural branches, as you've described, but that feels like an okay tradeoff.
src/languageHandler.js
Outdated
|
||
output.push(toPush); | ||
|
||
// Check if we need to wrap the output into a span at the end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would change this comment to be along the lines of "wrap the output in a React component if any items are React components"
it('variable substitution with React component', function() { | ||
// Need an extra space at the end because the result of _t() has an extra empty node at the end | ||
const text = 'You are now ignoring %(userId)s '; | ||
expect(JSON.stringify(languageHandler._t(text, { userId: () => <i>foo</i> }))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for JSON.stringify? This should probably be commented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a better way to compare objects by content rather than identity?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expect
supports toEqual
: https://github.com/mjackson/expect#toequal, which can be used in place of toBe
(which uses ===
).
}); | ||
|
||
it('variable substitution with React component', function() { | ||
// Need an extra space at the end because the result of _t() has an extra empty node at the end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way for it not to have an extra empty node? 😇
src/languageHandler.js
Outdated
*/ | ||
export function _t(text, variables, tags) { | ||
// Don't do subsitutions in counterpart. We hanle it ourselves so we can replace with React components | ||
const args = Object.assign({ interpolate: false }, variables); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So why is variables
passed in here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few comments (and ftr, have been chatting on riot dev too (https://matrix.to/#/!DdJkzRliezrwpNebLk:matrix.org/$1510674234381wUoHD:ldbco.de) about things like doing our own sprintf-ing rather than using counterparts, but I think this is probably fine.
Thanks for this, I appreciate it's a bit of a mission!
src/languageHandler.js
Outdated
|
||
let wrap = false; // Remember if the output needs to be wrapped later | ||
for (const regexpString in mapping) { | ||
const regexp = new RegExp(regexpString); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might find we need to look at caching these regexes, but I guess premature optimisation...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had the same thought. I concluded that seemingly our major perf issues are elsewhere, but could be wrong.
@@ -370,7 +373,10 @@ export const MsisdnAuthEntry = React.createClass({ | |||
}); | |||
return ( | |||
<div> | |||
<p>{ _tJsx("A text message has been sent to %(msisdn)s", /%\(msisdn\)s/, (sub) => <i>{this._msisdn}</i>) }</p> | |||
<p>{ _t("A text message has been sent to %(msisdn)s", | |||
{ msisdn: () => <i>this._msisdn</i> }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this not just be the value rather than a function?
{}, | ||
{ | ||
'CreateRoomButton': () => <CreateRoomButton size="16" callout={true} />, | ||
'RoomDirectoryButton': () => <RoomDirectoryButton size="16" callout={true} />, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More bits that don't need to be functions?
src/languageHandler.js
Outdated
* | ||
* @param {string} jsxText The untranslated stringified JSX e.g "click <a href=''>here</a> now". | ||
* This will be translated by passing the string through to _t(...) | ||
* The values to substitute with can be either simple strings, or functions that return the value to use in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the code, looks like they could probably be react components too? So you could just pass them in rather than functions that return them if it's a node that doesn't contain text? In fact, presumably you could even do these by passing it into vars
rather than tags
?
* Translates text and optionally also replaces XML-ish elements in the text with e.g. React components | ||
* @param {string} text The untranslated text, e.g "click <a>here</a> now to %(foo)s". | ||
* @param {object} variables Variable substitutions, e.g { foo: 'bar' } | ||
* @param {object} tags Tag substitutions e.g. { 'a': (sub) => <a>{sub}</a> } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth spelling out when one would use vars vs tags here (ie. tags is necessary whenever you need i18ned text inside an HTML tag like <a>
or <i>
?) With _tJsx
you also needed to use _tJsx
if you wanted any custom markup in the i18ned text (eg. an img
) but since your new _t
uses the same code paths for both, presumably tags like <img>
could equally be done with a regular vars
substitution?
Signed-off-by: Stefan Parviainen <pafcu@iki.fi>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tiny thing, but LGTM otherwise 😄
src/languageHandler.js
Outdated
|
||
let replaced; | ||
// If substitution is a function, call it | ||
if(mapping[regexpString] instanceof Function) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The style we use is if (
, not sure why we haven't added this to the linting.
Also see element-hq/element-web#5613
Get rid of _tJsx and offer all features in the same function:
%(foo)s
AND<a>foo</a>
replacements mixed togetherAlso, no need anymore to play around with regular expressions.
_t('Hello %(var)s', { var: 'foo' })
_tJsx('sent to %(emailAddress)s', /%\(emailAddress\)s/, (sub) => <i>{emailAddress}</i>)
_t('sent to %(emailAddress)s', { emailAddress: (sub) => <i>{emailAddress}</i> })
_tJsx('Click <a>here</a>', [/<a>(.*?)<\/a>/], [(sub) => <a>{ sub }</a>]
)_t('Click <a>here</a>', {}, {'a': (sub) => <a>{ sub }</a>
)_tJsx('Click <a>here</a> or <a>there</a>', [/<a>(.*?)<\/a>/,/<a>(.*?)<\/a>/], [(sub) => <a>{ sub }</a>,(sub) => <a>{ sub }</a>]
)_t('Click <hereLink>here</hereLink> or <thereLink>there</thereLink>', {}, {'hereLink': (sub) => <a>{ sub }</a>,'thereLink': (sub) => <a>{ sub }</a>}
)If this looks OK I'll update riot-web as well, and update the documentation.