홈 화면, 메뉴, 네비게이션, 계정 관리 다국어화 완료

- 홈 화면 일/월 탭, 매출/정산 현황 다국어화
- 매출/정산 상세 정보 라벨 다국어화 (승인/취소 건수, 정산한도 등)
- 거래 인사이트 및 랭킹 섹션 다국어화
- 요일 이름 동적 번역 기능 추가 (월요일-일요일 → Monday-Sunday)
- 계정 관리 화면 다국어화 (계정 상태, 로그인 범위, 권한 설정)
- 메뉴 카테고리 다국어화 (en 언어시 menuNameEng 사용)
- 즐겨찾기 메뉴 다국어화
- 하단 네비게이션 버튼 다국어화
- 공지사항 제목 다국어화

🤖 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 16:53:23 +09:00
parent 8c352e0c2c
commit 4faa8affb0
10 changed files with 195 additions and 132 deletions

View File

@@ -4,6 +4,8 @@ import { UserAccountAuthPermList } from './user-account-auth-perm-list';
import { useUserMenuPermissionsMutation } from '@/entities/user/api/use-user-menu-permission-mutation';
import { useUserUpdatePermissionsMutation } from '@/entities/user/api/use-user-update-permission-mutation';
import { UserMenuPermissionData } from '@/entities/user/model/types';
import { MenuItems } from '@/entities/common/model/constant';
import { useTranslation } from 'react-i18next';
export const UserAccountAuthWrap = ({
mid,
@@ -11,6 +13,7 @@ export const UserAccountAuthWrap = ({
idCL,
status,
}: UserAccountAuthWrapProps) => {
const { t } = useTranslation();
const [currentStatus, setCurrentStatus] = useState(status);
const [currentIdCL, setCurrentIdCl] = useState(idCL);
const [permissions, setPermissions] = useState<Array<UserMenuPermissionData>>([]);
@@ -46,84 +49,21 @@ export const UserAccountAuthWrap = ({
const idCLChanged = currentIdCL !== idCL;
setHasChanges(statusChanged || idCLChanged);
}, [currentStatus, currentIdCL, status, idCL]);
let menuItems = [
{menuId: 30, parent: 30, menuName: '거래조회', subMenu:
[
{menuId: 31, parent: 30, menuName: '거래내역조회'},
{menuId: 32, parent: 30, menuName: '현금영수증발행'},
{menuId: 33, parent: 30, menuName: '에스크로'},
{menuId: 34, parent: 30, menuName: '빌링'}
]
},
{menuId: 35, parent: 35, menuName: '정산조회', subMenu:
[
{menuId: 36, parent: 35, menuName: '정산달력'},
{menuId: 37, parent: 35, menuName: '정산내역'},
]
},
{menuId: 38, parent: 38, menuName: '가맹점관리', subMenu:
[
{menuId: 39, parent: 38, menuName: '가맹점정보'},
{menuId: 40, parent: 38, menuName: '등록현황'},
]
},
{menuId: 41, parent: 41, menuName: '결제관리', subMenu:
[
{menuId: 42, parent: 41, menuName: '결제정보'},
{menuId: 43, parent: 41, menuName: '결제데이터통보'},
]
},
{menuId: 44, parent: 44, menuName: '계정관리', subMenu:
[
{menuId: 45, parent: 44, menuName: '사용자관리'},
{menuId: 46, parent: 44, menuName: '비밀번호관리'},
]
},
{menuId: 47, parent: 47, menuName: '부가세신고자료', subMenu:
[
{menuId: 48, parent: 47, menuName: '세금계산서'},
{menuId: 49, parent: 47, menuName: '부가세참고'},
]
},
{menuId: 50, parent: 50, menuName: '부가서비스', subMenu:
[
{menuId: 51, parent: 50, menuName: '부가서비스소개'},
{menuId: 52, parent: 50, menuName: '신용카드ARS카드결제'},
{menuId: 53, parent: 50, menuName: '지급대행'},
{menuId: 54, parent: 50, menuName: '링크결제'},
{menuId: 55, parent: 50, menuName: '자금이체'},
{menuId: 56, parent: 50, menuName: 'KEY-IN결제'},
{menuId: 57, parent: 50, menuName: 'SMS결제통보'},
{menuId: 58, parent: 50, menuName: '알림톡결제통보'},
{menuId: 59, parent: 50, menuName: '계좌점유인증'},
{menuId: 60, parent: 50, menuName: '계좌성명조회'},
{menuId: 65, parent: 50, menuName: '안면인증'},
]
},
{menuId: 61, parent: 61, menuName: '고객지원', subMenu:
[
{menuId: 62, parent: 61, menuName: '공지사항'},
{menuId: 63, parent: 61, menuName: '자주묻는질문'},
{menuId: 64, parent: 61, menuName: '1:1문의'},
]
},
]
return (
<>
<div className="ing-list pdtop pb-86">
<div className="perm-form">
<div className="perm-field">
<div className="perm-label"> </div>
<div className="perm-label">{t('account.accountStatus')}</div>
<div className="perm-control">
<select value={currentStatus} onChange={(e) => setCurrentStatus(e.target.value)}>
<option value="NORMAL"></option>
<option value="SUSPENDED"></option>
<option value="NORMAL">{t('account.active')}</option>
<option value="SUSPENDED">{t('account.inactive')}</option>
</select>
</div>
</div>
<div className="perm-field">
<div className="perm-label"> </div>
<div className="perm-label">{t('account.loginScope')}</div>
<div className="perm-control">
<select value={currentIdCL} onChange={(e) => setCurrentIdCl(e.target.value)}>
<option value="MID">MID</option>
@@ -133,14 +73,14 @@ export const UserAccountAuthWrap = ({
</div>
</div>
<div className="ing-title fs18"> </div>
<div className="ing-title fs18">{t('account.menuPermissions')}</div>
<UserAccountAuthPermList
mid={ mid }
usrid={ usrid }
idCL={ currentIdCL }
status={ currentStatus }
menuItems={ menuItems }
menuItems={ MenuItems }
menuGrants={ permissions }
></UserAccountAuthPermList>
@@ -160,7 +100,7 @@ export const UserAccountAuthWrap = ({
});
}}
>
{updatePermissionsMutation.isPending ? '저장 중...' : '저장'}
{updatePermissionsMutation.isPending ? t('common.saving') : t('common.save')}
</button>
</div>

View File

@@ -3,7 +3,7 @@ import { NumericFormat } from 'react-number-format';
import { useEffect, useState } from 'react';
import { IMAGE_ROOT } from '@/shared/constants/common';
import { useHomeTodayMutation } from '../api/use-home-today-mutation';
import {
import {
HomeTodayParams,
HomeTodayResponse,
Sales,
@@ -12,9 +12,11 @@ import {
import { useNavigate } from '@/shared/lib/hooks';
import { PATHS } from '@/shared/constants/paths';
import { useStore } from '@/shared/model/store';
import { useTranslation } from 'react-i18next';
export const BoxContainer1 = () => {
const { navigate } = useNavigate();
const { t } = useTranslation();
const userMid = useStore.getState().UserStore.mid;
const [mid, setMid] = useState<string>(userMid);
@@ -66,14 +68,14 @@ export const BoxContainer1 = () => {
return (
<>
<div className="box-wrap">
<h3> </h3>
<h3>{t('home.todaySales')}</h3>
<div className="today-sales">
<span className="won01">
<NumericFormat
value={ sales?.todayTotalAmount }
thousandSeparator
displayType="text"
suffix='원'
suffix={t('home.currencyWon')}
></NumericFormat>
</span>
<span className={ `per ${(increaseRate && increaseRate >= 0)? 'plus': 'minus'}` }>
@@ -86,67 +88,67 @@ export const BoxContainer1 = () => {
></NumericFormat>
</span>
<a className="arrow">
<img
src={ IMAGE_ROOT + '/ico_arrow.svg' }
alt="오늘 매출 바로가기"
<img
src={ IMAGE_ROOT + '/ico_arrow.svg' }
alt={t('home.goToSales')}
onClick={ onClickToNavigate }
/>
</a>
</div>
<ul className="dales-detail">
<li className="approve">
{t('home.approvalCount')}
<strong style={{marginLeft: '4px'}}>
<NumericFormat
value={ sales?.totalCount }
thousandSeparator
displayType="text"
suffix='건'
suffix={t('home.count')}
></NumericFormat>
</strong>
</li>
<li className="cancel">
{t('home.cancelCount')}
<strong style={{marginLeft: '4px'}}>
<NumericFormat
value={ sales?.cancelCount }
thousandSeparator
displayType="text"
suffix='건'
suffix={t('home.count')}
></NumericFormat>
</strong>
</li>
</ul>
</div>
<div className="box-wrap">
<h3> </h3>
<h3>{t('home.todaySettlement')}</h3>
<div className="today-sales">
<span className="won02">
<NumericFormat
value={ settlement?.todaySettlementAmount }
thousandSeparator
displayType="text"
suffix='원'
suffix={t('home.currencyWon')}
></NumericFormat>
</span>
<span className="per"></span>
<span className="per">{t('home.depositCompleted')}</span>
<a className="arrow">
<img
src={ IMAGE_ROOT + '/ico_arrow.svg' }
alt="오늘 매출 바로가기"
<img
src={ IMAGE_ROOT + '/ico_arrow.svg' }
alt={t('home.goToSales')}
onClick={ onClickToNavigate }
/>
</a>
</div>
<div className="progressbar">
<div className="progress-header">
<span className="progress-title"></span>
<span className="progress-title">{t('home.settlementLimit')}</span>
<span className="progress-remaining">
<NumericFormat
value={ availableLimit }
thousandSeparator
displayType="text"
suffix='% 남음'
suffix={t('home.percentRemaining')}
></NumericFormat>
</span>
</div>
@@ -156,13 +158,13 @@ export const BoxContainer1 = () => {
</div>
</div>
<div className="remain-limit">
<span></span>
<span>{t('home.remainingSettlementLimit')}</span>
<strong style={{marginLeft: '4px'}}>
<NumericFormat
value={ settlement?.availableCredit }
thousandSeparator
displayType="text"
suffix='원'
suffix={t('home.currencyWon')}
></NumericFormat>
</strong>
</div>

View File

@@ -8,9 +8,11 @@ import { NumericFormat } from 'react-number-format';
import { useNavigate } from '@/shared/lib/hooks';
import { PATHS } from '@/shared/constants/paths';
import { useStore } from '@/shared/model/store';
import { useTranslation } from 'react-i18next';
export const BoxContainer2 = () => {
const { navigate } = useNavigate();
const { t, i18n } = useTranslation();
const userMid = useStore.getState().UserStore.mid;
const [mid, setMid] = useState<string>(userMid);
@@ -83,22 +85,38 @@ export const BoxContainer2 = () => {
navigate(PATHS.settlement.list);
};
const translateDayOfWeek = (koreanDay: string): string => {
if (i18n.language !== 'en') return koreanDay;
const dayMap: { [key: string]: string } = {
'월요일': 'Monday',
'화요일': 'Tuesday',
'수요일': 'Wednesday',
'목요일': 'Thursday',
'금요일': 'Friday',
'토요일': 'Saturday',
'일요일': 'Sunday'
};
return dayMap[koreanDay] || koreanDay;
};
return (
<>
<div>
<div className="section-header">
<h3>/ </h3>
<p>( / )</p>
<h3>{t('home.salesSettlementStatus')}</h3>
<p>({t('home.comparedToPreviousMonth')})</p>
</div>
<div className="box-wrap two-sales">
<h4> </h4>
<h4>{t('home.totalSales')}</h4>
<div className="today-sales mt-sty">
<span className="won01">
<NumericFormat
value={ sales?.currentMonthAmount }
thousandSeparator
displayType="text"
suffix='원'
suffix={t('home.currencyWon')}
></NumericFormat>
</span>
<span className={ `per ${(salesIncrease && salesIncrease >= 0)? 'plus': 'minus'}` }>
@@ -111,23 +129,23 @@ export const BoxContainer2 = () => {
></NumericFormat>
</span>
<a className="arrow">
<img
src={ IMAGE_ROOT + '/ico_arrow.svg' }
alt="오늘 매출 바로가기"
<img
src={ IMAGE_ROOT + '/ico_arrow.svg' }
alt={t('home.goToSales')}
onClick={ onClickToNavigate }
/>
</a>
</div>
</div>
<div className="box-wrap two-sales">
<h4> </h4>
<h4>{t('home.totalSettlement')}</h4>
<div className="today-sales mt-sty">
<span className="won02">
<NumericFormat
value={ settlement?.currentMonthSettlementAmount }
thousandSeparator
displayType="text"
suffix='원'
suffix={t('home.currencyWon')}
></NumericFormat>
</span>
<span className={ `per ${(settlementIncrease && settlementIncrease >= 0)? 'plus': 'minus'}` }>
@@ -140,9 +158,9 @@ export const BoxContainer2 = () => {
></NumericFormat>
</span>
<a className="arrow">
<img
<img
src={ IMAGE_ROOT + '/ico_arrow.svg' }
alt="오늘 매출 바로가기"
alt={t('home.goToSales')}
onClick={ onClickToNavigate }
/>
</a>
@@ -151,37 +169,37 @@ export const BoxContainer2 = () => {
</div>
<div>
<div className="section-header">
<h3> </h3>
<p>( )</p>
<h3>{t('home.transactionInsights')}</h3>
<p>({t('home.basedOnLastWeek')})</p>
</div>
<div className="box-wrap two-sales img-customer">
<h4> </h4>
<h4>{t('home.averageTransactionAmount')}</h4>
<div className="two-account">
<span>
<NumericFormat
value={ averageTransactionAmount }
thousandSeparator
displayType="text"
suffix='원'
suffix={t('home.currencyWon')}
></NumericFormat>
</span>
</div>
</div>
<div className="box-wrap two-sales img-states">
<h4> </h4>
<h4>{t('home.dailyAverageSalesAndCount')}</h4>
<div className="two-account">
<span>
<NumericFormat
value={ dailyAverageSales }
thousandSeparator
displayType="text"
suffix='원'
suffix={t('home.currencyWon')}
></NumericFormat>
(<NumericFormat
value={ dailyAverageCount }
thousandSeparator
displayType="text"
suffix='원'
suffix={t('home.currencyWon')}
></NumericFormat>)
</span>
</div>
@@ -189,13 +207,13 @@ export const BoxContainer2 = () => {
</div>
<div>
<div className="box-wrap ranking">
<h4> </h4>
<h4>{t('home.topSalesDays')}</h4>
<ul>
{
topSalesDayInfo?.daySalesRatios.map((value, index) => (
<li key={`key-day-sales-ratio-${index}`}>
<span className={ `ranking-num-${(index === 0)? '01': 'ot'}` }>{ (index + 1) }</span>
<span>{ value.dayOfWeek }</span>
<span>{ translateDayOfWeek(value.dayOfWeek) }</span>
<span className="last-per-01">{ value.ratio + '%' }</span>
</li>
))
@@ -203,7 +221,7 @@ export const BoxContainer2 = () => {
</ul>
</div>
<div className="box-wrap ranking">
<h4> </h4>
<h4>{t('home.topSalesHours')}</h4>
<ul>
{
topSalesTimeInfo?.timeSalesRatios.map((value, index) => (
@@ -217,7 +235,7 @@ export const BoxContainer2 = () => {
</ul>
</div>
<div className="box-wrap ranking">
<h4> </h4>
<h4>{t('home.topPaymentMethods')}</h4>
<ul>
{
topPaymentMethodInfo?.paymentMethodRatios.map((value, index) => (

View File

@@ -3,9 +3,11 @@ import { useState } from 'react';
import { BoxContainer1 } from './day-status-box-container1';
import { BoxContainer2 } from './day-status-box-container2';
import { HomeBannerList } from './home-banner-list';
import { HomeNoticeList } from './home-notice-list';
import { HomeNoticeList } from './home-notice-list';
import { useTranslation } from 'react-i18next';
export const DayStatusBox = () => {
const { t } = useTranslation();
const [tabActive, setTabActive] = useState<1 | 2>(1);
return (
@@ -14,14 +16,14 @@ export const DayStatusBox = () => {
<div className="day-tab">
<div>{ moment().format('YYYY.MM.DD') }</div>
<div>
<button
<button
className={ `day-tab-btn ${(tabActive===1)? 'active': ''}` }
onClick={ () => setTabActive(1) }
></button>
<button
>{t('home.daily')}</button>
<button
className={ `day-tab-btn ${(tabActive===2)? 'active': ''}` }
onClick={ () => setTabActive(2) }
></button>
>{t('home.monthly')}</button>
</div>
</div>
<div className={ `con-box one ${(tabActive===1)? 'active': ''}` }>

View File

@@ -6,6 +6,7 @@ import { IMAGE_ROOT } from '@/shared/constants/common';
import { UserFavorite } from '@/entities/user/model/types';
import { useStore } from '@/shared/model/store';
import { useLocation } from 'react-router';
import { useTranslation } from 'react-i18next';
/*
const items: Array<UserFavorite> = [
@@ -32,13 +33,14 @@ export const FavoriteWrapper = ({
setMenuOn
}: FavoriteWrapperProps) => {
const { navigate } = useNavigate();
const { i18n, t } = useTranslation();
const [favoriteItems, setFavoriteItems] = useState<Array<UserFavorite>>([]);
const itemAdd: UserFavorite = {
menuId: 0,
menuName: '편집하기',
menuNameEng: 'edit',
menuName: t('favorite.edit'),
menuNameEng: t('favorite.edit'),
iconFilePath: IMAGE_ROOT + '/ico_menu_plus.svg',
programPath: '',
};
@@ -66,6 +68,10 @@ export const FavoriteWrapper = ({
const makeFavoriteItems = () => {
let rs = [];
for(let i=0;i<favoriteItems.length;i++){
const displayName = i18n.language === 'en' && favoriteItems[i]?.menuNameEng
? favoriteItems[i]?.menuNameEng
: favoriteItems[i]?.menuName;
rs.push(
<SwiperSlide key={ `favorite-slide-key-${i}` }>
<div
@@ -75,10 +81,10 @@ export const FavoriteWrapper = ({
<div className="swiper-icon coin-icon">
<img
src={ favoriteItems[i]?.iconFilePath || '' }
alt={ favoriteItems[i]?.menuName }
alt={ displayName }
/>
</div>
<span className="swiper-text">{ favoriteItems[i]?.menuName }</span>
<span className="swiper-text">{ displayName }</span>
</div>
</SwiperSlide>
);

View File

@@ -49,10 +49,10 @@ export const HomeNoticeList = () => {
return (
<>
<div className="notice-list">
<h3> & </h3>
<h3>{t('home.noticesAndUpdates')}</h3>
<div className="notice-box">
{ (!!resultList && resultList.length > 0) &&
getItems()
{ (!!resultList && resultList.length > 0) &&
getItems()
}
</div>
</div>