< BACK TO BLOG

λ‹€κ΅­μ–΄ 지원 next.js μ›Ή μ‘μš©ν”„λ‘œκ·Έλž¨

πŸ—“οΈ2021-11-24

next.js μ›Ή μ‘μš©ν”„λ‘œκ·Έλž¨μ΄ λ‹€κ΅­μ–΄λ₯Ό μ§€μ›ν•˜κΈ° μœ„ν•΄ next-i18next λ₯Ό μ‚¬μš©ν•˜λŠ” μ˜ˆμ œμ— λŒ€ν•œ μ„€λͺ…μž…λ‹ˆλ‹€.

baseURL ν•˜μœ„ 경둜λ₯Ό μ‚¬μš©ν•΄μ„œ 언어별 λΌμš°νŠΈκ°€ κ΅¬ν˜„λ©λ‹ˆλ‹€.

http://myapp.com     <- κΈ°λ³Έ μ–Έμ–΄
http://myapp.com/en  <- μ˜μ–΄
http://myapp.com/ru  <- λŸ¬μ‹œμ•„μ–΄

의쑴 νŒ¨ν‚€μ§€

κ΄€λ ¨ 의쑴 νŒ¨ν‚€μ§€λŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

  • next 12.0.4
  • next-i18next 9.1.0
  • react 17.0.2
  • react-dom 17.0.2
$ npm install --save react react-dom next next-i18next 
$ npm install --save-dev typescript @types/react @types/react-dom

ꡬ성 파일

ν”„λ‘œμ νŠΈ 루트 디렉터리에 next.config.js, next-i18next.config.js νŒŒμΌμ„ μΆ”κ°€ν•©λ‹ˆλ‹€.

next.config.js 파일과 next-i18next.config.js νŒŒμΌμ€ ν”„λ‘œμ νŠΈ λ£¨νŠΈμ—μ„œ μž‘μ„±λ˜μ–΄μ•Ό ν•˜κ³ , νŒŒμΌμ΄λ¦„μ€ μ§€μ •λœ 이름을 μ‚¬μš©ν•΄μ•Ό 진행에 λ¬Έμ œκ°€ λ°œμƒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

next-i18next.config.js 파일

μžμ„Έν•œ λ‚΄μš©μ€ GitHub: next-i18next μ €μž₯μ†Œμ—μ„œ μ°ΎμœΌμ‹€ 수 μžˆμŠ΅λ‹ˆλ‹€.

const config = {
    i18n: {
        defaultLocale: 'ko',
        locales: ['ko', 'en', 'ru'],
        defaultNS: 'common',
        localeDetection: false,
    },
};

module.exports = config;

defaultLocale 은 μ–Έμ–΄ 경둜λ₯Ό ν¬ν•¨ν•˜μ§€ μ•ŠλŠ” μ–Έμ–΄λ₯Ό μ§€μ •ν•©λ‹ˆλ‹€. locales λŠ” μ§€μ›ν•˜λŠ” μ–Έμ–΄λ₯Ό λͺ¨λ‘ μž‘μ„±ν•©λ‹ˆλ‹€. defaultNS λŠ” λ²ˆμ—­ 파일의 νŒŒμΌμ΄λ¦„μ„ μž‘μ„±ν•©λ‹ˆλ‹€. (기본값은 common μž…λ‹ˆλ‹€.) localeDetection 은 μ›Ήμ„œλ²„μ— μš”μ²­μ‹œ μš”μ²­ ν—€λ”μ˜ accept-language λ₯Ό μ‚¬μš©ν•΄μ„œ μ–Έμ–΄λ₯Ό 지정할지λ₯Ό κ²°μ •ν•©λ‹ˆλ‹€. (false 둜 μ§€μ •λ˜λŠ” 경우 accept-language 값을 λ¬΄μ‹œν•©λ‹ˆλ‹€.)

next.config.js 파일

μžμ„Έν•œ λ‚΄μš©μ€ next.js Internationalized routing νŽ˜μ΄μ§€μ—μ„œ μ°ΎμœΌμ‹€ 수 μžˆμŠ΅λ‹ˆλ‹€.

const { i18n } = require('./next-i18next.config');

/**
 * @type {import('next').NextConfig}
 */
const nextConfig = {
    /* config options here */
    i18n,
};

module.exports = nextConfig;

next-i18next.config.js νŒŒμΌμ—μ„œ μž‘μ„±ν•œ ꡬ성을 next.config.js νŒŒμΌμ—μ„œ next config의 i18n μ†μ„±μœΌλ‘œ μ‚¬μš©ν•©λ‹ˆλ‹€.

λ²ˆμ—­ 파일

ν”„λ‘œμ νŠΈ λ£¨νŠΈμ— public 디렉터리λ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€. public 디렉터리에 locales 디렉터리λ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€. locales 디렉터리에 언어별 디렉터리λ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€. locales λ””λ ‰ν„°λ¦¬μ˜ 언어별 디렉터리에 common.json νŒŒμΌμ„ μž‘μ„±ν•©λ‹ˆλ‹€.

locales λ””λ ‰ν„°λ¦¬μ˜ 언어별 λ””λ ‰ν„°λ¦¬μ˜ 이름은 next-i18next.config.js 파일의 locales 에 μž…λ ₯λ˜λŠ” λ¬Έμžμ—΄ λ°°μ—΄μ˜ ν•­λͺ©λ“€μ˜ μ΄λ¦„μœΌλ‘œ 디렉터리 이름을 κ²°μ •ν•©λ‹ˆλ‹€.

