Skip to content
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

Translation outside React Component #909

Closed
Creaa opened this issue Jul 24, 2019 · 37 comments
Closed

Translation outside React Component #909

Creaa opened this issue Jul 24, 2019 · 37 comments

Comments

@Creaa
Copy link

Creaa commented Jul 24, 2019

I tried to translate strings exported outside React Components.
But any methods in docs doesn't work, and I guess its normal, because its typically React methods.

I've found similar topic, and there it is solution about importing instance.
But, when I do it I've had an error:

 Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.

Probably it because of this line in the instance:

i18n
    .use(initReactI18next) 

When I delete this line, the error is gone, but the translations are not included.
How can I plug this simple .js file to React translations?

@jamuhl
Copy link
Member

jamuhl commented Jul 24, 2019

Outside of react components just use the i18next instance:

import i18n from `./i18n`

i18n.t(...);

Just be aware of the normal catch you's:

@Creaa
Copy link
Author

Creaa commented Jul 24, 2019

Yep, I did and I described an error above.
I just import this instance, and the error appeared.
Dont have to even use it.

This is my istance:

import i18n from "i18next";
import { initReactI18next } from "react-i18next";

const resources = {
    en: {
        translation: {
           ...
}
};

i18n
    .use(initReactI18next) 
    .init({
        resources,
        lng: "en",

        keySeparator: false, 

        interpolation: {
            escapeValue: false 
        }
    });

export default i18n;

I don't know whats going on. I use a provider in index to encapsulate i18n in DOM tree.

@jamuhl
Copy link
Member

jamuhl commented Jul 24, 2019

Somewhere you're using a hook inside a class component...that's what the warning says...

@Creaa
Copy link
Author

Creaa commented Jul 24, 2019

Correct, I missed single useTranslation in another pure js file. When I removed that now everything is working as well, including translations.

Thank you so much.

@Creaa Creaa closed this as completed Jul 24, 2019
@Creaa
Copy link
Author

Creaa commented Jul 26, 2019

I've got the issue, what you mentioned. The language doesn't want to change. Of course I checked docs and link sent by you.

But, nothing works, I try to fire this on a change language event, a then add additional namespace.
Nothing happened.
Should I wrap this t function in something?

I noticed that, when I set defaultNs to correct value, everything is change (class and functional React components) to a proper state, but only the outside React files don't respond.
Have you met with this case?

@Creaa Creaa reopened this Jul 26, 2019
@jamuhl
Copy link
Member

jamuhl commented Jul 26, 2019

outside react files...what should trigger there a retranslation? once translated static content it's done...there is nothing like the render tree which gets rerendered and doing a fresh translation ---> use the events for that...https://www.i18next.com/overview/api#onlanguagechanged

@Creaa
Copy link
Author

Creaa commented Jul 26, 2019

I use it, but should I wrap this translations in a function?
I mean... This t translations can detect, that default namespace has been changed, and get the new data?

Code below:

i18n.on('languageChanged', language => {
    i18n.setDefaultNamespace('de')
})

Still nothing.

@jamuhl
Copy link
Member

jamuhl commented Jul 26, 2019

sorry guess i don't get what you like to do...why you need to change the namespace? makes no sense to me...what is the use case? do you have same keys on different namespaces and different values?

@jamuhl
Copy link
Member

jamuhl commented Jul 26, 2019

and why set namespace to de

i think you do something very wrong...above in code you have in resources the namespace translation now you like to access "de" namespace...!??!??

@jamuhl
Copy link
Member

jamuhl commented Jul 26, 2019

for change language just call i18n.changeLanguage(lng)

@Creaa
Copy link
Author

Creaa commented Jul 26, 2019

I try to force this t elements, imported from an instance to change itself.
You wrote that reload the namespace should work, and it doesn't, so I tried to change reloaded default namespaces, still with any effects.
It seems like this t elements dont affect on any changes.
I just want to force them to get new data, when the language is change. Just like of the rest code in React components.

@jamuhl
Copy link
Member

jamuhl commented Jul 26, 2019

make a codesandbox sample of what you like to do / have error with...i can't understand what you like to do, what is wrong by your text

or at least paste relevant code here

@Creaa Creaa closed this as completed Jul 26, 2019
@Creaa
Copy link
Author

Creaa commented Jul 26, 2019

The structure is too complicated to put it on code sandbox, so I try to reproduce that issue.

fileOutsideReact.js :

import i18n from '/../i18n'
i18n.on('languageChanged', language => {
  this code trigger on language change
})
export default {
string1: i18n.t('text1', 'exampleValueForString1'),  <----- GET NEW VALUE ON LANGUAGE CHANGE
string2:  i18n.t('text2', 'exampleValueForString2')  <----- GET NEW VALUE ON LANGUAGE CHANGE
};

This example is simplified. In this case I can overrride this values in the function. But in real project this file exports a tons of values, and redundant would be painfully.

i18n.changeLanguage(lng)

Is inside React component

When I change the language strings 1 and 2, still are in prev language.
They dont change itself. So how I can force to change again, when the user pick new language?

PS: Dont care about namespaces, I just tried anything to force translations to update.

@Creaa Creaa reopened this Jul 26, 2019
@adrai
Copy link
Member

adrai commented Jul 26, 2019

you have to call t function again... exported values will not change magically... i would suggest to redesign it... simply use i18next.t() where you need it...

@jamuhl
Copy link
Member

jamuhl commented Jul 26, 2019

i18n.t returns a string --- right?

export default { foo: 'bar' } --- exports an object with a string foo --- right?
export default { foo: i18n.t('foo') } --- still exports an object with a string foo --- right?

how does a string magically changes? - it does not --- right?

So this might work?

import { i18n } from './i18n';

const myExport = {};

function fill() {
  myExport.foo = i18n.t('foo');
}

// run it initial
fill();

// bind some events and fill values again (doing the magic you expect to happen magically)
i18n.on('languageChanged init',() => {
  fill(); // fills myExport with new value for foo based on current i18n lng
});

// export the const
export default myExport;

@Creaa
Copy link
Author

Creaa commented Jul 26, 2019

Problem is solved. For others if they are looking for solution in similar case.

I loop through object, and override a value, where is i18n translation function.
I thought that, export default would be work dynamically, but it wouldn't.

Thanks for advice.

@sebastialonso
Copy link

@Creaa so what @jamuhl proposed works? Did you end implementing that?

@sebastialonso
Copy link

@jamuhl Your implementation in not working on my end, but I don't have any errors.

Maybe it's because of loading times. Could you explain the bit about namespaces and how would that help me?

@adrai
Copy link
Member

adrai commented Jan 10, 2020

Can you create a sample and/or enable debug: true ?

@sebastialonso
Copy link

Oh my god. I'm an idiot. It works perfectly. debug: true helped me realize there was a missing key.

Thanks!

@knowankit
Copy link

@jamuhl Thanks a lot for the solution.

@jvaclavik
Copy link

import i18n from `./i18n`

i18n.t(...);

This is working, but I have a problem with i18next-scanner because it's not getting my key automatically. Do you have an idea of how to configure it?

Thanks!

@callinutter
Copy link

If you're calling the t function from outside a react component, you should call the t function from i18next instead. Also if the default namespace is different than translation, you can either change the default namespace, or pass the namespace as a prefix to the t function, like this:

import i18next from 'i18next;

const someFunction = () => {
   return i18next.t('namespace: title')
}

@diegohdez90
Copy link

I18n.t() function is working only inside in functions. There is a way to use them in variables.

@sekoyo
Copy link

sekoyo commented Jul 8, 2022

People are talking about instances but are just importing i18n via import i18n from "i18next" and then re-exporting it, so what's the point of importing it from that local file vs just doing import i18n from "i18next" elsewhere? 🤔

@jamuhl
Copy link
Member

jamuhl commented Jul 8, 2022

@dominictobias the instance is the same...important is that your i18n.js file (or where ever you init that instance) is imported and included into your build. re-exporting helps most to not forget it ;)

