In this document, we will showcase the capabilities of each framework and see how they map to each other and provide a foundation for how one would go about . migrating from react-i18next to Transifex Native Initialization Both frameworks require you to initialize a global object that will serve as a "gateway" between your application and the ability to localize it. In react-i18next, this looks like this (example taken from the official website): i18n ; { initReactI18next } ; resources = { : { : { : , }, }, : { : { : , }, }, }; i18n .use(initReactI18next) .init({ resources, : , : { : }, }); import from "i18next" import from "react-i18next" const en translation 'Hello world' 'Hello world' el translation 'Hello world' 'Καλημέρα κόσμε' lng 'en' interpolation escapeValue false This snippet doesn't tell the whole story. The translated content (the 'resources' field) can originate from a variety of sources and can also be added after the initialization. Also, the 'init' method accepts many more arguments and allows you to customize the experience to a great extent. With Transifex Native, you lose some of this flexibility, given that the source of truth for your content is always the content on transifex.com, and in return, you end up with the simpler: { tx } ; tx.init({ : , }); import from '@transifex/native' token '...' There are other initialization options in Transifex Native that allow you to: Limit the content you will receive from Transifex with the 'filterTags' field What to do when a translation is missing with the 'missingPolicy' field What to do when the translation cannot be rendered (because, for example, the translator made a formatting mistake) with the 'errorPolicy' field Keys react-i18next react-i18next is key-based. This means that if your component code looks like this: { { t } = useTranslation(); ; } ( ) function Paragraph const return {t('a_key')} < > p </ > p Then you need to have the exact same key in your 'resources': resources = { : { : { : , }, }, : { : { : , }, }, }; const en translation 'a_key' 'Hello world' el translation 'a_key' 'Καλημέρα κόσμε' If the key is not found in the resources, then the key itself will be rendered in the application. For this reason, it makes sense to use the source string itself as the key: resources = { : { : { : , }, }, : { : { : , }, }, }; { { t } = useTranslation(); ; } const en translation 'Hello world' 'Hello world' el translation 'Hello world' 'Καλημέρα κόσμε' // i18n.use(...).init(...) ( ) function Paragraph const return {t('Hello world')} < > p </ > p However, this gets a little difficult to manage if you have plurals. Let's assume you have the following component: { [count, setCount] = useState( ); { t } = useTranslation(); ( <p> {t('You have {{count}} messages', { count })} </p> <p> <button onClick={() => setCount(count - 1)}>-</button> <button onClick={() => setCount(count + 1)}>+</button> </p> ( ) function Paragraph const 2 const return <> ); } </> And the following resources: resources = { : { : { : , : , }, }, : { : { : , : , }, }, }; const en translation 'You have {{count}} messages_one' 'You have one message' 'You have {{count}} messages_other' 'You have {{count}} messages' el translation 'You have {{count}} messages_one' 'Έχετε ένα μήνυμα' 'You have {{count}} messages_other' 'Έχετε {{count}} μηνύματα' Then the application will work as expected. When the 'count' becomes 1, the application will render "You have one message". If the keys are not in the resources, however, or if they are not formatted properly, you"ll get "You have 1 messages". Transifex Native With Transifex Native, the default behaviour is to not worry about keys at all. The string itself becomes the key. In case of plurals, the fact that the strings are formatted with ICU MessageFormat means that it contains all the information needed to render in both the source and target languages: { [count, setCount] = useState( ); ( <p> <T _str="{count, plural, one {You have one message} other {You have # messages}}" count={count} /> </p> <p> <button onClick={() => setCount(count - 1)}>-</button> <button onClick={() => setCount(count + 1)}>+</button> </p> </> ); } ( ) function Paragraph const 2 return <> you want to use keys, however, probably because you are using Transifex Native with a design tool like Figma or , You can supply the '_key' prop: If Sketch { ( ) function Paragraph return ; } < = = /> T _key "a_key" _str "Hello world" In both cases, the source language is editable by using the "edit source strings" feature of transifex.com. t-function react-i18next Most of the translation methods offered by react-i18next boil down to offering you a javascript function called 't'. Using this function will render the argument in the correct language and make sure the component will re-render when the selected language changes. 'useTranslation' hook { useTranslation } ; { { t } = useTranslation(); ; } import from 'react-i18next' ( ) function Paragraph const return {t('Some text')} < > p </ > p 'withTranslation' (Higher-Order Component) { withTranslation } ; { ; } Paragraph = withTranslation()(_Paragraph); { import from 'react-i18next' ( ) function _Paragraph { t } return {t('Some text')} < > p </ > p const ( ) function App return ; } < /> Paragraph 'Translation' (Render Prop) { Translation } ; { ( <p>{t('Some text')}</p> ); } import from 'react-i18next' ( ) function Paragraph return {(t) => < > Translation } </ > Translation 'I18nProvider' context { useContext } ; { I18nContext, I18nextProvider } ; i18n.use(...).init({...}); ReactDOM.render( <App /> , .getElementById( ), ); { <p>{t('Some text')}</p> import from 'react' import from 'react-i18next' < = > I18nextProvider i18n {i18n} </ > I18nextProvider document 'root' ( ) function App return ; } function Paragraph() { const { i18n: { t } } = useContext(I18nContext); return < /> Paragraph ; } Transifex Native With Transifex Native you can achieve the same result by using the 'useT' hook: { useT } ; { t = useT(); ; } import from '@transifex/react' ( ) function Paragraph const return {t('Some text')} < > p </ > p or by using the preferable 'T-component': { T } ; { <T _str="Some text" /></p> import from '@transifex/react' ( ) function Paragraph return < > p ; } Interpolation and Plurals react-i18next react-i18next allows you to interpolate dynamic values into your translations by using the '{{name}}' syntax, as demonstrated here: { useTranslation } ; { { t } = useTranslation(); ; } import from 'react-i18next' ( ) function Paragraph const return {t('Hello {{name}}', { name: 'Bob' })} < > p </ > p or { useTranslation, Trans } ; { { t } = useTranslation(); ( <Trans t={t}> Hello <strong>{{name}}</strong> </Trans> ); } import from 'react-i18next' ( ) function Paragraph const return < > p </ > p In order to support plurals, you have to pay very close attention to your keys: resources = { : { : { : , : }, }, : { : { : , : } }, }; i18n.use(...).init({ ..., resources }); { count = ; { t } = useTranslation(); ; } const en translation messages_one 'You have 1 message' messages_other 'You have {{count}} messages' el translation messages_one 'Έχετε 1 μήνυμα' messages_other 'Έχετε {{count}} μηνύματα' ( ) function Paragraph const 3 const return {t('messages', { count })} < > p </ > p Or { count = ; { t } = useTranslation(); ( <Trans t={t} i18nkey="messages" count={count}> You have {{count}} messages </Trans> ); } ( ) function Paragraph const 3 const return < > p </ > p Transifex Native Transifex Native uses ICU MessageFormat natively (pun intended). This gives you a solid background to work with interpolation: { T } ; { <T _str="Hello {name}" name="Bob" /></p> <p> <T _str="{cnt, plural, one {You have 1 message} other {You have # messages}}" cnt={count} /> </p> ); } import from '@transifex/react' ( ) function Paragraph return < > p ; } ICU MessageFormat also offers you industry standard capabilities for plurals: function Messages() { const count = 3; return ( And also for select statements and number formatting: <T _str= gender={gender} /> "It's a {gender, select, male {boy} female {girl}}" < = = /> T _str "Today is {today, date}" today {today} Translation with Formatted Text react-i18next When you want to translate HTML content, react-i18next offers the '<Trans>' component: { useTranslation, Trans } ; { { t } = useTranslation(); ( <p>Some <strong>bold</strong> text</p> ); } import from 'react-i18next' ( ) function Paragraph const return < = > Trans t {t} </ > Trans In order for this to work, the source text in i18n's resources must have the form <1>Some <2>bold</2> text</1> Which you have to generate by hand. Transifex Native With Transifex Native you have the option to use the 'UT'-component: { UT } ; { <p>Some <strong>bold</strong> text</p> import from '@transifex/react' ( ) function Paragraph return < = UT _str " " />; } Or to interpolate the outer template with react elements (that can be also translated): { T } ; { ( <p>Some {strong} text</p> <strong><T _str="bold" /></strong>} /> ); } import from '@transifex/react' ( ) function Paragraph return < = T _str " " = strong { With both ways, what your translators will be asked to work on will have the exact same form as the argument you used for '_str'. Language Selection Both frameworks allow you to dynamically set the currently active language. With react-i18next you can do: { ( <button onClick={() => i18n.changeLanguage('en')}>English</button> <button onClick={() => i18n.changeLanguage('el')}>Greek</button> ( ) function Languages return <> ); } </> And with Transifex Native you can do the similar: { ( <button onClick={() => tx.setCurrentLocale('en')}>English</button> <button onClick={() => tx.setCurrentLocale('el')}>Greek</button> ( ) function Languages return <> ); } </> What Transifex Native can offer you above this is due to the fact that transifex.com is your source-of-truth not only for your translation content but also for your available languages. Taking this into account, you can do: { useLanguages } ; { languages = useLanguages(); ( <button key={code} onClick={() => tx.setCurrentLocale(code)}> {name} </button> import from '@transifex/react' ( ) function Languages const return {languages.map(({ code, name }) => ( <> ))} ); } </> Or the more direct: { LanguagePicker } ; { import from '@transifex/react' ( ) function Languages return ; } < = /> LanguagePicker className "pretty" Which will render a language selector dropdown. After this, languages added on transifex.com will be reflected in your application without requiring you to publish a new release. String Extraction react-i18next There are some third-party tools to help with extracting strings from your code in the i18next ecosystem. One that can work with react applications is . Assuming you have the following application: i18next-parser { { t } = useTranslation(); ( <p> {t('Hello world 1')} </p> <p> <Trans t={t}> Hello <strong>world</strong> 2 </Trans> </p> <p> {t('Hello {{count}} worlds', { count: 3 })} </p> export default ( ) function App const return <> ); } </> And use this configuration: .exports = { : , : { : [ ], }, }; module useKeysAsDefaultValue true lexers js 'JsxLexer' Then, if you run i18next-parser, you will end up with the following resource file: { : , : , : , : } "Hello world 1" "Hello world 1" "Hello <1>world</1> 2" "Hello <1>world</1> 2" "Hello {{count}} worlds_one" "Hello {{count}} worlds" "Hello {{count}} worlds_other" "Hello {{count}} worlds" ΅΅΅Which is a good starting point and even takes care of the unintuitive key generation for the '<Trans>' component and of plurals. After that, you will of course have to worry about how to generate translated versions of these files (hint: you should upload them to Transifex) and how to import these files into the application when it's running. Transifex Native With Transifex Native, you don't have to worry about files at all. You simply have to run: TRANSIFEX_TOKEN=... export TRANSIFEX_SECRET=... npx txjs-cli push src export After that: Your content will be available on transifex.com for your translators to work on Any ready translations will be available to your application during runtime Namespaces / Lazy loading react-i18next and i18next in general offers extensive support for compartmentalizing your translated content and, via plugins, loading these compartmentalized namespaces into your application via HTTP after the initial booting of the application. Transifex Native has limited support for namespacing via tags. You can add a tag to a translatable string with the '_tags' property: <T _str= _tags= /> "Hello world" "main" or by specifying tags during the extraction cli execution: npx txjs-cli push --append-tags=helpdesk src/helpdesk npx txjs-cli push --append-tags=dashboard src/dashboard Then, when initializing the 'tx' object, you can specify which tags you want to filter against: tx.init({ : ..., : }); token filterTags 'android' This is useful in case you are using the same transifex.com project for different platforms and you want each platform to only pull translations that are relevant. As of now, we don't support lazy loading of translations but we have plans to implement this in the near future. Migration Guide Preparing the code For minimal inconvenience, you should replace the invocation of react-i18next's 't' function with Transifex Native's 't' function. From: { useTranslation } ; { { t } = useTranslation(); ; } import from 'react-i18next' ( ) function Paragraph const return {t('Some text')} < > p </ > p to { useT } ; { t = useT(); ; } import from '@transifex/react' ( ) function Paragraph const return {t('Some text')} < > p </ > p However, it may be preferable to use the T-component: { T} ; { <T _str="Some text" /></p> import from '@transifex/react' ( ) function Paragraph return < > p ; } Simple variable interpolation is done with single curly brackets instead of double. From: { useTranslation } ; { { t } = useTranslation(); ; } import from 'react-i18next' ( ) function Paragraph const return {t('Hello {{username}}', { username: 'Bob' })} < > p </ > p to: { T } ; { <T _str="Hello {username}" username="Bob" /></p> import from '@transifex/react' ( ) function Paragraph return < > p ; } For formatted content, your best bet is to replace '<Trans>' with <UT />'. From: { useTranslation, Trans } ; { { t } = useTranslation(); ( <p>Some <strong>bold</strong> text</p> ); } import from 'react-i18next' ( ) function Paragraph const return < = > Trans t {t} </ > Trans To: { UT } ; { <p>Some <strong>bold</strong> text</p> import from '@transifex/react' ( ) function Paragraph return < = UT _str " " />; } Migrating the translated content First, you'll have to upload your current translations to transifex.com on a file-based project. After creating the project, you can use the transifex-client to help with uploading the resources (one namespace per resource): # Install the client wget https: tar xf tx-linux-amd64.tar.gz tx rm tx-linux-amd64.tar.gz mv tx ~ translations/ <lang>.json \ --type=KEYVALUEJSON \ locales/helpdesk/en.json # Push the content to transifex tx push --source --translation --all //github.com/transifex/cli/releases/download/v0.3.0/tx-linux-amd64.tar.gz /bin # Set up the mapping to transifex.com tx init tx add \ --organization=... \ --project=... \ --resource=translations \ --file-filter=locales/ .json \ --type=KEYVALUEJSON \ locales/translations/en.json tx add \ --organization=... \ --project=... \ --resource=helpdesk \ --file-filter=locales/helpdesk/ < > lang Next, you need to create a Native project. Make sure you keep the public and secret tokens for later. Before pushing the content to the new project, you need to: Add the target languages to the new project Add both projects to the same TM group Now in your local project, use the extraction cli to push content to the native project in transifex.com (use tags for namespacing as discussed above: npm install --save-dev @transifex/cli npx txjs-cli push --token=... --secret=... src Because we put both projects in the same TM group, existing translations will be used to fill up the new ones. In case you had to make minor changes to some of the strings while migrating the code, take a look in the editor to fill these in. Even if they were not filled up automatically, partial TM matches should make this job easy. Finally, make sure you initialize the 'tx' object when your application boots up and you should be good to go! Once everything is up and running, you can delete the old file-based project. Wrapping Up Wanna share a quick React localization guide with a fellow developer? ! Find it on this page This post was co-authored by Konstantinos Bairaktaris and Mike Giannakopoulos Find Transifex on our: , , , and . Website Facebook Twitter LinkedIn