최근에 다국어로 전환할 수 있는 웹사이트를 만들어 광범위한 관객을 대상으로 하는 흥미로운 프로젝트를 수행했습니다. 이를 통해 전통적으로 다른 언어 및 지역의 사용자들을 위해 콘텐츠를 적합하고 접근 가능하며 관련성 있게 적응하는 “로컬라이제이션” 개념을 더 잘 이해하게 되었습니다.

로컬라이제이션은 단순히 단어를 번역하는 것이 아니라 사용자들이 어떤 언어를 사용하더라도 편안함을 느끼게 하는 경험을 만드는 것입니다. 예를 들어, 아마존과 같은 글로벌 플랫폼은 언어 전환을 매우 원활하게 만들어 마법 같은 느낌을 줍니다. 사용자 경험을 향상시키는 것을 넘어, 이 기능은 더 넓은 관객에 도달하고 전 세계 고객들과 강한 연결을 유도하여 비즈니스를 촉진하는 데 중요한 역할을 합니다.

목차

i18n은 무엇이며 왜 사용하는가?

i18n은 국제화(internationalization)의 약자로, 애플리케이션이 여러 언어를 지원함을 의미합니다. “i18n”은 “internationalization”에서 첫 번째 “i”와 마지막 “n” 사이에 18개의 글자가 있다는 사실에서 유래되었습니다. 이는 텍스트 번역, 날짜 및 숫자 형식화, 통화 관리, 지역 관습 수용 등을 처리하여 귀하의 앱을 글로벌 사용자에게 적합하도록 만드는 것과 관련이 있습니다.

국제화를 활성화함으로써 귀하의 앱은 단순한 도구가 아니라 사용자의 선호도와 문화에 직접적으로 대응하는 포괄적인 플랫폼이 됩니다.

바로 시작해 봅시다

우리는 이 개념을 구현하는 방법을 보여주기 위해 다크 모드 전환 기능이 있는 매우 간단한 다국어 웹 애플리케이션 데모를 만들 것입니다.

전제 조건

  1. React에 대한 기본 지식 – 컴포넌트를 생성하고, 상태를 관리하며, useStateuseEffect와 같은 Hooks를 사용하는 방법을 이해해야 합니다. React가 처음이라면 공식 React 문서 부터 시작하여 탄탄한 기초를 쌓기를 추천합니다.

  2. 국제화 개념에 대한 이해 – 국제화(i18n)의 기본을 알고 그 중요성을 이해하는 것은 프로젝트에 대한 맥락을 제공해줍니다. 이 기사의 이전 섹션에서 기본 사항을 다룹니다.

  3. 테일윈드 CSS – 스타일링에는 테일윈드 CSS를 사용할 것입니다. 이는 HTML을 떠나지 않고도 현대적이고 반응형 디자인을 구축하는 데 도움이 되는 유틸리티 중심의 CSS 프레임워크입니다. 익숙하지 않다면 테일윈드의 문서를 확인하세요.

  4. Node.js – 시스템에 Node.js가 설치되어 있는지 확인하여 종속성을 처리합니다. 최신 버전은 Node.js에서 다운로드할 수 있습니다.

  5. 패키지 관리자 – 프로젝트 종속성을 관리하기 위해 npm( Node.js에 포함됨) 또는 yarn이 필요합니다

사용할 도구

  1. 코드 편집기

  2. 로컬라이제이션 라이브러리: react-i18next

  3. 아이콘 라이브러리: hero-icons

단계 1: 프로젝트 설정 방법

프로젝트 초기화

Vite를 사용하여 빠르게 설정하십시오:

npm create vite@latest multilingual-demo

터미널에서 표시된 지침을 따르세요. 아래 이미지에서 개발을 위해 React 및 TypeScript를 선택하세요:

의존성 설치

이 프로젝트에 필요한 종속성을 설치하려면 터미널에서 다음 명령을 실행하세요:

npm install i18next react-i18next i18next-browser-languagedetector i18next-http-backend heroicons 
npm install tailwindcss postcss autoprefixer  
npx tailwindcss init

TailwindCSS 구성

tailwind.config.ts 파일을 업데이트하십시오:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  darkMode: "class", // 다크 모드 기능을 위해
  theme: {
    container: {
      center: true,
      padding: "1.25rem",
      screens: {
        sm: "1200px",
      },
    },
    extend: {},
  },
  plugins: [],
};

src/index.css에 TailwindCSS 추가:

@tailwind base;  
@tailwind components;  
@tailwind utilities;

단계 2: i18next를 사용하여 국제화 설정하는 방법

i18next를 초기화

src 폴더에 i18n.tsx 파일을 생성하고 i18next를 구성하십시오:

import i18next from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import { initReactI18next } from "react-i18next";
import Backend from "i18next-http-backend";

