{"version":3,"file":"useIntl.cjs","sources":["../../src/intl/useIntl.ts"],"sourcesContent":["'use client'\n\nimport { useCallback } from 'react'\nimport {\n  type PrimitiveType,\n  type MessageDescriptor as ReactIntlMessageDescriptor,\n  useIntl as useReactIntl,\n} from 'react-intl'\n\nimport { useAvailableLocales } from './IntlProvider'\nimport { locales } from './locales'\n\nimport type { FormatXMLElementFn, Options as IntlMessageFormatOptions } from 'intl-messageformat'\n\ntype Messages = Record<keyof typeof locales.ja, string>\n\ntype MessageDescriptor<T extends keyof Messages> = Omit<ReactIntlMessageDescriptor, 'id'> & {\n  id: T\n  defaultText: (typeof locales.ja)[T]\n}\n\ntype DatePart = 'year' | 'month' | 'day' | 'weekday'\n\nexport type FormatDateProps = {\n  /**\n   * フォーマット対象の日付\n   */\n  date: Date\n\n  /**\n   * 表示する日付のパーツ。指定しない場合は全て表示\n   */\n  parts?: readonly [DatePart, ...DatePart[]]\n\n  /**\n   * フォーマットオプション\n   */\n  options?: Intl.DateTimeFormatOptions & {\n    /**\n     * 日本語ロケールでスラッシュを無効化し、月を長形式で表示する\n     * @example\n     * // 通常: \"2025/01\"\n     * // disableSlashInJa: true の場合: \"2025年1月\"\n     */\n    disableSlashInJa?: boolean\n\n    /**\n     * 最初の文字を大文字化する\n     * @example\n     * // 通常: \"seg.\" (pt)\n     * // capitalizeFirstLetter: true の場合: \"Seg.\" (pt)\n     */\n    capitalizeFirstLetter?: boolean\n  }\n}\n\n/**\n * useIntlフックの戻り値の型定義\n */\nexport type UseIntlReturn = {\n  /** 利用可能なロケールのリスト */\n  availableLocales: string[]\n  /** メッセージのローカライズ関数 */\n  localize: <T extends keyof Messages>(\n    descriptor: MessageDescriptor<T>,\n    values?: Record<string, PrimitiveType | FormatXMLElementFn<string, string>>,\n    opts?: IntlMessageFormatOptions,\n  ) => string\n\n  /**\n   * 日付をロケールに応じた形式でフォーマットする関数\n   * @param props - フォーマットのオプション\n   * @param props.date - フォーマット対象の日付\n   * @param props.parts - 表示する日付のパーツ。デフォルトは ['year', 'month', 'day', 'weekday']\n   * @param props.options - フォーマットオプション\n   * @param props.options.disableSlashInJa - 日本語ロケールでスラッシュを無効化し、月を長形式で表示する\n   * @param props.options.capitalizeFirstLetter - 最初の文字を大文字化する\n   * @returns フォーマットされた日付文字列\n   * @example\n   * // 基本的な使用法（ロケールのデフォルト形式：曜日なし）\n   * formatDate({ date: new Date() }) // \"2024/01/15\" (ja)\n   *\n   * // 日付を曜日ありで表示\n   * formatDate({ date: new Date(), parts: ['year', 'month', 'day', 'weekday'] }) // \"2024/01/15（水）\" (ja)\n   *\n   * // 特定のフィールドのみ表示\n   * formatDate({ date: new Date(), parts: ['year', 'month'] }) // \"2024/01\" (ja)\n   * formatDate({ date: new Date(), parts: ['weekday'] }) // \"月\" (ja)\n   *\n   * // 日本語でスラッシュを無効化（月を長形式で表示）\n   * formatDate({ date: new Date(), parts: ['year', 'month'], options: { disableSlashInJa: true } }) // \"2024年1月\" (ja)\n   *\n   * // 最初の文字を大文字化\n   * formatDate({ date: new Date(), parts: ['weekday'], options: { capitalizeFirstLetter: true } }) // \"Seg.\" (pt)（指定しなければ \"seg.\"）\n   */\n  formatDate(props: FormatDateProps): string\n\n  /**\n   * 現在のロケールに基づいて週の開始日を決定する関数\n   * Intl.Locale.prototype.getWeekInfo()の動作を再現\n   * getWeekInfo()を使わない理由は、Firefoxでは動作しないため\n   *\n   * @returns 週の開始日（0 = 日曜日, 1 = 月曜日, ..., 6 = 土曜日）\n   */\n  getWeekStartDay: () => number\n  /** 現在のロケール */\n  locale: keyof typeof locales\n}\n\nconst WEEK_START_DAYS = {\n  SUNDAY: 0,\n  MONDAY: 1,\n  TUESDAY: 2,\n  WEDNESDAY: 3,\n  THURSDAY: 4,\n  FRIDAY: 5,\n  SATURDAY: 6,\n} as const\n\nconst DATE_FORMATS: Record<\n  keyof typeof locales,\n  Intl.DateTimeFormatOptions & { weekStartDay: number }\n> = {\n  // localeがja, ja-easyの場合、フォーマットを YYYY/MM/DD 形式にする\n  // 曜日が含まれている場合は括弧を追加する\n  // 参考: https://smarthr.design/products/contents/idiomatic-usage/count/#h2-3\n  ja: {\n    year: 'numeric',\n    month: '2-digit',\n    day: '2-digit',\n    weekday: 'short',\n    weekStartDay: WEEK_START_DAYS.SUNDAY,\n  },\n  'ja-easy': {\n    year: 'numeric',\n    month: 'long',\n    day: 'numeric',\n    weekday: 'short',\n    weekStartDay: WEEK_START_DAYS.SUNDAY,\n  },\n  'en-us': {\n    year: 'numeric',\n    month: 'short',\n    day: '2-digit',\n    weekday: 'short',\n    weekStartDay: WEEK_START_DAYS.SUNDAY,\n  },\n  'id-id': {\n    year: 'numeric',\n    month: 'short',\n    day: '2-digit',\n    weekday: 'short',\n    weekStartDay: WEEK_START_DAYS.MONDAY,\n  },\n  ko: {\n    year: 'numeric',\n    month: 'long',\n    day: 'numeric',\n    weekday: 'short',\n    weekStartDay: WEEK_START_DAYS.SUNDAY,\n  },\n  pt: {\n    year: 'numeric',\n    month: 'short',\n    day: '2-digit',\n    weekday: 'short',\n    weekStartDay: WEEK_START_DAYS.MONDAY,\n  },\n  vi: {\n    year: 'numeric',\n    month: 'long',\n    day: '2-digit',\n    weekday: 'short',\n    weekStartDay: WEEK_START_DAYS.MONDAY,\n  },\n  'zh-cn': {\n    year: 'numeric',\n    month: 'long',\n    day: 'numeric',\n    weekday: 'short',\n    weekStartDay: WEEK_START_DAYS.SUNDAY,\n  },\n  'zh-tw': {\n    year: 'numeric',\n    month: 'long',\n    day: 'numeric',\n    weekday: 'short',\n    weekStartDay: WEEK_START_DAYS.SUNDAY,\n  },\n} as const\n\n/**\n * 各ロケールの括弧設定マップ\n * ロケールに応じて適切な括弧スタイル（半角/全角）と配置を定義\n */\nconst WEEKDAY_FORMATS: Record<keyof typeof locales, { replacer: (base: string) => string }> = {\n  ja: { replacer: (base) => base.replace(/(.+?)\\(([月火水木金土日])\\)$/, '$1（$2）') },\n  'ja-easy': { replacer: (base) => base.replace(/(.+?)\\(([月火水木金土日])\\)$/, '$1（$2）') },\n  'en-us': { replacer: (base) => base.replace(/^([A-Za-z]{3}), (.+)$/, '$2 ($1)') },\n  'id-id': { replacer: (base) => base.replace(/^([A-Za-z]{3}), (.+)$/, '$2 ($1)') },\n  ko: { replacer: (base) => base.replace(/(.+?)([월화수목금토일])$/, '$1 ($2)') },\n  pt: { replacer: (base) => base.replace(/^([A-Za-z]{3}\\.), (.+)$/, '$2 ($1)') },\n  vi: { replacer: (base) => base.replace(/^(\\S+ \\d+), (.+)$/, '$2 ($1)') },\n  'zh-cn': { replacer: (base) => base.replace(/(.+?)\\s*([周][一二三四五六日])$/, '$1（$2）') },\n  'zh-tw': { replacer: (base) => base.replace(/(.+?)\\s*([週][一二三四五六日])$/, '$1（$2）') },\n} as const\n\nconst isValidLocale = (locale: string): locale is keyof typeof locales => locale in locales\n\nconst applyCapitalization = (text: string, shouldCapitalize: boolean) =>\n  shouldCapitalize ? text.charAt(0).toUpperCase() + text.slice(1) : text\n\n/**\n * 多言語化機能を提供するフック\n * react-intlをベースにした国際化機能を提供します\n *\n * @returns {UseIntlReturn} 国際化に関連する関数とプロパティを含むオブジェクト\n * @example\n * // 基本的な使用法（メッセージのローカライズ）\n * const Component = () => {\n *   const { localize } = useIntl()\n *   return <span>{localize({ id: 'smarthr-ui/common/language', defaultText: '日本語' })}</span>\n * }\n *\n * @example\n * // 日付のフォーマット\n * const Component = () => {\n *   const { formatDate } = useIntl()\n *   return <span>{formatDate({ date: new Date() })}</span> // \"2024/01/15\" (ja)\n * }\n *\n * @example\n * // 利用可能なロケールの確認\n * const Component = () => {\n *   const { availableLocales, locale } = useIntl()\n *   return <div>現在のロケール: {locale}</div>\n * }\n */\nexport const useIntl = (): UseIntlReturn => {\n  const intl = useReactIntl()\n  const locale = isValidLocale(intl.locale) ? intl.locale : 'ja'\n  const availableLocales = useAvailableLocales()\n\n  const localize = useCallback(\n    <T extends keyof Messages>(\n      descriptor: MessageDescriptor<T>,\n      values?: Record<string, PrimitiveType | FormatXMLElementFn<string, string>>,\n      opts?: IntlMessageFormatOptions,\n    ): string =>\n      intl.formatMessage({ ...descriptor, defaultMessage: descriptor.defaultText }, values, opts),\n    [intl],\n  )\n\n  const formatDate = useCallback(\n    ({ date, parts = ['year', 'month', 'day'], options }: FormatDateProps): string => {\n      const {\n        disableSlashInJa = false,\n        capitalizeFirstLetter = false,\n        year,\n        month,\n        day,\n        weekday,\n        ...rest\n      } = options || {}\n\n      // パーツの存在を事前に計算\n      const hasPart = parts.reduce(\n        (prev, part) => {\n          prev[part] = true\n          return prev\n        },\n        { year: false, month: false, day: false, weekday: false } as {\n          year: boolean\n          month: boolean\n          day: boolean\n          weekday: boolean\n        },\n      )\n\n      // ロケールのデフォルト形式を取得\n      const isJaKanjiFormat = disableSlashInJa && locale === 'ja'\n      const defaultFormats = DATE_FORMATS[locale]\n\n      // local が 'ja' で漢字表記の場合はゼロ埋めをしないようにする\n      const defaultMonth = isJaKanjiFormat ? 'long' : defaultFormats.month\n      const defaultDay = isJaKanjiFormat ? 'numeric' : defaultFormats.day\n\n      const actualFormatOptions: Intl.DateTimeFormatOptions = {\n        ...rest,\n        year: year ?? (hasPart.year ? defaultFormats.year : undefined),\n        month: month ?? (hasPart.month ? defaultMonth : undefined),\n        day: day ?? (hasPart.day ? defaultDay : undefined),\n        weekday: weekday ?? (hasPart.weekday ? defaultFormats.weekday : undefined),\n      }\n\n      const formattedDate = intl.formatDate(date, actualFormatOptions)\n\n      // 曜日をロケールに応じてフォーマットする\n      let formattedResult = formattedDate\n      const config = WEEKDAY_FORMATS[locale]\n      // 曜日が含まれており、かつ曜日のみの表示でない場合は括弧を追加\n      // 例: \"2025/01/01（水）\" → 括弧あり, \"水\" → 括弧なし\n      if (config && hasPart.weekday && (parts.length !== 1 || parts[0] !== 'weekday')) {\n        formattedResult = config.replacer(formattedDate)\n      }\n\n      return applyCapitalization(formattedResult, capitalizeFirstLetter)\n    },\n    [intl, locale],\n  )\n\n  const getWeekStartDay = (): number => DATE_FORMATS[locale].weekStartDay\n\n  return { availableLocales, localize, formatDate, locale, getWeekStartDay }\n}\n"],"names":[],"mappings":";;;;;;;;AA6GA;AACE;AACA;AAQF;;;;AAOE;AACE;AACA;AACA;AACA;;AAED;AACD;AACE;AACA;AACA;AACA;;AAED;AACD;AACE;AACA;AACA;AACA;;AAED;AACD;AACE;AACA;AACA;AACA;;AAED;AACD;AACE;AACA;AACA;AACA;;AAED;AACD;AACE;AACA;AACA;AACA;;AAED;AACD;AACE;AACA;AACA;AACA;;AAED;AACD;AACE;AACA;AACA;AACA;;AAED;AACD;AACE;AACA;AACA;AACA;;AAED;;AAGH;;;AAGG;AACH;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGF;AAEA;AAGA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AACI;AACL;AACA;AACA;AAEA;;;;;AAyBQ;AACA;AACF;;AAUF;AACA;;AAGA;AACA;AAEA;AACE;AACA;AACA;AACA;AACA;;;;;AAOF;;;;AAIE;;AAGF;AACF;;;AAOJ;;"}