Implementando Formatacao de Moeda com Claude Code
Aprenda sobre implementing currency formatting usando o Claude Code. Inclui exemplos praticos de codigo.
通貨フォーマットをClaude Codeで正しく実装する
ECサイト、会計アプリ、国際対応サービスでは、通貨の正確なフォーマットが不可欠です。小数点、桁区切り、通貨記号のルールは国・通貨ごとに異なるため、自前でフォーマットすると不具合の温床になります。Claude Codeを使えば、Intl.NumberFormatや専用ライブラリを活用した堅牢な実装を効率的に構築できます。
Intl.NumberFormatの活用
> 多通貨対応の通貨フォーマットユーティリティを作って。
> 日本円、USドル、ユーロに対応して。
> ロケール別表示、為替変換、金額のバリデーションも含めて。
// src/lib/currency.ts
export type CurrencyCode = 'JPY' | 'USD' | 'EUR' | 'GBP' | 'CNY' | 'KRW';
interface FormatOptions {
currency: CurrencyCode;
locale?: string;
compact?: boolean; // "1.2万" のような短縮表示
showSign?: boolean; // +/- 符号を表示
}
/**
* 通貨をフォーマットする
*/
export function formatCurrency(
amount: number,
options: FormatOptions
): string {
const { currency, locale = 'en-US', compact = false, showSign = false } = options;
const formatter = new Intl.NumberFormat(locale, {
style: 'currency',
currency,
notation: compact ? 'compact' : 'standard',
signDisplay: showSign ? 'exceptZero' : 'auto',
// 小数通貨は小数点2桁、JPY/KRWは0桁(自動判定)
});
return formatter.format(amount);
}
// Usage example
formatCurrency(1234567, { currency: 'JPY' }); // "¥1,234,567"
formatCurrency(1234.56, { currency: 'USD' }); // "$1,234.56"
formatCurrency(1234.56, { currency: 'EUR', locale: 'de-DE' }); // "1.234,56 €"
formatCurrency(1234567, { currency: 'JPY', compact: true }); // "¥123万"
通貨計算の注意点
// src/lib/currency-math.ts
/**
* 浮動小数点の誤差を避けるため、整数(最小通貨単位)で計算する
* JPY: 1円 = 1 | USD: 1セント = 1 | EUR: 1セント = 1
*/
export class Money {
constructor(
private readonly amountInMinorUnit: number,
private readonly currency: CurrencyCode
) {}
// セント/円の小数桁数
private static decimals(currency: CurrencyCode): number {
return ['JPY', 'KRW'].includes(currency) ? 0 : 2;
}
static fromMajorUnit(amount: number, currency: CurrencyCode): Money {
const decimals = Money.decimals(currency);
const minor = Math.round(amount * Math.pow(10, decimals));
return new Money(minor, currency);
}
toMajorUnit(): number {
const decimals = Money.decimals(this.currency);
return this.amountInMinorUnit / Math.pow(10, decimals);
}
add(other: Money): Money {
this.assertSameCurrency(other);
return new Money(this.amountInMinorUnit + other.amountInMinorUnit, this.currency);
}
subtract(other: Money): Money {
this.assertSameCurrency(other);
return new Money(this.amountInMinorUnit - other.amountInMinorUnit, this.currency);
}
multiply(factor: number): Money {
return new Money(Math.round(this.amountInMinorUnit * factor), this.currency);
}
format(locale?: string): string {
return formatCurrency(this.toMajorUnit(), { currency: this.currency, locale });
}
private assertSameCurrency(other: Money) {
if (this.currency !== other.currency) {
throw new Error(`通貨が異なります: ${this.currency} と ${other.currency}`);
}
}
}
// Usage example
const price = Money.fromMajorUnit(1980, 'JPY');
const tax = price.multiply(0.1);
const total = price.add(tax);
console.log(total.format()); // "¥2,178"
為替レート変換
// src/lib/exchange.ts
interface ExchangeRates {
base: CurrencyCode;
rates: Record<CurrencyCode, number>;
updatedAt: string;
}
let cachedRates: ExchangeRates | null = null;
export async function getExchangeRates(base: CurrencyCode = 'JPY'): Promise<ExchangeRates> {
// キャッシュが1時間以内なら再利用
if (cachedRates && Date.now() - new Date(cachedRates.updatedAt).getTime() < 3600000) {
return cachedRates;
}
const res = await fetch(`https://api.exchangerate-api.com/v4/latest/${base}`);
const data = await res.json();
cachedRates = {
base,
rates: data.rates,
updatedAt: new Date().toISOString(),
};
return cachedRates;
}
export async function convertCurrency(
amount: number,
from: CurrencyCode,
to: CurrencyCode
): Promise<{ converted: number; rate: number }> {
const rates = await getExchangeRates(from);
const rate = rates.rates[to];
return {
converted: Math.round(amount * rate * 100) / 100,
rate,
};
}
通貨入力コンポーネント
// src/components/CurrencyInput.tsx
'use client';
import { useState } from 'react';
interface Props {
value: number;
currency: CurrencyCode;
onChange: (value: number) => void;
}
export function CurrencyInput({ value, currency, onChange }: Props) {
const [displayValue, setDisplayValue] = useState(value.toLocaleString());
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const raw = e.target.value.replace(/[^0-9.-]/g, '');
setDisplayValue(e.target.value);
const num = parseFloat(raw);
if (!isNaN(num)) onChange(num);
};
const handleBlur = () => {
setDisplayValue(value.toLocaleString());
};
const symbol = new Intl.NumberFormat('en-US', {
style: 'currency',
currency,
currencyDisplay: 'narrowSymbol',
}).formatToParts(0).find((p) => p.type === 'currency')?.value || '';
return (
<div className="relative">
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">{symbol}</span>
<input
type="text"
value={displayValue}
onChange={handleChange}
onBlur={handleBlur}
className="w-full border rounded-lg pl-8 pr-4 py-2 text-right"
inputMode="decimal"
/>
</div>
);
}
浮動小数点の落とし穴
JavaScriptで0.1 + 0.2 === 0.30000000000000004となる問題は有名です。通貨計算では必ず最小単位(セント/円)の整数で扱い、表示時にのみ小数に変換するのが鉄則です。
関連記事
日付のフォーマットは日付・時間の扱い、国際化全般はi18n実装ガイドを参照してください。
Intl.NumberFormatの詳細仕様はMDN(developer.mozilla.org/Intl/NumberFormat)で確認できます。
Related Posts
Como Turbinar Seus Projetos Pessoais com o Claude Code [Com Exemplos]
Aprenda a acelerar drasticamente projetos de desenvolvimento pessoal usando o Claude Code. Inclui exemplos reais e um workflow prático da ideia ao deploy.
Como Automatizar Refatoração com o Claude Code
Aprenda a automatizar refatoração de código de forma eficiente usando o Claude Code. Inclui prompts práticos e padrões concretos de refatoração para projetos reais.
Guia Completo de Configuração CORS com Claude Code
Aprenda sobre o guia completo de configuração CORS usando o Claude Code. Dicas práticas e exemplos de código incluídos.