거래 관련 페이지 다국어화 완료 (현금영수증, 에스크로, 빌링)

- 현금영수증 페이지 다국어화 (목록, 상세, 수기발행)
  * 승인/취소 금액 통화 표기 개선 (₩ prefix for EN)
  * 검색옵션, 다운로드, 용도변경 등 모든 텍스트 다국어화
- 에스크로 페이지 다국어화 (목록, 상세)
  * 헤더 타이틀 및 UI 텍스트 다국어화
- 빌링 페이지 다국어화 (목록, 상세, 청구)
  * 통화 표기 언어별 처리 (한국어: 원 suffix / 영어: ₩ prefix)
- 번역 키 추가: cashReceipt, escrow, billing, common 네임스페이스
- 모든 거래 페이지 일관된 다국어 지원 완료

🤖 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-29 17:48:25 +09:00
parent aedf5d3d8f
commit 00b0290fa7
10 changed files with 93 additions and 38 deletions

View File

@@ -1,4 +1,5 @@
import { ChangeEvent, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { PATHS } from '@/shared/constants/paths';
import { IMAGE_ROOT } from '@/shared/constants/common';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
@@ -18,6 +19,7 @@ import { notiBar, snackBar } from '@/shared/lib';
export const BillingChargePage = () => {
const { navigate } = useNavigate();
const { t } = useTranslation();
const [billKey, setBillKey] = useState<string>('');
const [productName, setProductName] = useState<string>('');
@@ -30,7 +32,7 @@ export const BillingChargePage = () => {
const [calendarOpen, setCalendarOpen] = useState<boolean>(false);
useSetHeaderTitle('빌링 결제 신청');
useSetHeaderTitle(t('billing.charge'));
useSetHeaderType(HeaderType.RightClose);
useSetOnBack(() => {
navigate(PATHS.transaction.billing.list);

View File

@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router';
import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
@@ -24,6 +25,7 @@ import { AmountInfoSection } from '@/entities/transaction/ui/section/amount-info
export const BillingDetailPage = () => {
const { navigate } = useNavigate();
const { t, i18n } = useTranslation();
const location = useLocation();
const tid = location?.state.tid;
const serviceCode = location?.state.serviceCode;
@@ -32,7 +34,7 @@ export const BillingDetailPage = () => {
const [billingInfo, setBillingInfo] = useState<BillingInfo>();
const [amountInfo, setAmountInfo] = useState<AmountInfo>();
useSetHeaderTitle('빌링 상세');
useSetHeaderTitle(t('billing.detailTitle'));
useSetHeaderType(HeaderType.RightClose);
useSetOnBack(() => {
navigate(PATHS.transaction.billing.list);
@@ -67,12 +69,13 @@ export const BillingDetailPage = () => {
<div className="txn-num-group">
<div className="txn-amount">
<div className="value">
{ i18n.language === 'en' && <span className="unit">{ t('home.currencySymbol') }</span> }
<NumericFormat
value={ amountInfo?.transactionAmount }
value={ amountInfo?.transactionAmount }
thousandSeparator
displayType="text"
></NumericFormat>
<span className="unit"></span>
{ i18n.language !== 'en' && <span className="unit">{ t('home.currencyWon') }</span> }
</div>
</div>
<div className="txn-mid">

View File

@@ -1,5 +1,6 @@
import moment from 'moment';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useStore } from '@/shared/model/store';
import { IMAGE_ROOT } from '@/shared/constants/common';
import { PATHS } from '@/shared/constants/paths';
@@ -33,6 +34,7 @@ import useIntersectionObserver from '@/widgets/intersection-observer';
export const BillingListPage = () => {
const { navigate } = useNavigate();
const { t } = useTranslation();
const userMid = useStore.getState().UserStore.mid;
const [onActionIntersect, setOnActionIntersect] = useState<boolean>(false);
@@ -54,7 +56,7 @@ export const BillingListPage = () => {
const [downloadBottomSheetOn, setDownloadBottomSheetOn] = useState<boolean>(false);
useSetHeaderTitle('빌링');
useSetHeaderTitle(t('billing.title'));
useSetHeaderType(HeaderType.LeftArrow);
useSetOnBack(() => {
navigate(PATHS.home);
@@ -176,19 +178,19 @@ export const BillingListPage = () => {
readOnly={ true }
/>
<button className="filter-btn">
<img
src={ IMAGE_ROOT + '/ico_setting.svg' }
alt="검색옵션"
<img
src={ IMAGE_ROOT + '/ico_setting.svg' }
alt={ t('transaction.searchOptions') }
onClick={ () => onClickToOpenFilter() }
/>
</button>
</div>
<button className="download-btn">
<img
src={ IMAGE_ROOT + '/ico_download.svg' }
alt="다운로드"
<img
src={ IMAGE_ROOT + '/ico_download.svg' }
alt={ t('transaction.download') }
onClick={ () => onClickToOpenDownloadBottomSheet() }
/>
/>
</button>
</div>
</div>

View File

@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router';
import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
@@ -31,6 +32,7 @@ import { snackBar } from '@/shared/lib';
export const CashReceiptDetailPage = () => {
const { navigate, reload } = useNavigate();
const { t } = useTranslation();
const location = useLocation();
const [amountInfo, setAmountInfo] = useState<AmountInfo>();
@@ -42,8 +44,8 @@ export const CashReceiptDetailPage = () => {
const [purposeType, setPurposeType] = useState<string>();
const [canDownloadReceipt, setCanDownloadReceipt] = useState<boolean>(false);
useSetHeaderTitle('현금영수증 상세');
useSetHeaderTitle(t('cashReceipt.detailTitle'));
useSetHeaderType(HeaderType.RightClose);
useSetOnBack(() => {
navigate(PATHS.transaction.cashReceipt.list);
@@ -140,10 +142,10 @@ export const CashReceiptDetailPage = () => {
{ (issueInfo?.transactionType === CashReceiptTransactionType.APPROVAL) &&
(issueInfo?.processResult === '발급완료') &&
<div className="apply-row">
<button
<button
className="btn-50 btn-blue flex-1"
onClick={ () => onClickToPurposeUpdate() }
> </button>
>{ t('cashReceipt.changePurpose') }</button>
</div>
}
</div>

View File

@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { CashReceiptHandWrittenIssuanceStep1 } from '@/entities/transaction/ui/cash-receipt-hand-written-issuance-step1';
@@ -13,6 +14,7 @@ import { snackBar } from '@/shared/lib';
export const CashReceitHandWrittenIssuancePage = () => {
const { navigate } = useNavigate();
const { t } = useTranslation();
let businessInfo = useStore.getState().UserStore.businessInfo;
let userMid = useStore.getState().UserStore.mid;
// 1 or 2
@@ -30,7 +32,7 @@ export const CashReceitHandWrittenIssuancePage = () => {
const [taxFreeAmount, setTaxFreeAmount] = useState<number>(0);
const [serviceCharge, setServiceCharge] = useState<number>(0);
useSetHeaderTitle('수기 발행');
useSetHeaderTitle(t('cashReceipt.manualIssuance'));
useSetHeaderType(HeaderType.RightClose);
useSetFooterMode(false);
@@ -172,12 +174,12 @@ export const CashReceitHandWrittenIssuancePage = () => {
}
</div>
<div className="apply-row">
<button
<button
className="btn-50 btn-blue flex-1"
onClick={ () => onClickToChangeTab() }
>
{ (processStep === ProcessStep.One) && '다음' }
{ (processStep === ProcessStep.Two) && '발행' }
{ (processStep === ProcessStep.One) && t('common.next') }
{ (processStep === ProcessStep.Two) && t('cashReceipt.issue') }
</button>
</div>
</div>

View File

@@ -1,5 +1,6 @@
import moment from 'moment';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { NumericFormat } from 'react-number-format';
import { IMAGE_ROOT } from '@/shared/constants/common';
import { PATHS } from '@/shared/constants/paths';
@@ -37,6 +38,7 @@ import useIntersectionObserver from '@/widgets/intersection-observer';
export const CashReceiptListPage = () => {
const { navigate } = useNavigate();
const { t, i18n } = useTranslation();
const userMid = useStore.getState().UserStore.mid;
const [onActionIntersect, setOnActionIntersect] = useState<boolean>(false);
@@ -60,7 +62,7 @@ export const CashReceiptListPage = () => {
const [emailBottomSheetOn, setEmailBottomSheetOn] = useState<boolean>(false);
useSetHeaderTitle('현금영수증');
useSetHeaderTitle(t('cashReceipt.title'));
useSetHeaderType(HeaderType.LeftArrow);
useSetOnBack(() => {
navigate(PATHS.home);
@@ -199,7 +201,7 @@ export const CashReceiptListPage = () => {
<button className="filter-btn">
<img
src={ IMAGE_ROOT + '/ico_setting.svg' }
alt="검색옵션"
alt={ t('transaction.searchOptions') }
onClick={ () => onClickToOpenFilter() }
/>
</button>
@@ -207,20 +209,21 @@ export const CashReceiptListPage = () => {
<button className="download-btn">
<img
src={IMAGE_ROOT + '/ico_download.svg'}
alt="다운로드"
alt={ t('transaction.download') }
onClick={() => onClickToDownloadExcel()}
/>
</button>
</div>
<div className="credit-summary">
<div className="row">
<span className="label"></span>
<span className="label">{ t('cashReceipt.approval') }</span>
<strong className="amount22">
<NumericFormat
value={ approvalAmount }
thousandSeparator
displayType="text"
suffix='원'
prefix={ i18n.language === 'en' ? t('home.currencySymbol') : '' }
suffix={ i18n.language === 'en' ? '' : t('home.currencyWon') }
></NumericFormat>
</strong>
<span className="count">
@@ -234,13 +237,14 @@ export const CashReceiptListPage = () => {
</span>
</div>
<div className="row">
<span className="label"></span>
<span className="label">{ t('common.cancel') }</span>
<strong className="amount19">
<NumericFormat
value={ cancelAmount }
thousandSeparator
displayType="text"
suffix='원'
prefix={ i18n.language === 'en' ? t('home.currencySymbol') : '' }
suffix={ i18n.language === 'en' ? '' : t('home.currencyWon') }
></NumericFormat>
</strong>
<span className="count">

View File

@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router';
import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
@@ -35,6 +36,7 @@ import { AmountInfoSection } from '@/entities/transaction/ui/section/amount-info
export const EscrowDetailPage = () => {
const { navigate } = useNavigate();
const { t } = useTranslation();
const location = useLocation();
const paramTid = location?.state.tid;
const serviceCode = location?.state.serviceCode;
@@ -60,7 +62,7 @@ export const EscrowDetailPage = () => {
const [orderNumber, setOrderNumber] = useState<string>();
const [tid, setTid] = useState<string | undefined>(paramTid);
useSetHeaderTitle('에스크로 상세');
useSetHeaderTitle(t('escrow.detailTitle'));
useSetHeaderType(HeaderType.RightClose);
useSetOnBack(() => {
navigate(PATHS.transaction.escrow.list);

View File

@@ -1,5 +1,6 @@
import moment from 'moment';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useStore } from '@/shared/model/store';
import { IMAGE_ROOT } from '@/shared/constants/common';
import { PATHS } from '@/shared/constants/paths';
@@ -32,6 +33,7 @@ import useIntersectionObserver from '@/widgets/intersection-observer';
export const EscrowListPage = () => {
const { navigate } = useNavigate();
const { t } = useTranslation();
const userInfo = useStore((state) => state.UserStore.userInfo);
const userMid = useStore.getState().UserStore.mid;
@@ -53,7 +55,7 @@ export const EscrowListPage = () => {
const [emailBottomSheetOn, setEmailBottomSheetOn] = useState<boolean>(false);
useSetHeaderTitle('에스크로');
useSetHeaderTitle(t('escrow.title'));
useSetHeaderType(HeaderType.LeftArrow);
useSetOnBack(() => {
navigate(PATHS.home);
@@ -180,19 +182,19 @@ export const EscrowListPage = () => {
readOnly={ true }
/>
<button className="filter-btn">
<img
src={ IMAGE_ROOT + '/ico_setting.svg' }
alt="검색옵션"
<img
src={ IMAGE_ROOT + '/ico_setting.svg' }
alt={ t('transaction.searchOptions') }
onClick={ () => onClickToOpenFilter() }
/>
</button>
</div>
<button className="download-btn">
<img
src={ IMAGE_ROOT + '/ico_download.svg' }
alt="다운로드"
<img
src={ IMAGE_ROOT + '/ico_download.svg' }
alt={ t('transaction.download') }
onClick={ () => onClickToDownloadExcel() }
/>
/>
</button>
</div>
</div>