4 ms·
Translation and i18n of text is really just a function - send text in, get text out. Why would you ever put it in a hook, let alone a component? > We managed t
by activitypea 5y ago
Translation and i18n of text is really just a function - send text in, get text out. Why would you ever put it in a hook, let alone a component?
> We managed to abstract away the logic for translation into the Translation component.
A function call. You abstracted away a function call.
> Now there is a centralized translation component where we can change any UI or business logic if needed.
A plain old JS function still centralizes i18n functionality. As for the UI logic, all the component is doing is wrapping text, therefore your component needs to support anything anyone would ever want to do a p tag. Or an h1 tag. Or a li tag. Or every other tag that can contain a text node. At some point you're just reimplementing the DOM API. What specifically is gained in going from `<p>i18n('Hello!')</p>` to `<Translation text='Hello!'/>`?
> It’s now a lot easier to test files that use translation. Before refactoring we would have to mock the useTranslation somehow because we don’t want to test its implementation. With the refactored code we only need to check that we render the Translation component and that we pass the correct props.
I can't criticize this part because I genuinely don't understand where its coming from. "We only need to check that we render the Translation component" implies that the Translation component is rendered in the test. And for the Translation component to render, the `useTranslation` hook still has to be called, so we still have to mock it, right?
- zarathustreal 5y agoI think this poster is missing the key insight that React components are indeed nothing more than functions. If anyone is reading this right now go console.log the output of your component, it's an object that describes the DOM output. Logic should not live in components, React is a rendering library. Make use of higher-order functions. Make use of a proper state management solution. Don't over-use hooks. It's really that simple.
- eliseumds 5y agoThey're definitely more than simple functions if you look at it from the the entire application point-of-view. With components you have access to context and lifecycle. Doing: import someFunc from 'someFunc'; // ... return someFunc(); is not the same as: import SomeComponent from 'SomeComponent'; // ... return <SomeComponent />
- karottenreibe 5y agoIt all depends on how the component is written. And the function. The function could access global variables for example. The component could be pure and not use context. Neither of which you can tell just by looking at the import statement.
- activitypea 5y agoI guess I should clarify that I am specifically discussing i18n of text. I understand that proper i18n of apps is a much more complex topic.
- eliseumds 5y agoI beg your pardon? Even translation of pure text (and not numbers, date, currency) relies on locale data, like plurals and genders. It's just way more elegant to have this data inside a React context instead of importing a global translator or passing in locale data whenever needed (dangerous repetition).
- aniforprez 5y agoI genuinely don't understand this comment or the reason for writing it as a hook or a separate component in the article Why wouldn't you just write it as a function with data and context being passed through as arguments? Isn't it much simpler to write a pure function and test that function than render a component and test it? If you simply write a function that accepts the locale, the string and context, does the translator magic and returns the translated text, would you not then just need to write a unit test simply to make sure you're getting the expected output? Why the abstraction into hooks and then further into a separate component that you then have to render?
- eliseumds 5y agoIt doesn't matter what your code is doing inside that component, you can still make it easily testable by having the core translation pipeline as pure functions. See https://news.ycombinator.com/item?id=27031847 https://news.ycombinator.com/item?id=27031847 for more reasons.
- activitypea 5y agoWhat's "elegant" about putting it in a React Context? Outside of initial configuration, it's a stateless function. Just chuck it in a separate module and have some initialisation code. Nothing about it demands that it live in the component tree.
- tshaddox 5y agoI agree that I don't see a huge advantage to using a Translation component over a useTranslation hook, but I think the useTranslation hook can sometimes make sense over a plain old JS function, because you might want that hook to grab stuff from React context or even fetch data from the server using useEffect. It might not be feasible to have a plain old JS function with all internationalization data for all supported languages.
- jakelazaroff 5y ago> Translation and i18n of text is really just a function - send text in, get text out. Why would you ever put it in a hook, let alone a component? One concrete reason: if the language is user-configurable, you can avoid an additional argument at the call site by putting that data in a context.
- gizmo385 5y agoDoesn't that make testing the translation and internationalization functionality that much harder though? If it's a pure function that takes a string and a locale, I can write really simple, straightforward tests that I can be confident accurately capture the breadth of the functionality. If part of that input is now bundled up in this large globally structure, then now testing it seems like it would that much more complicated. Then, I'd need to artificially construct these structures as a part of my tests, making them less transparent to readers of the codebase.
- eliseumds 5y agoBy reducing the surface area of a widely-used interface like a translation component/hook, you massively decrease the chance of generating bugs and fewer tests would bring you way more confidence. When testing, you just need to wrap that component with a fake context that adheres to your LocaleDataContext interface (or whatever you decide to call it). > making them less transparent to readers of the codebase Sometimes that's exactly what you want. It's the same as hiding CSS styles behind a set of props like `<Button buttonStyle="primary" buttonSize="large">Continue</Button>`. It actually makes your codebase much safer by reducing the combination of props and values you're able to pass.
- root_axis 5y agoA context isn't necessarily "global state", it's "contextually shared state" hence the name. Of course, you could use it as a global state, but typically you shouldn't do that. Then again, the language locale (like a theme) is actually a compelling case for "application global" state. From a testing perspective, you just wrap the component being tested in a context provider that provides the necessary fixtures for your test case. However, I'd also note that this is probably not an ideal function to test since all that you could possibly be testing is that react context and i8n are working as expected, which is almost certainly outside the scope of your application's testing surface.
- ljm 5y agoThat one is kind of odd. I use a hook for i18n but all it does is pull from a react context. All of our tests that assert on text, assert using the i18n key. So we could re-run the suite in Arabic or Hebrew, for example, and be confident in the result. There's no reason to mock it. It's an identity function if the translation doesn't exist.
- root_axis 5y agoThe simplicity of the function isn't the point, the idea is that the language locale is a piece of shared state between all instances of the Translation component. With this approach, all instances of the Translation component are able to know which translation set to use without developers having to drill down a "locale" prop through the entire component hierarchy everywhere text is used (i.e. the entire application)