-
Notifications
You must be signed in to change notification settings - Fork 79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add getFragment method to ReactLocalization #595
Conversation
It's similar to getString(), but returns a React Fragment instead of a string. The main added benefit here is that it allows the caller to pass in `elems` analogous to the similarly-named prop on <Localized>.
Apologies for the delay; I've been travelling and currently fluent.js reviews pretty much fall on me. In general, I think I'd be fine with adding I took a quick look at this PR; do I understand right that effectively you've combined code from |
No worries @eemeli, hope you enjoyed your travels :) Yes, you are correct about the approach I took. I understand the desire for a single internal implementation, but I'm hesitant about doing that myself - I find that introducing layers of abstraction ideally is informed by an idea of which lines of code are likely to change together in the future, which is hard to do for someone like me who's unfamiliar with the codebase and its history. The wrong abstractions are often worse than no abstraction, and I don't want to be responsible for that :) Possibly I could give it a shot if you'd have some pointers about the abstractions you'd like to see? |
@Vinnl I'd be happy to have you poke at this. :) My gut feeling would be to have it so that |
Thanks @eemeli, that made sense. There was a bunch of extra code in (And apologies for the delayed response - holidays and other work and all that :) ) |
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 like this! I do think we need to bikeshed the API a bit, though. See inline comments.
fluent-react/src/localization.ts
Outdated
args?: { | ||
vars?: Record<string, FluentVariable>, | ||
elems?: Record<string, ReactElement>, | ||
attrs?: Record<string, boolean>; | ||
}, |
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'm not really sure about this. In <Localized/>
passing each of these in as props makes sense, but here it feels like we're putting different things into the same basket -- esp. as this is so close to the shape of the getString()
args
argument.
My first instinct would be to separate vars
from the other two, but maybe calling this something other than args
could also help?
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.
Oh heh, good eye. I'd actually argue that renaming getString
s second argument to vars
would be more appropriate, since the Fluent docs also call them variables, but I can understand that changing the name of an existing API might not be desirable.
I could name it something like props
? Because they're basically analogous to <Localized>
's props, although it could also be confusing, given that they're not React props. Or how about params
?
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'm ok with changing the internal name of the getSrting()
argument to vars
, because it only impacts the documentation.
props
and params
aren't really better, and both come with their own sets of baggage.
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.
fluent-react/src/localization.ts
Outdated
// Check if the component to render is a valid element -- if not, then | ||
// it's either null or a simple fallback string. No need to localize the | ||
// attributes. | ||
if (!isValidElement(componentToRender)) { |
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 really rather prefer requiring componentToRender
to be a valid element when using this method. For <Localized/>
we do need to continue supporting use with no child, but would it be possible to check for that and handle it there, rather than needing to do so 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.
Hmm, I'm not sure what the best way to do that is - there'd be quite a bit of duplication in initialising the bundle and handling its various error cases. Personally I wouldn't be too worried about doing too much error handling. What I could do is update the type definition of getElement
to only allow ReactChild | ReactFragment
? Then we're technically requiring it to be a valid element in the public API.
Or, hmm, what I could also do is update <Localized>
to check for non-element children and, if found, wrap them in a fragment and pass that to getElement
? That would cause the latter to do some unnecessary work, but nothing too egregious I thinkg - what do you think?
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.
How about calling getString()
rather than getElement()
from a Localized with non-element children, and wrapping the result in a Fragment? As far as I can tell, that should replicate the current behaviour.
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.
Ah yeah, that's clever: 59671cc
I should note that that does change the behaviour a little bit: if you do not provide a child, it will now render the string ID, rather than null
. Which makes sense to me and aligns it with the existing behaviour of getString()
, but I can also imagine that that's not desirable.
One way to work around that might be to set the fallback string to some unique string, and return null
instead of a fragment if getString
returns the fallback string, but that feels pretty hacky :/ Alternatively, although still a change in behaviour but closer to the previous behaviour, would be to set the fallback string to the empty string, resulting in it returning a fragment containing an empty string instead of null
. That's technically different from what it used to be, but should have the same result for the user.
fluent-react/src/localization.ts
Outdated
@@ -106,7 +106,7 @@ export class ReactLocalization { | |||
if (Array.isArray(component)) { | |||
if (component.length > 1) { | |||
throw new Error( | |||
"<Localized/> expected to receive a single React node child" | |||
"Expected to receive a single React node element to inject a localized string into." |
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.
"Expected to receive a single React node element to inject a localized string into." | |
"Expected to receive a single React element to localize" |
Adding more words doesn't really help 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.
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.
See replies above for requested changes, and a couple minor new inline comments.
It's a thin wrapper, so not worth it for now to spend additional API surface area on, as per projectfluent#595 (comment)
It's equivalent to the `vars` prop on <Localiszed> and the respective parameter for getElement, holding Fluent variables.
@eemeli Just wondering if you have time to look at my latest changes - I think I've addressed all your comments :) |
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.
@Vinnl Sorry for the delay here, finally managed to take a look. A few small changes, and then I think we're good?
Thanks for getting back on it @eemeli, applied your suggestions! |
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 reverted the Children.only()
addition and applied a little code styling. Let's finally merge this! 🎉
Thank you so much @eemeli! |
Hi @eemeli, sorry to bug you again about this, but I was wondering if we could get a release of |
Fixes #544.
It's similar to
getString
, but returns a React Fragment insteadof a string. The main added benefit here is that it allows the
caller to pass in
elems
analogous to the similarly-named prop on<Localized>
.We're mostly using
getString
in the Firefox Relay codebase, but have to switch to<Localized>
every time we want to use an element in the translation string, which can be confusing to new contributors.I do understand that generally it's best to be aligned on a new feature before submitting a PR, which I didn't do, so I want to emphasise that it's perfectly fine to let me know that this feature isn't desirable for one reason or another, or that I should redo everything if I want to get this in. It didn't seem like a feature that was too hard to implement, so I decided to just go ahead and give it a shot, with the worst case being a little wasted effort :)