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

[data grid] how to update column header titles when switching language without reseting data-grid state #13055

Open
layerok opened this issue May 8, 2024 · 11 comments
Labels
component: data grid This is the name of the generic UI component, not the React module! enhancement This is not a bug, nor a new feature i18n internationalization l10n localization support: commercial Support request from paid users support: premium standard Support request from a Premium standard plan user. https://mui.com/legal/technical-support-sla/

Comments

@layerok
Copy link
Contributor

layerok commented May 8, 2024

The problem in depth

I know that columns prop should keep the same reference between two rerenders to avoid loosing data grid state.
But If I don't update columns prop when switching language then column headers aren't translated. If I update columns prop then data grid state is reset. What should I do in this situation?

Link to sandbox

Steps:

  1. resize some column
  2. change language
datagrid-state-reset-when-switching-language.mov

Your environment

No response

Search keywords: i18next translation column language data grid
Order ID: 74777

@layerok layerok added status: waiting for maintainer These issues haven't been looked at yet by a maintainer support: commercial Support request from paid users labels May 8, 2024
@layerok layerok changed the title [question] how to update column header texts when switching language without reseting data-grid state [question] how to update column header titles when switching language without reseting data-grid state May 8, 2024
@layerok
Copy link
Contributor Author

layerok commented May 8, 2024

well I found something

instead of doing this

const { t } = useTranslation();
const columns = useMemo(() => [
  {
    field: "first_name",
    headerName: t("first_name")
  },
  {
    field: "last_name",
    headerName: t("last_name")
  },
], [t]);

I can do this

const columns = useMemo(() => [
  {
    field: "first_name",
    renderHeader: () => <Trans i18nKey="first_name"/>
  },
  {
    field: "last_name",
    renderHeader: () => <Trans i18nKey="last_name"/>
  },
], []);

@layerok
Copy link
Contributor Author

layerok commented May 8, 2024

Title of a custom filter operator also doesn't update when switching languages.
But if you pass the Trans component instead of a simple string, it will update

const { t } = useTranslation();
const columns = useMemo(() => [
  {
    field: "first_name",
    renderHeader: () => <Trans i18nKey="first_name"/>,
    filterable: true,
    filterOperators: [
      {
        value: "contains",
        getApplyFilterFn: () => null,
        // @ts-ignore
        label: <Trans i18nKey="header-operators.mode.contains"/>, // <-- it will update when switching languages
        // @ts-ignore
        headerLabel: <Trans i18nKey="header-operators.mode.contains"/>, // <-- it will update when switching languages
      },
      {
        value: "exact",
        getApplyFilterFn: () => null,
        label: t("header-operators.mode.exact"), // <-- it won't update when switching languages
        headerLabel: t("header-operators.mode.exact"), // <-- it won't update when switching languages
      },
      // ... other filter operators
    ]
  },
  // ... other column definitions 
], []);

I had to use ts-ignore because typescript is unhappy about Trans being passed to a property that accepts only a value of type string

@zannager zannager added support: question Community support but can be turned into an improvement component: data grid This is the name of the generic UI component, not the React module! support: premium standard Support request from a Premium standard plan user. https://mui.com/legal/technical-support-sla/ labels May 8, 2024
@michelengelen
Copy link
Member

Hey @layerok ...
Sry for the late reply. I had some time off.
It seems you solved your issue already.
Is there anything else we can help you with?

@michelengelen michelengelen added status: waiting for author Issue with insufficient information l10n localization i18n internationalization and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer support: question Community support but can be turned into an improvement labels May 13, 2024
@michelengelen michelengelen changed the title [question] how to update column header titles when switching language without reseting data-grid state [data grid] how to update column header titles when switching language without reseting data-grid state May 13, 2024
@layerok
Copy link
Contributor Author

layerok commented May 13, 2024

@michelengelen I am not satisfied with my solution
Firstly, I don't like using ts-ignore
Secondly, I don't like using renderHeader because I can't just return translation in the renderHeader callback
because then the DataGrid will render the unstyled header. I need to wrap it in some component first. And this wrapping makes column config larger and harder to maintain.

renderHeader: () => <HeaderCell><Trans i18nKey="some-key"/></HeaderCell>

I think this problem requires solution at the library level
How I see a perfect solution

const columns = useMemo([
  {
    field: 'title',
    headerName: () => t('title'),
    filterOperators: [
      {
        label: () => t('contains'),
        headerLabel: () => t('contains')
      }
    ]
  }
], []);

const apiRef = useGridApiRef();
  
