ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Next.js 14] 다국어 지원 방법 / Localization / Internationalization 응용(데이터 및 태그를 포함한 변환) [i18next]
    웹 개발/Nextjs 14 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 안의 인덱스와 매칭하여 로드한다.

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

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

     

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

     


     

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

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

     

    이상 끗~