different situation if you create multiple instances - but that's another topic

@bildungsroman
Copy link

Outside of react components just use the i18next instance:

import i18n from `./i18n`

i18n.t(...);

Just be aware of the normal catch you's:

@jamuhl Could you please expand on this? I'm in a similar situation, in which I have a React app where most translations are done inside components, and useTranslation() works great in those cases. However, I also have a bunch of stand-alone util/store TS functions where I'm trying to import my i18n instance, but it's never initialized so it's not working. I haven't been able to find any examples of how to do both kinds of instances in a React app.

@jamuhl
Copy link
Member

jamuhl commented Mar 17, 2023

@bildungsroman I guess inside your ./i18n file you init i18next -> but you still get some "i18next is not initialized" warnings from your util/store functions?

The reason for this (I guess) is that those files get imported earlier or too early.

#1236 (comment) might help for using i18n.t outside of react functions (and react to language changes, ....)

@ddubrava
Copy link

@dominictobias the instance is the same...important is that your i18n.js file (or where ever you init that instance) is imported and included into your build. re-exporting helps most to not forget it ;)

different situation if you create multiple instances - but that's another topic

do you get this instance from the i18next.use(initReactI18next).init()? It returns a Promise, so how do you export this instance?

@adrai
Copy link
Member

adrai commented Sep 19, 2023

@dominictobias the instance is the same...important is that your i18n.js file (or where ever you init that instance) is imported and included into your build. re-exporting helps most to not forget it ;)
different situation if you create multiple instances - but that's another topic

do you get this instance from the i18next.use(initReactI18next).init()? It returns a Promise, so how do you export this instance?

i18next.use(initReactI18next).init()
export default i18next

@ddubrava
Copy link

ddubrava commented Sep 19, 2023

@dominictobias the instance is the same...important is that your i18n.js file (or where ever you init that instance) is imported and included into your build. re-exporting helps most to not forget it ;)
different situation if you create multiple instances - but that's another topic

do you get this instance from the i18next.use(initReactI18next).init()? It returns a Promise, so how do you export this instance?

