Skip to content

NextI18next:ClientSideUseTranslation

Nextjs에서 클라이언트 측 반응 구성 요소는 비동기화(async)할 수 없으므로, 몇 가지 조정한 useTranslation 함수가 포함된 파일 전체 코드.

자세한 내용은 Next-i18next 항목 참조.

client.ts

'use client';

import i18next, {type TFunction} from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import resourcesToBackend from 'i18next-resources-to-backend';
import {useEffect, useState} from 'react';
import {useCookies} from 'react-cookie';
import {
  initReactI18next,
  useTranslation as useTranslationOrg,
  type UseTranslationOptions,
} from 'react-i18next';
import {backendI18nImporter} from '@/app/lib/i18n/backend';
import {defaultOptions, LANGUAGES, COOKIE_I18N_KEY} from '@/app/lib/i18n/settings';

const runsOnServerSide = typeof window === 'undefined';

function onI18nInitializeSuccess(trans: TFunction) {
  if (i18next.options.debug) {
    console.debug(`I18n initialization was successful: ${trans}`);
  }
}

function onI18nInitializeFailure(reason: any) {
  if (i18next.options.debug) {
    console.error(`I18n initialization failed: ${reason}`);
  }
}

function onChangeLanguageSuccess(trans: TFunction) {
  if (i18next.options.debug) {
    console.debug(`Language change was successful: ${trans}`);
  }
}

function onChangeLanguageFailure(reason: any) {
  if (i18next.options.debug) {
    console.error(`Language change failed: ${reason}`);
  }
}

i18next
  .use(initReactI18next)
  .use(LanguageDetector)
  .use(resourcesToBackend(backendI18nImporter))
  .init({
    ...defaultOptions(),
    lng: undefined, // let detect the language on client side
    detection: {
      order: ['path', 'htmlTag', 'cookie', 'navigator'],
    },
    preload: runsOnServerSide ? LANGUAGES : [],
  })
  .then(onI18nInitializeSuccess)
  .catch(onI18nInitializeFailure);

export function useTranslation(
  lng?: string,
  ns?: string,
  options?: UseTranslationOptions<undefined>
) {
  const translationResponse = useTranslationOrg(ns, options);
  const {i18n} = translationResponse;
  if (runsOnServerSide && lng && i18n.resolvedLanguage !== lng) {
    i18n
      .changeLanguage(lng)
      .then(onChangeLanguageSuccess)
      .catch(onChangeLanguageFailure);
    return translationResponse;
  }

  // eslint-disable-next-line react-hooks/rules-of-hooks
  const [activeLng, setActiveLng] = useState(i18n.resolvedLanguage);

  // eslint-disable-next-line react-hooks/rules-of-hooks
  useEffect(() => {
    if (activeLng !== i18n.resolvedLanguage) {
      setActiveLng(i18n.resolvedLanguage);
    }
  }, [activeLng, i18n.resolvedLanguage]);

  // eslint-disable-next-line react-hooks/rules-of-hooks
  useEffect(() => {
    if (lng && i18n.resolvedLanguage !== lng) {
      i18n
        .changeLanguage(lng)
        .then(onChangeLanguageSuccess)
        .catch(onChangeLanguageFailure);
    }
  }, [lng, i18n]);

  // eslint-disable-next-line react-hooks/rules-of-hooks
  const [cookies, setCookie] = useCookies([COOKIE_I18N_KEY]);

  // eslint-disable-next-line react-hooks/rules-of-hooks
  useEffect(() => {
    if (cookies.i18n !== lng) {
      setCookie(COOKIE_I18N_KEY, lng, {path: '/'});
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [lng, cookies.i18n]);

  return translationResponse;
}

See also