웹 개발/Nextjs 14

[Next.js 14] 다국어 지원 방법 / Localization / Internationalization 응용(데이터 및 태그를 포함한 변환) [i18next]

마라턍 2024. 12. 13. 21:58

아래의 예시처럼 특정 위치에 css를 다르게 적용하고 싶거나

텍스트 중간에 사용자 닉네임이나 결제 예정일 등의 데이터를 포함하여 함께 Localization, 즉 다국어 지원을 해야한다면

잘 찾아오셨습니다!

 

 

들어가기 앞서, 해당 글은 이전에 작성했던 i18next를 이용한 App router 기반 Localization 방법의 응용편입니다.

 

[Next.js 14] App router 기반 Localization / Internationalization [i18next]

다국어를 지원하는 웹에 들어가보면 드롭다운으로 언어를 바꾸고, 그에 맞게 텍스트가 휙휙 바뀌는 것을 본 적이 있을 것이다.이번 포스팅에서는 i18next 모듈을 이용해서 앱 라우터 기반의 Next.js

hotsunchip.tistory.com

 따라서 처음 적용하려는 사람은 해당 게시물을 참고하시면 좋을 것 같아요!


 

이제 반말로...

앞의 포스팅을 참고하여 기본적인 변환에 대해 익숙해졌다면, 이제 앞으로 한 단계 더 나아가보자.

 

Settings.ts 변경

응용편에서는 이전에 사용했던 settings.ts를 아래와 같이 변경해주어야 한다.

// settings.ts

import {InitOptions, Namespace} from 'i18next';
import {defaultLanguage} from '@/utils/userLocaleUtils';

export const isTestEnv = false;

export const fallbackLng = defaultLanguage;
export const locales = [fallbackLng, 'en', 'ja'] as const;
export type LocaleTypes = (typeof locales)[number];
export const defaultNS = 'common';

export function getOptions(lang = fallbackLng, ns:Namespace = defaultNS): InitOptions {
    return {
        // debug: true, // Set to true to see console logs
        supportedLngs: locales,
        fallbackLng,
        lng: lang,
        fallbackNS: defaultNS,
        defaultNS,
        ns: ns,
        nsSeparator: '::',
        react: {
            defaultTransParent: '',
            transEmptyNodeValue: '',
            transSupportBasicHtmlNodes: true,
            transKeepBasicHtmlNodesFor: ['br', 'strong', 'i', 'button', 'a', 'span', 'div', 'input', 'ul', 'li'],
            transWrapTextNodes: '',
        },
    };
}

 

비교해보면 nsSeparator이라는 옵션이랑 react라는 옵션이 추가된 것을 확인할 수 있다.

쉽게 말하자면 nsSeparator은 많은 json 파일 중에서 어떤 파일에서 localization key를 가져올 것인지,

react 속성 중 transKeepBasicHtmlNodesFor은 localizationString 내에 어떤 태그를 허용할 것인지

를 정해주는 속성이라고 생각하면 될 것 같다.

 

사용 방법

이전에는 간단하게

const { t } = await createTranslation(locale, 'customns'); // 서버 컴포넌트일 때
const { t } = useTranslation(locale, 'customns'); // 클라이언트 컴포넌트일 때

 

를 썼다면, 이번에는 react-i18next에서 제공하는 Trans라는 컴포넌트를 사용할 것이다.

import {Trans} from 'react-i18next/TransWithoutContext';

 

 

더 자세한 사용 방법은...

이제 열심히 풀어 설명하는 것보다, 코드를 한번 보는 게 이해하기 쉬울 것 같아 예시를 가져왔다.

더보기
'use client';

import "@/styles/pages/main.css";

import React from 'react';
import {useParams} from 'next/navigation';
import {useTranslation} from '@/utils/localization/client';
import type {LocaleTypes} from '@/utils/localization/settings';
import {Trans} from 'react-i18next/TransWithoutContext';
import Link from 'next/link';
import siteLinks from '@/data/links/siteLinks';

