RORK LABEN
ENGINE — Rork MaxはClaude CodeとClaude Opus 4.6を基盤に、ネイティブSwiftアプリを直接生成しますCORE ML — Rork MaxからCore MLの端末内推論・HealthKit・HomeKit・NFC・App Clipsといった機能に手が届きますSEED — Rorkは2026年4月にLeft Lane Capital主導で$15Mのシードを調達し、Peak XVやa16z Speedrunが参加しましたM&A — Rorkはアプリビルダー Paperline を買収し、エンジニアリング人材の獲得を目的に買収を継続する方針ですMARKET — Gartnerは2026年末までに新規アプリの75%がローコードまたはノーコードで作られると予測していますGROWTH — ノーコードAI市場は2024年の$4.9Bから2029年に$24.8Bへ、年38.2%の成長が見込まれていますENGINE — Rork MaxはClaude CodeとClaude Opus 4.6を基盤に、ネイティブSwiftアプリを直接生成しますCORE ML — Rork MaxからCore MLの端末内推論・HealthKit・HomeKit・NFC・App Clipsといった機能に手が届きますSEED — Rorkは2026年4月にLeft Lane Capital主導で$15Mのシードを調達し、Peak XVやa16z Speedrunが参加しましたM&A — Rorkはアプリビルダー Paperline を買収し、エンジニアリング人材の獲得を目的に買収を継続する方針ですMARKET — Gartnerは2026年末までに新規アプリの75%がローコードまたはノーコードで作られると予測していますGROWTH — ノーコードAI市場は2024年の$4.9Bから2029年に$24.8Bへ、年38.2%の成長が見込まれています
記事一覧/開発ツール
開発ツール/2026-06-23中級

価格が一部の端末だけ「¥1234」になる——Expo の Intl を信用しきらないフォーマット層の作り方

同じコードなのに、ある端末では「¥1,234」、別の端末では「¥1234」。Expo / Hermes の Intl は端末とOSのロケールデータに依存して出力が変わります。通貨・数値・日付・タイムゾーンを1ファイルに集約し、Intl が欠ける端末へ安全に後退するフォーマット層の設計を、動くコードで整理しました。

Expo139React Native201Intlローカライズ7Hermes6

プレミアム記事

ある朝、自分の運営アプリのレビューに「価格表示が崩れている」という一文が届きました。スクリーンショットを見ると、本来「¥1,480」と出るはずの値段が「¥1480」と、桁区切りのカンマだけが抜け落ちています。手元の検証端末では再現しません。コードは一行も変えていないのに、です。

原因はコードではなく、端末が持っているロケールデータの差でした。React Native(Expo) のアプリは JavaScript エンジンに Hermes を使うことが多く、その Intl 実装は端末側の国際化データ(ICU)に寄りかかっています。つまり同じ Intl.NumberFormat を呼んでも、OS のバージョンや言語設定によって出力文字列が微妙に変わり得ます。

個人開発で複数のアプリを各国向けに出していると、この「自分の端末では起きない不具合」が一番やっかいです。今日は、価格・数値・日付の表示を Intl 任せにせず、崩れを出荷前に捕まえるためのフォーマット層を、実際に私が使っている形で整理します。

なぜ「同じコード」で表示が変わるのか

Intl は ECMAScript の国際化 API ですが、フォーマットに必要なロケールデータ(通貨記号の位置、桁区切り、月名、タイムゾーン規則)はランタイムが用意します。Hermes はこの部分を端末の ICU に橋渡しする設計のため、次の3つが変数になります。

ひとつ目は OS バージョンです。Android は OS に同梱された ICU を使うため、古い端末ほどロケールデータが古く、新しい通貨や地域の表記が欠けることがあります。ふたつ目はユーザーの言語・地域設定で、navigator 由来のデフォルトロケールはユーザーが自由に変えられます。みっつ目はエンジンのビルド構成で、Intl を含むかどうか、どこまで含むかがプロジェクトの設定に依存します。

ここで重要なのは、Intl が「ある/ない」の二択ではなく、「あるが、機能ごとに届く範囲が違う」状態だという点です。NumberFormat は広く動くのに、DateTimeFormattimeZone に任意の IANA 名(Asia/Tokyo など)を渡すと一部端末で無視される、といった部分的な欠落が現実に起きます。

フォーマットの呼び出しを一点に集める

最初の打ち手は、アプリ中に散らばった toLocaleString() や手書きの桁区切りを禁止し、フォーマットの入口を1ファイルに集約することです。表示の崩れが起きても、直す場所が1か所で済みます。

// lib/format.ts — アプリ内のフォーマットはすべてここを通す
import { getLocales } from "expo-localization";
 
// 端末の第一言語。取れなければ日本語にフォールバック
export const APP_LOCALE = getLocales()[0]?.languageTag ?? "ja-JP";
 
// Intl が実際に使えるかを起動時に一度だけ判定する
const hasIntl =
  typeof Intl !== "undefined" &&
  typeof Intl.NumberFormat === "function" &&
  // 通貨スタイルが例外を投げないかまで確かめる
  (() => {
    try {
      new Intl.NumberFormat("ja-JP", { style: "currency", currency: "JPY" }).format(1);
      return true;
    } catch {
      return false;
    }
  })();
 
export function formatNumber(value: number, locale: string = APP_LOCALE): string {
  if (hasIntl) {
    return new Intl.NumberFormat(locale).format(value);
  }
  // 後退: 3桁ごとにカンマを入れる素朴な実装
  return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

hasIntltry/catch 付きで一度だけ評価しているのが要点です。Intl オブジェクトが存在しても、style: "currency" のような特定オプションで例外を投げる端末が稀にあるため、「呼べること」まで確認してから採用します。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
Intl.NumberFormat / DateTimeFormat が端末・OS・エンジンで出力を変える理由と、出荷前に崩れを検知する方法
通貨・数値・日付・相対時間を1ファイルに集約する format 層の実装(フォールバック付きの動くコード)
任意の IANA タイムゾーンが効かない端末への後退と、UTC保存・ロケール表示を分ける設計
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

開発ツール2026-03-15
Expo SDK 55 で始める最新パフォーマンス最適化テクニック
Expo SDK 55がもたらすHermes V1、EASビルドキャッシング、モーダル改善など最新のパフォーマンス最適化手法を完全解説。実装例とベストプラクティス付き。
開発ツール2026-07-10
React Compiler を Expo に入れて、手書き memo を 41 箇所消しました
Rork が生成した React Native 画面に React Compiler を導入し、再レンダリング回数を Profiler で実測しました。手書き memo/useCallback の削除判断、効かない箇所の見分け方、CI での回帰検知までをまとめています。
開発ツール2026-07-09
Amplitude のファネルが一晩で改善したように見えたとき — イベントのスキーマ崩れを自己監査で炙り出す運用メモ
Rork で作ったアプリの Amplitude ファネルで、課金転換率が8%から19%へ跳ね上がったのに売上が動かない。原因はプロパティ名の崩れによる二重計上でした。イベント契約と実行時バリデータで崩れを計測し、段階的に締め直した運用メモです。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →