aralroca / next-translate

Next.js plugin + i18n API for Next.js 🌍 - Load page translations and use them in an easy way!
MIT License
2.62k stars 207 forks source link

Add optional param t to Trans component #517

Open piiok opened 3 years ago

piiok commented 3 years ago

I’m making a page and it has a view that uses aprox 25 json files. But I need to load only one file by default, and the other files will be needed based on how the user interact with it. I used DynamicNamespace but this component load all files by default, this is not necesary because it may use only use 6 of 25 json files. I saw the source code and I think this problem it's easy to solve if an optional parameter can be added to the Trans component.

aralroca commented 3 years ago

@piiok DynamicNamespace loads the files that you specify... Did you try something like this?

import React, { useState } from 'react'
import Trans from 'next-translate/Trans'
import DynamicNamespaces from 'next-translate/DynamicNamespaces'

const [INITIAL, SECOND, THIRD, FINAL] = [0, 1, 2, 3]
const namespaces = ['initial', 'second', 'third', 'final']

export default function ExampleWithDynamicNamespace() {
  const [status, setStatus] = useState(INITIAL)
  const [ns, setNs] = useState(namespaces.slice(0, 1))

  function nextStatus() {
    const newStatus = status + 1
    setStatus(newStatus)
    setNs(oldNs => [...oldNs, namespaces[newStatus]])
  }

  return (
    <DynamicNamespaces
      // Force to download on each status change
      key={`ns-${status}`}
      // Using dynamic import only downloads the chunks that have not 
      // yet been downloaded, those that have already been downloaded.
      // are not downloaded again.
      namespaces={ns}
      fallback="Loading..."
    >
      {(() => {
        if (status === INITIAL) return <Trans i18nKey="initial:title" />
        if (status === SECOND) return <Trans i18nKey="second:title" />
        if (status === THIRD) return <Trans i18nKey="third:title" />
        if (status === FINAL) return <Trans i18nKey="final:title" />
      })()}
      {status !== FINAL && <button onClick={nextStatus}>Next status</button>}
    </DynamicNamespaces>
  )
}
piiok commented 3 years ago

The json is split because there are so much content. So i have 25 namespaces for each case. But i need load json file only if user need this namespace and DynamicNamespace load all.

piiok commented 3 years ago

I'm trying to do my own DynamicNamespace

import React, { useContext, useEffect, useState } from 'react';
import useSWR from 'swr';
import { swrFetcher } from '@/utils/api';
import { internalApiPaths } from '@/constants';
import PropTypes from 'prop-types';
import I18nProvider, { InternalContext } from 'next-translate/I18nProvider';
import useTranslation from 'next-translate/useTranslation';

const GetterNamespaces = (props) => {
  const internal = useContext(InternalContext);

  const { namespace: namespaceProp, loadedNamespace, loading: Loading, deleteUnused, children } = props;
  const { lang } = useTranslation();

  const [needNs, setNeedNs] = useState([]);
  const [ns, setNs] = useState({});
  const [loaded] = useState(loadedNamespace);
  const { data, isValidating } = useSWR(
    needNs.length !== 0 ? [internalApiPaths.NAMESPACE_FILE, 'POST', lang, ...needNs] : null,
    swrFetcher
  );

  const loading = !data && isValidating;

  useEffect(() => {
    if (namespaceProp && namespaceProp !== loaded && !Object.keys(ns).includes(namespaceProp)) {
      setNeedNs([namespaceProp]);
    }
  }, [namespaceProp]);

  useEffect(() => {
    if (data) {
      const newNs = Object.assign(deleteUnused ? {} : ns, data);
      setNs(newNs);
    }
  }, [data]);

  return loading ? (
    Loading
  ) : (
    <I18nProvider lang={lang} namespaces={ns}>
      <InternalContext.Provider value={{ ns, config: internal.config }}>{children}</InternalContext.Provider>
    </I18nProvider>
  );
};

GetterNamespaces.defaultProps = {
  loadedNamespace: '',
  deleteUnused: true,
  loading: 'loading...',
  children: null,
};

GetterNamespaces.propTypes = {
  namespace: PropTypes.string.isRequired,
  loadedNamespace: PropTypes.string,
  deleteUnused: PropTypes.bool,
  loading: PropTypes.oneOfType([PropTypes.element, PropTypes.string]),
  children: PropTypes.element,
};

export default GetterNamespaces;
aralroca commented 3 years ago

@piiok did you finally solve the problem?