Next.js(Pages Router)v16で通貨金額をフォーマットする方法

通貨記号と区切り記号を使用して価格を表示する

問題

金額を表示するには、通貨自体とユーザーの地域の数値フォーマット規則という2つの異なるローカライゼーション要素を調整する必要があります。1200.50という価格は、米国英語のユーザーには「$1,200.50」と表示される必要がありますが、ドイツのユーザーには「1 200,50 €」と表示される必要があります。通貨記号の位置、小数点区切り記号、桁区切り記号、スペーシングはすべてロケールによって異なります。これらの要素が不整合であったりハードコードされていたりすると、ユーザーは見慣れないフォーマットを目にし、表示された金額が正しいかどうか疑念を抱き、価格情報への信頼が損なわれます。

視覚的な一貫性を超えて、誤った通貨フォーマットは実際の混乱を引き起こす可能性があります。カンマを千の位の区切り記号として慣れているユーザーは、「1.200」を1200ではなく1.2と誤読する可能性があります。同様に、通貨記号の位置が間違っていると、価格がプロフェッショナルでないように見えたり、まったく異なる通貨を示唆したりする可能性があります。適切な通貨フォーマットは、通貨コードとユーザーのロケール固有の数値規則の両方を尊重し、価格が即座に明確で信頼できるものになることを保証します。

解決策

ISO 4217通貨コードとユーザーのアクティブなロケールを組み合わせて通貨値をフォーマットし、地域に適した数値フォーマットを生成します。このアプローチは、記号の配置、区切り記号の選択、スペーシングルールを国際化ライブラリに委任し、各ロケールと通貨のペアに対して正しい規則を適用します。その結果、アプリケーションコードでの手動の文字列操作やロケール固有のロジックなしに、ユーザーの期待に沿った価格表示が実現されます。

手順

1. 再利用可能な通貨フォーマットコンポーネントを作成する

数値と通貨コードを受け取り、react-intlのコンテキストから現在のロケールを使用して価格をフォーマットするコンポーネントを構築します。

import { FormattedNumber } from "react-intl";

interface PriceProps {
  value: number;
  currency: string;
}

export default function Price({ value, currency }: PriceProps) {
  return <FormattedNumber value={value} style="currency" currency={currency} />;
}

FormattedNumberコンポーネントは、最も近いIntlProviderからロケールを読み取り、通貨記号とロケール固有の数値フォーマットルールの両方を自動的に適用します。

2. 動的な価格データを持つページでコンポーネントを使用する

数値の金額と通貨コードをコンポーネントに渡して価格をレンダリングし、フォーマットがユーザーのロケールに適応できるようにします。

import { GetServerSideProps } from "next";
import Price from "../components/Price";

interface Product {
  id: string;
  name: string;
  price: number;
  currency: string;
}

interface ProductPageProps {
  product: Product;
}

export default function ProductPage({ product }: ProductPageProps) {
  return (
    <div>
      <h1>{product.name}</h1>
      <p>
        <Price value={product.price} currency={product.currency} />
      </p>
    </div>
  );
}

export const getServerSideProps: GetServerSideProps = async () => {
  const product = {
    id: "1",
    name: "Wireless Headphones",
    price: 1299.99,
    currency: "USD",
  };

  return {
    props: {
      product,
    },
  };
};

ページは通貨コードを含む製品データを取得し、それをPriceコンポーネントに渡します。コンポーネントはアクティブなロケールに応じて金額をフォーマットします。

3. 命令的な通貨フォーマット用のヘルパーを作成する

属性の設定や非JSXコンテキスト用のデータ準備など、Reactコンポーネントを使用できないシナリオの場合、useIntlフックを使用してフォーマット関数を公開します。

import { useIntl } from "react-intl";

export function useCurrencyFormatter() {
  const intl = useIntl();

  return (value: number, currency: string): string => {
    return intl.formatNumber(value, {
      style: "currency",
      currency,
    });
  };
}

このフックは、通貨値を文字列としてフォーマットする関数を返します。これは、ariaラベル、メタタグ、またはコンポーネントが適切でないその他のテキストのみのコンテキストに役立ちます。

4. 属性コンテキストでフォーマッターを適用する

命令的フォーマッターを使用して、React要素ではなくプレーンテキストを必要とするHTML属性を設定します。

import { useCurrencyFormatter } from "../hooks/useCurrencyFormatter";

interface ProductCardProps {
  name: string;
  price: number;
  currency: string;
  imageUrl: string;
}

export default function ProductCard({
  name,
  price,
  currency,
  imageUrl,
}: ProductCardProps) {
  const formatCurrency = useCurrencyFormatter();
  const priceLabel = formatCurrency(price, currency);

  return (
    <article aria-label={`${name}, ${priceLabel}`}>
      <img src={imageUrl} alt={name} />
      <h2>{name}</h2>
      <p>{priceLabel}</p>
    </article>
  );
}

フォーマッターは、aria-label属性に埋め込むことができるローカライズされた通貨文字列を生成し、支援技術がユーザーのロケールに適した形式で価格を読み上げることを保証します。