# [ 살펴보기 ] Next-Intl - Messages

Application에서 다국어 기능을 제공하기 위해 application에서 지원하는 언어별 json 파일을 구성하고 유저가 선택한 언어에 맞는 json 파일의 내용을 application에 적용하여 다국어 기능을 제공하게 된다.

Next-Intl에서 message란 각 언어별 json 파일에서 제공하는 content를 뜻한다. 예를 들어 다음 en.json 파일에서 userPage와 title은 모두 message가 된다.

en.json

```json
 {
  "userPage": {
    "title": "User page",
  }
}
```

## Static, Dynamic Messages

언어별 message는 next-intl이 제공하는 useTranslations hook을 통해 render할 수 있다. 또한 언어별 message를 구성할 때는 다음과 같이 `userPage`, `aboutPage`등 특정 category별로 namespace를 정해 message를 구성하는 것이 추천된다.

*en.json*

```json
{
  "userPage": {
    "title": "User page",
    "subTitle": "This is an user page"
  },
  "aboutPage":{
    "title": "About page",
    "subTitle": "This is an about page"
  }
}
```

*app/user/page.tsx*

```typescript
import { useTranslations } from "next-intl";
import React from "react";

const UserPage = () => {
  const t = useTranslations("userPage");
  return (
    <div>
      <h3>User</h3>
      <div>{t("title")}</div>
      <div>{t("subTitle")}</div>
    </div>
  );
};

export default UserPage;
```

만약 위의 예제와 같이 userPage namespace에 소속된 message가 아닌 현재 설정된 language의 모든 message를 load해서 사용하고 싶다면 다음과 같이 사용할 수도 있다.

```typescript
import { useTranslations } from "next-intl";
import React from "react";

const UserPage = () => {
  const t = useTranslations();
  return (
    <div>
      <h3>User</h3>
      <div>{t("userPage.title")}</div>
      <div>{t("aboutPage.title")}</div>
    </div>
  );
};

export default UserPage;
```

만약 위의 예제와 같이 messag를 이미 정해진 static value가 아닌 dynamic value 역시 포함해서 사용하고 싶다면 아래의 예제처럼 사용할 수 있다.

*en.json*

```json
{
  "userPage": {
    "title": "User page",
    "subTitle": "This is an user page and {info}"
  },
  "aboutPage":{
    "title": "About page",
    "subTitle": "This is an about page and {info}"
  }
}
```

*app/user/page.tsx*

```typescript
import { useTranslations } from "next-intl";
import React from "react";

const UserPage = () => {
  const t = useTranslations("userPage");
  return (
    <div>
      <h3>User</h3>
      <div>{t("subTitle", { info: "test 1" })}</div>
      <div>{t("subTitle", { info: "test 2" })}</div>
    </div>
  );
};

export default UserPage;
```

## Pluralization

각 언어마다 단수와 복수를 표현할 때 사용하는 다른 방법이 사용된다. 예를 들어 영어로 하나의 compute는 a computer로 표현해야 하지만 다수의 computer는 computers라고 표현해야 한다. 이런 상황에서 다음과 같이 plural keyword를 통해 pluralization 작업을 진행할 수 있다.

*en.json*

```json
{
  "userPage": {
     "computer": "{count, plural, =1 {a computer is} other {# computers are}} on the desk"
  }
}
```

*app/user/page.tsx*

```typescript
import { useTranslations } from "next-intl";
import React from "react";

const UserPage = () => {
  const t = useTranslations("userPage");
  return (
    <div>
      <h3>User</h3>
      <div>{t("computer", { count: 1 })}</div>
    </div>
  );
};

export default UserPage;
```

위의 예제에서 userPage.computer는 dynamic value count가 1일 때는 `a computer is on the desk count`가 2 이상일 때는 `2 computers are on the desk`가 된다.

## Enum

만약 dynamic value에 전달되는 값을 특정한 값에 matching 시키고 싶다면 다음과 같이select keyword를 통해 구현할 수 있다.

*en.json*

```json
{
  "userPage": {
     "user": "{gender, select, female {She} male {He} other {Person}} is online"
  }
}
```