β”œβ”€β”€β”€public
β”‚   └───locales
β”‚       β”œβ”€β”€β”€en
β”‚       β”œβ”€β”€β”€ko
β”‚       └───ru

common.json 파일의 λ‚΄μš©μ€ μ•„λž˜μ™€ 같이 μœ νš¨ν•œ json ν˜•μ‹μœΌλ‘œ μž‘μ„±ν•©λ‹ˆλ‹€.

{
    "global": {
        "title": "next-i18next 예제"
    },
    "index": {
        "title": "μ•ˆλ…•ν•˜μ„Έμš”! πŸ‘‹"
    },
    "about": {
        "title": "μ†Œκ°œν•©λ‹ˆλ‹€! 😊 "
    },
    "debug": {
        "title": "디버그"
    },
    "navigator": {
        "link": "Link μ»΄ν¬λ„ŒνŠΈ μ‚¬μš©",
        "router": "Router μ‚¬μš©"
    }
}

λ²ˆμ—­νŒŒμΌμ˜ λ‚΄μš©μ„ μ‚¬μš©ν•  λ•ŒλŠ” μ•„λž˜μ™€ 같이 μž‘μ„±ν•©λ‹ˆλ‹€.

import { useTranslation } from 'next-i18next'

const Compo = () => {
    const { i18n, t } = useTranslation('common');

    return (
        <div>
            <p>{t('global.title')}</p> {/* ν•œκ΅­μ–΄μΈ 경우 "next-i18next 예제" 좜λ ₯ */}
        </div>
    );
};

확인

μ΄λ ‡κ²Œ κ΅¬μ„±ν•œ ν›„ μ—°κ²°λ˜λŠ” 경우 next.js 의 κΈ°λŠ₯이 i18n κ³Ό μ—°λ™λ˜μ–΄ λ™μž‘ν•©λ‹ˆλ‹€.

ν™•μΈλœ κΈ°λŠ₯은 μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

next.js의 μ•„λž˜ 두 κΈ°λŠ₯은 locale 을 μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄, ν˜„μž¬ i18n.language 값을 locale 둜 μ§€μ •λ˜μ–΄ λ™μž‘ν•©λ‹ˆλ‹€.

  • Link μ»΄ν¬λ„ŒνŠΈ next/link
  • Router next/router

next/link

Anchor λŠ” locale 을 μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄ ν˜„μž¬ i18n.language 의 값을 μ‚¬μš©ν•©λ‹ˆλ‹€.

// import Link from 'next/link';

<Link href={href}>
    <a>
        {label}
    </a>
</Link>

next/router

router λŠ” locale 을 μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄ i18n.language 값을 μ‚¬μš©ν•©λ‹ˆλ‹€.

import React from 'react'
import { useRouter } from 'next/router'

const Compo = () => {
    const router = useRouter()
    const handleClick = () => {
        router.push('/somepath');
    }
    return (
        <button onClick={handleClick}>{label}</button>
    );
};

Change language

μ‚¬μš©μžκ°€ μ–Έμ–΄λ₯Ό λ³€κ²½ν•˜λŠ” κΈ°λŠ₯을 μ§€μ›ν•˜λ €λ©΄ μ•„λž˜μ™€ μœ μ‚¬ν•˜κ²Œ i18n.changeLanguage() λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

i18n.changeLanguage(languageCode, () => {
    const { pathname, query, asPath } = router;
    // κ°’ ν™•μΈμš© μ½˜μ†” 좜λ ₯
    console.group('i18n.changeLanguage -> callback');
    console.info('language', languageCode);
    console.info(
        'pathname, query, asPath',
        pathname,
        query,
        asPath,
    );
    console.groupEnd();
    router.push({ pathname, query }, asPath, {
        locale: languageCode,
    });
});

i18n.changeLanguage() μ‹€ν–‰ μœ„μΉ˜λ₯Ό μ£Όμ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ _app.tsx 에 μœ„ μ½”λ“œλ₯Ό μ‚¬μš©ν•  λ•Œ, i18n.changeLanguage() μ‹€ν–‰ ν›„ i18n.language 의 변경이 μžμ‹ μ»΄ν¬λ„ŒνŠΈμ—μ„œ μ •ν™•ν•˜κ²Œ μ²˜λ¦¬λ˜λŠ”μ§€ 확인해야 ν•©λ‹ˆλ‹€. _app.tsx μ—μ„œ μ‚¬μš©ν•˜λŠ” 것보닀 μžμ‹ μ»΄ν¬λ„ŒνŠΈμ—μ„œ μ‚¬μš©ν•˜λŠ” 것이 더 μ’‹λ‹€κ³  μƒκ°λ©λ‹ˆλ‹€. 이 경우 λ¬Έμ œμ—†μ΄ λ™μž‘ν•˜λŠ” κ²ƒμœΌλ‘œ ν™•μΈλ˜μ—ˆμŠ΅λ‹ˆλ‹€.

μ‹œμ—°

sample-next-i18next.vercel.app νŽ˜μ΄μ§€μ—μ„œ λ™μž‘μ„ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

μ°Έμ‘°

GitHub: sample-next-i18next μ €μž₯μ†Œμ—μ„œ μ½”λ“œλ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

GitHub Repository


Profile picture

Pon Cheol Ku (ꡬ본철)

Software developer

Other sites

If does not find interesting topic, you might visit other site on below link.

Β© 2024, Built with Gatsby