useLayoutEffect(() => {
  const forceUpdate = () => {
    apiRef.current.forceUpdate();
    apiRef.current.updateColumns(columns)
  };
  i18n.on("languageChanged", forceUpdate);
  return () => i18n.off("languageChanged", forceUpdate);
}, [apiRef, columns]);
  
return <DataGrid columns={columns} />

but right now this is impossible because headerName doesn't accept a callback value

I guess this issue is becoming more of a feature request than a question

@github-actions github-actions bot added status: waiting for maintainer These issues haven't been looked at yet by a maintainer and removed status: waiting for author Issue with insufficient information labels May 13, 2024
@layerok
Copy link
Contributor Author

layerok commented May 13, 2024

Workaround 2

const columnsRef = useRef<ExtendedGridColDef[]>([
  {
    field: 'title',
    lazyHeaderName: () => t('title'),
    headerName: "",
  }
], []);


const apiRef = useGridApiRef();

useLayoutEffect(() => {
  const forceUpdate = () => {
    apiRef.current.forceUpdate();
    apiRef.current.updateColumns(columnsRef.current.map((column) => ({
      ...column,
      headerName: column.lazyHeaderName()
    }));
  };
  i18n.on("languageChanged", forceUpdate);
  return () => i18n.off("languageChanged", forceUpdate);
}, [apiRef, columnsRef.current]);

return <DataGrid columns={columnsRef.current}/>

@michelengelen
Copy link
Member

I am not sure where the problem is:
This does work for me.

const translations = {
  first_name: {
    en: 'First Name',
    de: 'Vorname',
  },
  last_name: {
    en: 'Last Name',
    de: 'Nachname',
  },
};

export default function DataGridDemo() {
  const [lang, setLang] = React.useState<'en' | 'de'>('en');
  // dummy function to just return some values from an object
  const t = (key: keyof typeof translations) => translations[key][lang];

  const HeaderCell = ({ labelKey }: { labelKey: keyof typeof translations }) => (
    <div>{t(labelKey)}</div>
  );

  const columns = React.useMemo(
    () => [
      { field: 'id', headerName: 'ID', width: 90 },
      {
        field: 'firstName',
        width: 150,
        editable: true,
        renderHeader: () => <HeaderCell labelKey="first_name" />,
      },
      {
        field: 'lastName',
        width: 150,
        editable: true,
        renderHeader: () => <HeaderCell labelKey="last_name" />,
      },
    ],
    [t],
  );
...

Wouldn't this be sufficient?

This does work like as well when using:

const columns = React.useMemo(
  () => [
    { field: 'id', headerName: 'ID', width: 90 },
    {
      field: 'firstName',
      headerName: t('first_name'),
      width: 150,
      editable: true,
    },
    {
      field: 'lastName',
      headerName: t('last_name'),
      width: 150,
      editable: true,
    },
  ],
  [t],
);

Is i18n not updating the t function on a language change?

@michelengelen
Copy link
Member

I would need setup a dev environment with next for this. This might take until tomorrow though. Would that be ok?

@michelengelen michelengelen added status: waiting for author Issue with insufficient information and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels May 13, 2024
@layerok
Copy link
Contributor Author

layerok commented May 13, 2024

Yes, and you can use my sandbox. next is already there

@github-actions github-actions bot added status: waiting for maintainer These issues haven't been looked at yet by a maintainer and removed status: waiting for author Issue with insufficient information labels May 13, 2024
@layerok
Copy link
Contributor Author

layerok commented May 13, 2024

Is i18n not updating the t function on a language change?

The problem is not with i18next but with the columns prop.
The columns prop must keep the reference between two rerenders. Otherwise, the datagrid state will be reset
So putting the t function into dependency array of useMemo is not an option.

const columns = React.useMemo(() => [{ field: 'id', headerName: t('id'), width: 90 }], [t]);

If you don't put the t function into the dependency array then the header won't be updated when the language changes
We can use renderHeader to workaround this problem, but I've already explained why I don't like this solution. And also what to do with filterOperator label, it doesn't have a renderLabel option.

@michelengelen
Copy link
Member

OK, thanks ... got it.
It does indeed look as if there is no satisfying solution (afaik) to this.
I will add this to the board and see what the team has to say about it.

@michelengelen michelengelen added enhancement This is not a bug, nor a new feature and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels May 14, 2024
@flaviendelangle
Copy link
Member

You could probably use apiRef.updateColumns and pass the new headers, but it's not the prettier solution imaginable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: data grid This is the name of the generic UI component, not the React module! enhancement This is not a bug, nor a new feature i18n internationalization l10n localization support: commercial Support request from paid users support: premium standard Support request from a Premium standard plan user. https://mui.com/legal/technical-support-sla/
Projects
None yet
Development

No branches or pull requests

4 participants