*app/user/page.tsx*

```typescript
import { useTranslations } from "next-intl";
import React from "react";

const UserPage = () => {
  const t = useTranslations("userPage");
  return (
    <div>
      <h3>User</h3>
      <div>{t("user", { gender: "male" })}</div>
    </div>
  );
};

export default UserPage;
```

위의 예제에서 userPage.user는 dynamic value gender가 mail일 때는 `He is online`이 되고 dynamic value gender가 female일 때는 `She is online`이 된다.

## Rich text

rich method를 통해 특정 html tag를 translation message에 mapping 시켜 사용할 수 있다. 예를 들어 en.json에 다음과 같은 message가 있다고 가정해보자.

*en.json*

```json
{
  "userPage": {
     "userDetail": "Move to <userDetailPage>detail page</userDetailPage>"
  }
}
```

만약 위의 message를 render할 때 `<userDetailPage>` custom tag 부분을 html a tag로 변경해서 render하고 싶다면 다음과 같이 처리한다.

```typescript
import { useTranslations } from "next-intl";
import React from "react";

const UserPage = () => {
  const t = useTranslations("userPage");
  return (
    <div>
      <h3>User</h3>
      <div>
        {t.rich("userDetail", {
          userDetailPage: (chunk) => <a href="/user/detail">{chunk}</a>,
        })}
      </div>
    </div>
  );
};

export default UserPage;
```

위의 예제에서 `t.rich` 부분은 다음과 같은 html로 render된다.

```xml
<div>Move to <a href="/user/detail">detail page</a></div>
```

custom tag는 다음과 같이 계층적으로 정의하여 사용할 수도 있다.

en.json

```json
{
  "userPage": {
    "userDetail": "<highlight>Move to <userDetailPage>detail page</userDetailPage></highlight>"
  }
}
```

```typescript
import { useTranslations } from "next-intl";
import React from "react";

const UserPage = () => {
  const t = useTranslations("userPage");
  return (
    <div>
      <h3>User</h3>
      <div>
        {t.rich("userDetail", {
          userDetailPage: (chunk) => <a href="/user/detail">{chunk}</a>,
          highlight: (chunk) => <b>{chunk}</b>,
        })}
      </div>
    </div>
  );
};

export default UserPage;
```

위의 예제에서 `t.rich` 부분은 다음과 같은 html로 render된다.

```xml
<div><b>Move to <a href="/user/detail">detail page</a></b></div>
```

Rich text를 사용하기 위해 message에 custom tag를 추가할 때 주의할 점은 message file에선 self-closing tag를 custom tag로 사용할 수 없다는 것이다. 예를 들어 `<br />`와 같은 html tag를 message의 custom tag와 matching 시켜려고 할 때 message file에선 `<br></br>`와 같은 형식으로 opening, closing tag 모두 선언해주어야 한다.

en.json

```json
{
  "userPage": {
     "userDetail": "Move to <br></br><userDetailPage>detail page</userDetailPage>"
  }
}
```

```typescript
import { useTranslations } from "next-intl";
import React from "react";

const UserPage = () => {
  const t = useTranslations("userPage");
  return (
    <div>
      <h3>User</h3>
      <div>
        {t.rich("userDetail", {
          userDetailPage: (chunk) => <a href="/user/detail">{chunk}</a>,
          br: () => <br />,
        })}
      </div>
    </div>
  );
};

export default UserPage;
```

위의 예제에서 `t.rich` 부분은 다음과 같은 html로 render된다.

```xml
<div>Move to <br><a href="/user/detail">detail page</a></div>
```

## has

현재 선택한 language중에 특정 message가 존재하는지 확인하기 위해 has method를 사용할 수 있다. 아래는 userPage.memo message가 존재하는지 has method를 통해 확인하는 예제다.

en.json

```json
{
  "userPage": {
    "memo": "user memo"
  }
}
```

```typescript
import { useTranslations } from "next-intl";
import React from "react";

const UserPage = () => {
  const t = useTranslations("userPage");

  if (t.has("memo")) {
    console.log(" ::: memo found ::: ");
  } else {
    console.log(" ::: meme not found ::: ");
  }

  return (
    <div>
      <h3>User</h3>
    </div>
  );
};

export default UserPage;
```