i18next.use(LanguageDetector).use(initReactI18next).use(Backend).init({
  returnObjects: true,
  fallbackLng: "en", // 선택한 언어가 구성되지 않은 경우로 폴백할 언어
  debug: true, // 오류를 확인할 수 있도록 설정
  //   lng: "en", // 영어로 설정된 기본 언어
});

번역 기능을 활성화하는 데 중요한 역할을 하는 이 파일의 내용을 간단히 살펴보겠습니다. 이 파일은 번역 프로세스의 핵심을 설정하고 언어 전환 기능이 앱 전체에서 원활하게 작동하도록합니다.

  • i18next: 번역에 사용되는 핵심 내부화 라이브러리입니다.

  • LanguageDetector: 브라우저 설정에 기반하여 사용자의 기본 언어를 자동으로 감지하는데 도움이 됩니다.

  • initReactI18next: i18next 플러그인을 React와 통합하며 useTranslation 훅과 다른 유틸리티를 제공합니다.

  • Backend: 외부 소스에서 번역 데이터를 동적으로 가져옵니다. 이 경우, JSON 파일을 사용할 것입니다.

이 파일을 main.tsx 파일에 가져옵니다:

//main.tsx

import React, { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
import "./i18n.tsx";  //여기에 가져오기

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <React.Suspense fallback="loading">
      <App />
    </React.Suspense>
  </StrictMode>
);

번역 파일 생성

public/locales 디렉토리에서 각 언어에 대한 하위 폴더를 생성합니다 (예: en, fr) 그리고 translation.json 파일을 포함합니다:

en/translation.json

{
    "greeting": "Welcome to the Language Playground",
    "detail": {
        "line1": "Did you know that over 7,000 languages are spoken worldwide?",
        "line2": "This Playground demonstrates how web applications can support users in multiple languages, making them accessible and inclusive to people from different backgrounds."
    }
}

fr/translation.json

{
    "greeting": "Bienvenue sur le terrain de jeu linguistique",
    "detail": {
        "line1": "Saviez-vous que plus de 7 000 langues sont parlées dans le monde ?",
        "line2": "Ce terrain de jeu démontre comment les applications web peuvent prendre en charge les utilisateurs dans plusieurs langues, les rendant accessibles et inclusives aux personnes de différents horizons."
    }
}

여기서 i18next에 제공될 번역 파일과 함께 여러 언어를 추가할 수 있습니다. JSON 파일의 키는 웹사이트에 표시할 때 참조로 사용될 것이므로 동일해야 합니다.

3단계: 구성 요소 만들기

src 디렉토리에 components 폴더를 생성하고 다음 구성 요소를 추가합니다:

언어 선택기

LanguageSelector 구성 요소를 만듭니다. – 사용자가 언어를 동적으로 전환할 수 있도록 select 요소를 포함합니다.

import { useEffect, useState } from "react";
import i18next from "i18next";
import { useTranslation } from "react-i18next";

type languageOption = { language: string; code: string };

const languageOptions: languageOption[] = [
  {
    language: "English",
    code: "en",
  },
  { language: "French", code: "fr" },
  { language: "German", code: "de" },
  { language: "Spanish", code: "es" },
  { language: "Arabic", code: "ar" },
  { language: "Yoruba", code: "yo" },
];

const LanguageSelector = () => {
  // i18next의 감지된 또는 기본 언어로 초기 언어 설정
  const [language, setLanguage] = useState(i18next.language);

  const { i18n } = useTranslation();

  const handleLanguageChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    const selectedLanguage = e.target.value;
    setLanguage(selectedLanguage);
    i18next.changeLanguage(selectedLanguage); // i18next에서 언어 업데이트
  };

  useEffect(() => {
    document.body.dir = i18n.dir(); // 왼쪽에서 오른쪽 또는 오른쪽에서 왼쪽으로 body 설정
  }, [i18n, i18n.language]);

  return (
    <select
      id="language"
      value={language}
      onChange={handleLanguageChange}
      className="p-2 border border-gray-300 rounded-md shadow-sm focus:border-indigo-500 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
        dark:bg-gray-800 dark:border-gray-600 dark:text-gray-200 dark:focus:border-indigo-400 dark:focus:ring-indigo-700 dark:focus:ring-opacity-50"
    >
      {languageOptions.map(({ language, code }, key) => (
        <option value={code} key={key}>
          {language}
        </option>
      ))}
    </select>
  );
};

export default LanguageSelector;
  • i18next에 의해 감지된 언어 또는 설정된 기본 언어로 언어를 초기화합니다.

  • useTranslation 훅은 국제화 설정과 상호 작용하기 위해 i18next에서 i18n 인스턴스를 노출합니다.

  • handleLanguageChange 함수는 사용자가 선택한 언어를 업데이트하는 데 사용됩니다. 사용자가 드롭다운 메뉴에서 새 언어를 선택하면 트리거됩니다.

