Add i18n localization foundation to additional-service entity

- Convert 10 constant files to i18n-compatible getter functions:
  - ars/constant.ts: ARS status codes and payment methods
  - sms-payment/constant.ts: SMS payment status codes
  - link-pay/constant.ts: Link payment status codes
  - account-holder-auth/constant.ts: Account holder verification
  - account-holder-search/constant.ts: Account holder search types
  - face-auth/constant.ts: Face authentication types
  - fund-account/constant.ts: Fund account status codes
  - alimtalk/constant.ts: Alimtalk status and transaction types
  - payout/constant.ts: Payout search types
  - key-in/constant.ts: Key-in cancel types

- Refactor lib/payment-status-utils.ts to curried functions with TFunction

- Add 63 translation keys to additionalService namespace in ko.json/en.json

- Localize 2 UI components as examples:
  - ui/filter/ars-filter.tsx
  - ui/ars/resend-sms-bottom-sheet.tsx

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Jay Sheen
2025-10-30 18:06:52 +09:00
parent e60fe0f014
commit ee932f2a46
15 changed files with 345 additions and 199 deletions

View File

@@ -1,6 +1,8 @@
import { BottomSheetMotionDuration, BottomSheetMotionVaiants } from "@/entities/common/model/constant";
import { IMAGE_ROOT } from '@/shared/constants/common';
import { motion } from 'framer-motion';
import { useTranslation } from 'react-i18next';
export interface ArsResendSmsBottomSheetProps {
bottomSheetOn: boolean;
setBottomSheetOn: (bottomSheetOn: boolean) => void;
@@ -14,6 +16,7 @@ export const ArsResendSmsBottomSheet = ({
phoneNumber,
callResendSms
}: ArsResendSmsBottomSheetProps) => {
const { t } = useTranslation();
const onClickToClose = () => {
setBottomSheetOn(false);
@@ -40,14 +43,14 @@ export const ArsResendSmsBottomSheet = ({
>
<div className="bottomsheet-header">
<div className="bottomsheet-title">
<h2>SMS </h2>
<h2>{t('additionalService.common.resend')} SMS</h2>
<button
className="close-btn"
type="button"
>
<img
<img
src={ IMAGE_ROOT + '/ico_close.svg' }
alt="닫기"
alt={t('common.close')}
onClick={ () => onClickToClose() }
/>
</button>
@@ -55,7 +58,7 @@ export const ArsResendSmsBottomSheet = ({
</div>
<div className="bottomsheet-content">
<div className="bottom-section">
<p>[01095800212] SMS를 ?</p>
<p>[{phoneNumber}] {t('transaction.sms.resendConfirmMessage')}</p>
</div>
</div>
<div className="bottomsheet-footer">
@@ -63,7 +66,7 @@ export const ArsResendSmsBottomSheet = ({
className="btn-50 btn-blue flex-1"
type="button"
onClick={ () => onCliickToResendSms() }
></button>
>{t('transaction.apply')}</button>
</div>
</motion.div>
</>

View File

@@ -1,19 +1,20 @@
import { useEffect, useState } from 'react';
import { motion } from 'framer-motion';
import { useTranslation } from 'react-i18next';
import { IMAGE_ROOT } from '@/shared/constants/common';
import { FilterSelect } from '@/shared/ui/filter/select';
import { FilterCalendar } from '@/shared/ui/filter/calendar';
import { FilterButtonGroups } from '@/shared/ui/filter/button-groups';
import { FilterRangeAmount } from '@/shared/ui/filter/range-amount';
import {
FilterMotionDuration,
FilterMotionStyle,
import {
FilterMotionDuration,
FilterMotionStyle,
FilterMotionVariants
} from '@/entities/common/model/constant';
import moment from 'moment';
import { FilterInput } from '@/shared/ui/filter/input';
import { OrderStatus, PaymentStatus } from '../../model/ars/types';
import { ArsOrderStatusBtnGroup, ArsPaymentStatusBtnGroup } from '../../model/ars/constant';
import { getArsOrderStatusBtnGroup, getArsPaymentStatusBtnGroup } from '../../model/ars/constant';
import { useStore } from '@/shared/model/store';
import { FilterSelectMid } from '@/shared/ui/filter/select-mid';
@@ -67,7 +68,9 @@ export const ArsFilter = ({
const [filterOrderStatus, setFilterOrderStatus] = useState<OrderStatus>(orderStatus);
const [filterMinAmount, setFilterMinAmount] = useState<number | undefined>(minAmount);
const [filterMaxAmount, setFilterMaxAmount] = useState<number | undefined>(maxAmount);
const { t } = useTranslation();
const onClickToClose = () => {
setFilterOn(false);
};
@@ -100,15 +103,15 @@ export const ArsFilter = ({
>
<div className="full-menu-container">
<div className="full-menu-header">
<div className="full-menu-title center"></div>
<div className="full-menu-title center">{t('filter.filter')}</div>
<div className="full-menu-actions">
<button
<button
id="closeFullMenu"
className="full-menu-close"
>
<img
<img
src={ IMAGE_ROOT + '/ico_close.svg' }
alt="닫기"
alt={t('filter.close')}
onClick={ () => onClickToClose() }
/>
</button>
@@ -116,36 +119,36 @@ export const ArsFilter = ({
</div>
<div className="option-list pt-16">
<FilterSelectMid
title='가맹점'
title={t('filter.merchant')}
selectSetter={ setFilterMid }
showType={ 'GID'}
></FilterSelectMid>
<FilterInput
title='주문번호'
title={t('transaction.constants.orderNumber')}
inputValue={ filterMoid }
inputSetter={ setFilterMoid }
></FilterInput>
<FilterCalendar
title='조회기간'
title={t('filter.period')}
startDate={ filterFromDate }
endDate={ filterToDate }
setStartDate={ setFilterFromDate }
setEndDate={ setFilterToDate }
></FilterCalendar>
<FilterButtonGroups
title='결제상태'
title={t('transaction.filter.paymentStatus')}
activeValue={ filterPaymentStatus }
btnGroups={ ArsPaymentStatusBtnGroup }
btnGroups={ getArsPaymentStatusBtnGroup(t) }
setter={ setFilterPaymentStatus }
></FilterButtonGroups>
<FilterButtonGroups
title='주문상태'
title={t('transaction.filter.orderStatus')}
activeValue={ filterOrderStatus }
btnGroups={ ArsOrderStatusBtnGroup }
btnGroups={ getArsOrderStatusBtnGroup(t) }
setter={ setFilterOrderStatus }
></FilterButtonGroups>
<FilterRangeAmount
title='거래금액'
title={t('filter.transactionAmount')}
minAmount={ filterMinAmount }
maxAmount={ filterMaxAmount }
setMinAmount={ setFilterMinAmount }
@@ -153,10 +156,10 @@ export const ArsFilter = ({
></FilterRangeAmount>
</div>
<div className="apply-row">
<button
<button
className="btn-50 btn-blue flex-1"
onClick={ () => onClickToSetFilter() }
></button>
>{t('filter.apply')}</button>
</div>
</div>
</motion.div>