## Number Format

next-intl에서 제공하는 `useFormatter` hook을 통해 number value를 format할 수 있다. 아래 예제는 전달 받은 value를 KRW curreny format으로 render하는 예제다.

```typescript
import { useFormatter } from "next-intl";
import React from "react";

const UserPage = () => {
  const format = useFormatter();

  return (
    <div>
      <div>My account : {format.number(300000000, { style: "currency", currency: "KRW" })}</div>
    </div>
  );
};

export default UserPage;
```

위의 예제에서 `format.number`에 전달한 값은 다음과 같이 render된다.

```xml
<div>My account : ₩300,000,000</div>
```

KRW뿐만 아니라 USD등 원하는 currenycy를 전달해주면 그에 맞게 currenycy 기호가 대체된다.

```typescript
...
  return (
    <div>My account : {format.number(300000000, { style: "currency", currency: "USD" })}</div>
  )
...
```

```xml
<div>My account : $300,000,000.00</div>
```

Number value를 message에 포함 시키고자 한다면 ICU syntax를 통해 포함 시킨다. 아래는 message에 price value를 포함하고 사용하는 예제다.

*en.json*

```json
{
  "productPage": {
    "price": "The prduct costs {price, number, currency}"
  }
}
```

```typescript
import { useFormatter, useTranslations } from "next-intl";
import React from "react";

const UserPage = () => {
  const t = useTranslations("productPage");
  const format = useFormatter();

  return (
    <div>
      <h3>User</h3>
      <div>
        {t(
          "price",
          { price: 300000000 },
          {
            number: {
              currency: {
                style: "currency",
                currency: "KRW",
              },
            },
          }
        )}
      </div>
    </div>
  );
};

export default UserPage;
```

## Dates and times

next-intl에서 제공하는 `useFormatter` hook을 통해 date와 time value를 format할 수 있다. 아래 예제는 전달 받은 date value를 korean time format으로 render하는 예제다.

```typescript
import { useFormatter } from "next-intl";
import React from "react";

const UserPage = () => {
  const format = useFormatter();
  const dateTime = new Date();
  console.log({ dateTime });
  return (
    <div>
      <h3>User</h3>
      <div>
        {format.dateTime(dateTime, {
          year: "numeric",
          month: "long",
          day: "numeric",
          hour: "numeric",
          minute: "numeric",
          second: "numeric",
        })}
      </div>
    </div>
  );
};

export default UserPage;
```

위의 예제에서 `format.dateTime`을 통해 render되는 결과는 다음과 같다.

```plaintext
December 17, 2024 at 8:13:39 PM
```

dateTime에 전달한 date value를 특정 timezone을 기준으로 render하고자 한다면 다음과 같이 특정 timezone을 설정할 수 있다.

```typescript
import { useFormatter } from "next-intl";
import React from "react";

const UserPage = () => {
  const format = useFormatter();
  const dateTime = new Date();
  return (
    <div>
      <h3>User</h3>
      <div>
        {format.dateTime(dateTime, {
          year: "numeric",
          month: "long",
          day: "numeric",
          hour: "numeric",
          minute: "numeric",
          second: "numeric",
          timeZone: "America/Los_Angeles",
        })}
      </div>
    </div>
  );
};

export default UserPage;
```

위의 예제와 같이 timeZone을 Los Angeles로 설정하고 값을 확인해보면 `December 17, 2024 at 3:13:39 AM`와 같이 현재 기준 Los Angeles 시간이 출력 되는 것을 확인할 수 있다.

이제 각 language json 파일의 message에 date를 포함하는 예제를 살펴보자.

*en.json*

```json
{
  "timePage": {
    "title": "Title date : {titleDate, date, long}"
  }
}
```

```typescript
import { useTranslations } from "next-intl";
import React from "react";

const UserPage = () => {
  const t = useTranslations("timePage");
  return (
    <div>
      <h3>User</h3>
      <div>
        {t(
          "title",
          { titleDate: new Date() }
        )}
      </div>
    </div>
  );
};

export default UserPage;
```