텍스트 방향 구현

HTML의 dir 속성은 웹 애플리케이션에서 접근성과 포용성을 보장하는 데 중요한 기능이며, 텍스트 방향이 다른 언어를 다룰 때 특히 중요합니다. 예를 들어:

  • 왼쪽에서 오른쪽 (LTR): 영어, 프랑스어, 스페인어를 포함한 대부분의 언어가 이 방향을 따릅니다.

    오른쪽에서 왼쪽 (RTL): 아랍어, 히브리어와 같은 언어는 가독성과 문화적 맥락을 유지하기 위해 텍스트 정렬과 레이아웃을 뒤집어야 합니다.

우리 앱에서 이를 달성하기 위해 i18n에서 dirdocument.body.dir를 설정하고, useEffect 훅을 사용하여 언어 선택의 변경 사항을 듣습니다.

다크 모드 전환

DarkModeToggle 컴포넌트를 만들어 사용자가 원하는 대로 밝은 모드와 어두운 모드를 전환할 수 있게 합니다.

import { useEffect, useState } from "react";
import { SunIcon, MoonIcon } from "@heroicons/react/solid";

const DarkModeToggle = () => {
  const [darkMode, setDarkMode] = useState(false);

  useEffect(() => {
    // 첫 로드 때 로컬 저장소 또는 시스템 기본 설정 확인
    const isDark =
      localStorage.getItem("theme") === "dark" ||
      (!localStorage.getItem("theme") &&
        window.matchMedia("(prefers-color-scheme: dark)").matches);
    setDarkMode(isDark);
    document.documentElement.classList.toggle("dark", isDark);
  }, []);

  const toggleDarkMode = () => {
    setDarkMode(!darkMode);
    document.documentElement.classList.toggle("dark", !darkMode);
    localStorage.setItem("theme", !darkMode ? "dark" : "light");
  };

  return (
    <button
      aria-label="Toggle dark mode"
      onClick={toggleDarkMode}
      className="p-1 rounded"
    >
      {darkMode ? (
        <SunIcon
          className="w-6 h-6 text-yellow-500 "
          onClick={toggleDarkMode}
        />
      ) : (
        <MoonIcon className="w-6 h-6 text-gray-900 " onClick={toggleDarkMode} />
      )}
    </button>
  );
};

export default DarkModeToggle;

헤더 컴포넌트

Header 컴포넌트는 DarkModeTogglelanguageSelector 컴포넌트의 상위 컴포넌트로 작동합니다.

import DarkModeToggle from "./DarkModeToggle";
import LanguageSelector from "./LanguageSelector";

const Header = () => {
  return (
    <header className="container flex justify-between">
      <DarkModeToggle />
      <LanguageSelector />
    </header>
  );
};

export default Header;

단계 4: 메인 앱 컴포넌트

src/app 파일에 다음을 포함하세요.

import { useTranslation } from "react-i18next";
import Header from "./components/Header";

const App = () => {
  const { t } = useTranslation();

  const line1 = t("detail.line1");
  const line2 = t("detail.line2");

  return (
    <div className="h-[100vh] bg-white text-black dark:bg-gray-900 dark:text-white py-8">
      <Header />
      <div className="container text-center max-w-2xl mt-28">
        <h1 className="text-4xl font-bold">{t("greeting")}</h1>
        <p className="mt-8">{line1}</p>
        <p className="mt-2">{line2}</p>
      </div>
    </div>
  );
};

export default App;
  • react-i18next

    useTranslation 훅은 번역된 텍스트를 가져오는 데 사용되는 t 함수를 노출합니다.

  • 이 함수는 번역 파일(예: en.json, fr.json)에서 키를 기반으로 번역된 문자열을 가져옵니다.

이러한 단계를 따르면 이제 번역이 완벽하게 통합된 앱이 완전히 작동해야 합니다. 우리 앱의 최종 결과물은 다음과 같습니다:

실시간 데모를 확인하고 GitHub에서 소스 코드를 확인하십시오.

결론

사용자가 선호하는 언어를 선택할 수 있는 웹사이트를 만드는 것은 기술적 성취뿐만 아니라 웹을 더 포용적이고 환영받는 곳으로 만드는 한 걸음입니다.

React-i18next와 Tailwind CSS와 같은 도구를 사용하여 국제화(i18n)를 결합하고 스타일링하면 전 세계 사용자에게 유연하고 사용하기 쉬우며 접근성이 뛰어난 애플리케이션을 구축할 수 있습니다.

이 프로젝트에서는 i18n 설정, 언어 전환기 추가 및 더 나은 사용성을 위한 “다크 모드” 추가를 살펴보았습니다.

참고문헌

https://react.i18next.com/

https://www.youtube.com/watch?v=dltHi9GWMIo