const Hero = () => {
    const locale = useParams()?.locale as LocaleTypes;
    const {t} = useTranslation(locale, ['public', 'subscribe']);

    return (
      <div className="flex flex-col gap-4">
        <div className="example first">
          <p>첫 번째 예시</p>
          <div className="main-hero--text-area">
            <h1><Trans t={t} i18nKey="main-hero-title" /></h1>
          </div>
        </div>
        <div className="example second">
          <p>두 번째 예시</p>
          <Trans t={t} i18nKey="subscribe::withdrawal-complete-description"
                 values={{expirationDate: new Date().toLocaleString()}} />
        </div>
        <div className="example third">
          <p>세 번째 예시</p>
          <Trans t={t} i18nKey={`main-faq-answer-6`}
                 components={[
                   <Link href={siteLinks.discord}
                         className="text-link"
                         rel="noopener noreferrer"
                         target="_blank"
                         key="0"
                   />,
                   <Link href={siteLinks.twitter[locale]}
                         className="text-link"
                         rel="noopener noreferrer"
                         target="_blank"
                         key="0"
                   />,
                   <Link href={siteLinks.blog}
                         className="text-link"
                         rel="noopener noreferrer"
                         target="_blank"
                         key="0"
                   />,
                 ]}
          />
        </div>
      </div>
    );
};

export default Hero;

 

 

첫 번째 예시

제일 간단한 응용 방법이다. 앞서 setting.ts를 바꿨기 때문에, localizationString 안에 기본 html tag인 li, ul, span 등의 태그를 이용할 수 있게 된다.

<div className="main-hero--text-area">
	<h1><Trans t={t} i18nKey="main-hero-title" /></h1>
</div>

 

이렇게 Trans 컴포넌트에 t랑 key를 세팅해주면 끝난다.

이제 스타일을 넣는 건 css 파일에서 해주면 된다는 말씀.

위 예시의 경우, main-hero--text-area의 span에 text-gradient를 적용시킨 것이다.


키는 아래와 같이 작성하면 된다.

 

두 번째 예시

그런데 이제 중간에 유저의 닉네임이라던가, 현재 시간이라던가 등을 포함하여 정보를 제공해야 하는 경우가 있을 것이다.

이 경우에는 {{}}를 이용하여 문자열 보간(String Interpolation)하는 것처럼 사용할 수 있다.

<Trans t={t} i18nKey="subscribe::withdrawal-complete-description"
                 values={{expirationDate: new Date().toLocaleString()}} />

 

지금 예시랑 아래의 키 예시랑 살짝 다른데, 뭐 감 잡으신 것처럼

키에서는 {{ }}안에 변수명을 적어주고, 스크립트에서는 values라는 속성에 키랑 값을 매칭해주면 된다.

 

키는 아래와 같이 작성하면 된다.

 

 

세 번째 예시

화룡점정으로, 변수뿐만 아니라 컴포넌트도 넣을 수 있다.

 

<Trans t={t} i18nKey={`main-faq-answer-6`}
                 components={[
                   <Link href={siteLinks.discord}
                         className="text-link"
                         rel="noopener noreferrer"
                         target="_blank"
                         key="0"
                   />,
                   <Link href={siteLinks.twitter[locale]}
                         className="text-link"
                         rel="noopener noreferrer"
                         target="_blank"
                         key="0"
                   />,
                   <Link href={siteLinks.blog}
                         className="text-link"
                         rel="noopener noreferrer"
                         target="_blank"
                         key="0"
                   />,
                 ]}
          />

 

이건 이제 첫 번째 예시랑 비슷한데, 인덱스를 태그로 사용해서 렌더링 시 components 안의 인덱스와 매칭하여 로드한다.

그래서 아래 키 예시 오른쪽에 보이는 것처럼 각 국가별로 컴포넌트 순서를 다르게 보여주거나,

아예 다른 컴포넌트를 보여주는 것도 가능하게 된다.

 

키는 아래와 같이 작성하면 된다.

 


 

이렇게 보면 복잡해보이지만 전혀 어렵지 않다!

예시도 많이 가져왔으니까, 한번 해보면 껌이네~ 할듯

 

이상 끗~