i18next.use(initReactI18next).init()
export default i18next

ah, okay. I thought I need to get the instance from the init. Thank you for your fast replies! This ETA is insane :d

@AndonMitev
Copy link

AndonMitev commented Dec 28, 2023

Hey i have issue calling i18n.t() in a object, so this is the example obj:

import i18n from '../i18n';

export const sec1 = {
  title: i18n.t('test.text1'),
};

this one has not been translated successfully on the page, however if i convert it into a function like this:

import i18n from '../i18n';

export const sec1 = () => ({
title: i18n.t('test.text1'),
});

is working, any idea how i can make it work with plain object?

PS: I have added:

i18n.on('loaded', () => {
 console.log('loaded');
});

and i can see loaded is called after constant file is executed, so how to prevent execution of this file until i18n is loaded?

Yes, can confirm contants.ts file is executed before i18n to be initialized, wrapped app in Suspense but again not working, any ideas?

@jamuhl
Copy link
Member

jamuhl commented Dec 28, 2023

@AndonMitev why not search in issues: #1698 (comment)

@dev-itgrapes
Copy link

I created an intermediary function like this:

import i18n from '../i18n';

const t = (textCode:string) => i18n?.t(textCode)

export const config = {
  title: t('variable_title'),
};

@jamuhl
Copy link
Member

jamuhl commented Jan 7, 2024

@dev-itgrapes just keep in mind...on language change your config.title gets not updated and will provide the value it had on time calling that t function...#1236 (comment) for the case you need that

@impana-12spec
Copy link

import React, { useState, ReactElement } from "react";
import { Box, styled } from "@mui/material";
import {
GridRowsProp,
GridRowModesModel,
DataGrid,
GridEventListener,
GridRowEditStopReasons,
} from "@mui/x-data-grid";
import { useTranslation } from "react-i18next";
import { BaseProps } from "../../types/base-props";
import LayoutBannerDetail from "../../components/layout-banner-detail/layout-banner-detail";
import HeaderActionbar from "../../components/header-actionbar/header-actionbar";
import i18next from "i18next";
import { CustomNameEdit, CustomRoleEdit } from "./grid-input-cell";

interface RowData {
id: number;
name: string;
age: number;
joinDate: string;
department: string;
}

const HeaderActionsContainer = styled(Box)display: flex; align-items: center; justify-content: flex-end;;

const initialRows: GridRowsProp = [
{
id: 1,
name: "John Doe",
age: 25,
joinDate: "2022-01-15",
department: "IT",
},
];

export const CollectionSiteTable: React.FC = ({
id,
}: BaseProps): ReactElement => {
const [rows, setRows] = useState<GridRowsProp>(initialRows);
const [rowModesModel, setRowModesModel] = useState({});

const { t } = useTranslation();

const handleRowEditStop: GridEventListener<"rowEditStop"> = (params, event) => {
if (params.reason === GridRowEditStopReasons.rowFocusOut) {
event.defaultMuiPrevented = true;
}
};

return (
<>
<LayoutBannerDetail
data-testId="layout-banner"
id={${id}-banner}
disableGutters
hideDivider
banner={
<HeaderActionbar
id={${id}-header}
text="Collection Site"
children={


}
/>
}
detail={

<DataGrid
rows={rows}
columns={COLUMNS}
editMode="row"
rowModesModel={rowModesModel}
onRowEditStop={handleRowEditStop}
onRowModesModelChange={setRowModesModel}
slotProps={{
toolbar: { setRows, setRowModesModel },
}}
/>

}
>
</>
);
};

export default CollectionSiteTable;

const COLUMNS = [
{
field: "name",
headerName: i18next.t("name", { ns: "common", defaultValue: "Name" }),
width: 180,
editable: true,
renderEditCell: (params:any) => (
<CustomNameEdit
value={params.value}
onChange={(newValue) =>
params.api.setEditCellValue({
id: params.id,
field: "name",
value: newValue,
})
}
/>
),
},
{
field: "department",
headerName: i18next.t("department", { ns: "common", defaultValue: "Department" }),
width: 220,
editable: true,
renderEditCell: (params:any) => (
<CustomRoleEdit
value={params.value}
onChange={(newValue) =>
params.api.setEditCellValue({
id: params.id,
field: "department",
value: newValue,
})
}
/>
),
},
];

i18next.init({
resources: {
en: {
common: {
name: "Name",
department: "Department",
},
},
},
lng: "en",
fallbackLng: "en",
});

the translations outside a component inside the columns object ns file name isnt accessible for this any one has solution for this?

@jamal299
Copy link

jamal299 commented Apr 23, 2024

Export the key as it is from the static file from the ts file and useTranslation to translate the key in the tsx file.

Example

en.json
{
"translation_key": "Free for all.",
},

plans.ts

export const plans = [
{
short_description: 'translation_key', // the value is the translation key
},
]

subscriptions.tsx
import { useTranslation } from 'react-i18next';
import { plans } from './utils';

const { t } = useTranslation();

return

{plans.map((plan, index) => (
{t(plan.short_description)}
))}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests