Implement multi-language support in user management and system management pages

- Integrated multi-language functionality across various user management components, including user list, roles list, and user authorization pages, enhancing accessibility for diverse users.
- Updated UI elements to utilize translation keys, ensuring that all text is dynamically translated based on user preferences.
- Improved error handling messages to be localized, providing a better user experience in case of issues.

These changes significantly enhance the usability and internationalization of the user management features, making the application more inclusive.
This commit is contained in:
kjs
2026-04-01 15:57:12 +09:00
parent 2ff01456dc
commit 1d49fc7ac7
15 changed files with 812 additions and 200 deletions

View File

@@ -10,6 +10,7 @@ interface UserToolbarProps {
isSearching?: boolean;
onSearchChange: (searchFilter: Partial<UserSearchFilter>) => void;
onCreateClick: () => void;
t?: (key: string, params?: Record<string, string | number>) => string;
}
/**
@@ -22,7 +23,32 @@ export function UserToolbar({
isSearching = false,
onSearchChange,
onCreateClick,
t: tProp,
}: UserToolbarProps) {
// 다국어 함수 (prop이 없으면 한국어 기본값 사용)
const _t = tProp || ((key: string) => {
const defaults: Record<string, string> = {
"toolbar.search.placeholder": "통합 검색...",
"toolbar.searching": "검색 중...",
"toolbar.advanced.search": "고급 검색",
"toolbar.advanced.search.title": "고급 검색 옵션",
"toolbar.advanced.search.desc": "각 필드별로 개별 검색 조건을 설정할 수 있습니다",
"toolbar.advanced.mode.warning":
"고급 검색 모드가 활성화되어 있습니다. 통합 검색을 사용하려면 고급 검색 조건을 초기화하세요.",
"toolbar.advanced.reset": "고급 검색 조건 초기화",
"toolbar.total.count": "총",
"toolbar.total.unit": "명",
"toolbar.create.user": "사용자 등록",
"toolbar.search.company": "회사명 검색",
"toolbar.search.dept": "부서명 검색",
"toolbar.search.position": "직책 검색",
"toolbar.search.userId": "사용자 ID 검색",
"toolbar.search.userName": "사용자명 검색",
"toolbar.search.tel": "전화번호/휴대폰 검색",
"toolbar.search.email": "이메일 검색",
};
return defaults[key] || key;
});
const [showAdvancedSearch, setShowAdvancedSearch] = useState(false);
// 통합 검색어 변경
@@ -77,7 +103,7 @@ export function UserToolbar({
}`}
/>
<Input
placeholder="통합 검색..."
placeholder={_t("toolbar.search.placeholder")}
value={searchFilter.searchValue || ""}
onChange={(e) => handleV2SearchChange(e.target.value)}
disabled={isAdvancedSearchMode}
@@ -87,10 +113,10 @@ export function UserToolbar({
} ${isAdvancedSearchMode ? "cursor-not-allowed bg-muted text-muted-foreground" : ""}`}
/>
</div>
{isSearching && <p className="mt-1.5 text-xs text-primary"> ...</p>}
{isSearching && <p className="mt-1.5 text-xs text-primary">{_t("toolbar.searching")}</p>}
{isAdvancedSearchMode && (
<p className="mt-1.5 text-xs text-warning">
. .
{_t("toolbar.advanced.mode.warning")}
</p>
)}
</div>
@@ -102,7 +128,7 @@ export function UserToolbar({
onClick={() => setShowAdvancedSearch(!showAdvancedSearch)}
className="h-10 gap-2 text-sm font-medium"
>
{_t("toolbar.advanced.search")}
{showAdvancedSearch ? <ChevronUp className="h-4 w-4" /> : <ChevronDown className="h-4 w-4" />}
</Button>
</div>
@@ -111,13 +137,13 @@ export function UserToolbar({
<div className="flex items-center gap-4">
{/* 조회 결과 정보 */}
<div className="text-sm text-muted-foreground">
<span className="font-semibold text-foreground">{totalCount.toLocaleString()}</span>
{_t("toolbar.total.count")} <span className="font-semibold text-foreground">{totalCount.toLocaleString()}</span> {_t("toolbar.total.unit")}
</div>
{/* 사용자 등록 버튼 */}
<Button onClick={onCreateClick} size="default" className="h-10 gap-2 text-sm font-medium">
<Plus className="h-4 w-4" />
{_t("toolbar.create.user")}
</Button>
</div>
</div>
@@ -126,56 +152,56 @@ export function UserToolbar({
{showAdvancedSearch && (
<div className="space-y-4">
<div className="space-y-1">
<h4 className="text-sm font-semibold"> </h4>
<p className="text-xs text-muted-foreground"> </p>
<h4 className="text-sm font-semibold">{_t("toolbar.advanced.search.title")}</h4>
<p className="text-xs text-muted-foreground">{_t("toolbar.advanced.search.desc")}</p>
</div>
{/* 고급 검색 필드들 */}
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
<Input
placeholder="회사명 검색"
placeholder={_t("toolbar.search.company")}
value={searchFilter.search_companyName || ""}
onChange={(e) => handleAdvancedSearchChange("search_companyName", e.target.value)}
className="h-10 text-sm"
/>
<Input
placeholder="부서명 검색"
placeholder={_t("toolbar.search.dept")}
value={searchFilter.search_deptName || ""}
onChange={(e) => handleAdvancedSearchChange("search_deptName", e.target.value)}
className="h-10 text-sm"
/>
<Input
placeholder="직책 검색"
placeholder={_t("toolbar.search.position")}
value={searchFilter.search_positionName || ""}
onChange={(e) => handleAdvancedSearchChange("search_positionName", e.target.value)}
className="h-10 text-sm"
/>
<Input
placeholder="사용자 ID 검색"
placeholder={_t("toolbar.search.userId")}
value={searchFilter.search_userId || ""}
onChange={(e) => handleAdvancedSearchChange("search_userId", e.target.value)}
className="h-10 text-sm"
/>
<Input
placeholder="사용자명 검색"
placeholder={_t("toolbar.search.userName")}
value={searchFilter.search_userName || ""}
onChange={(e) => handleAdvancedSearchChange("search_userName", e.target.value)}
className="h-10 text-sm"
/>
<Input
placeholder="전화번호/휴대폰 검색"
placeholder={_t("toolbar.search.tel")}
value={searchFilter.search_tel || ""}
onChange={(e) => handleAdvancedSearchChange("search_tel", e.target.value)}
className="h-10 text-sm"
/>
<Input
placeholder="이메일 검색"
placeholder={_t("toolbar.search.email")}
value={searchFilter.search_email || ""}
onChange={(e) => handleAdvancedSearchChange("search_email", e.target.value)}
className="h-10 text-sm"
@@ -202,7 +228,7 @@ export function UserToolbar({
}
className="h-9 text-sm text-muted-foreground hover:text-foreground"
>
{_t("toolbar.advanced.reset")}
</Button>
</div>
)}