1
0
forked from baron/baron-sso

devfront 설정 화면 로케일 누락 수정

This commit is contained in:
2026-06-16 13:54:48 +09:00
parent 3819a29ed8
commit 66556c9f03
10 changed files with 241 additions and 71 deletions

View File

@@ -1178,14 +1178,14 @@ function ClientGeneralPage() {
if (!trimmedJwksUri) { if (!trimmedJwksUri) {
validationErrors.push( validationErrors.push(
t( t(
"msg.dev.clients.general.public_key.validation.missing_jwks_uri", "ui.dev.clients.general.public_key.validation.missing_jwks_uri",
"JWKS URI를 입력해야 합니다.", "JWKS URI를 입력해야 합니다.",
), ),
); );
} else if (!isValidUrl(trimmedJwksUri)) { } else if (!isValidUrl(trimmedJwksUri)) {
validationErrors.push( validationErrors.push(
t( t(
"msg.dev.clients.general.public_key.validation.invalid_jwks_uri", "ui.dev.clients.general.public_key.validation.invalid_jwks_uri",
"JWKS URI 형식이 올바르지 않습니다.", "JWKS URI 형식이 올바르지 않습니다.",
), ),
); );
@@ -1193,7 +1193,7 @@ function ClientGeneralPage() {
if (unsupportedParsedAlgorithms.length > 0) { if (unsupportedParsedAlgorithms.length > 0) {
validationErrors.push( validationErrors.push(
t( t(
"msg.dev.clients.general.public_key.validation.unsupported_parsed_algorithms", "ui.dev.clients.general.public_key.validation.unsupported_parsed_algorithms",
"JWKS에 지원하지 않는 알고리즘이 있습니다: {{details}}", "JWKS에 지원하지 않는 알고리즘이 있습니다: {{details}}",
{ details: unsupportedParsedAlgorithmSummary }, { details: unsupportedParsedAlgorithmSummary },
), ),
@@ -1202,7 +1202,7 @@ function ClientGeneralPage() {
if (missingParsedAlgorithms.length > 0) { if (missingParsedAlgorithms.length > 0) {
validationErrors.push( validationErrors.push(
t( t(
"msg.dev.clients.general.public_key.validation.missing_parsed_algorithms", "ui.dev.clients.general.public_key.validation.missing_parsed_algorithms",
"JWKS에 알고리즘(`alg`)이 선언되지 않은 키가 있습니다: {{details}}", "JWKS에 알고리즘(`alg`)이 선언되지 않은 키가 있습니다: {{details}}",
{ details: missingParsedAlgorithmSummary }, { details: missingParsedAlgorithmSummary },
), ),
@@ -2050,13 +2050,13 @@ function ClientGeneralPage() {
<p className="text-sm font-semibold"> <p className="text-sm font-semibold">
{t( {t(
"ui.dev.clients.general.scopes.picker_title", "ui.dev.clients.general.scopes.picker_title",
"추가할 scope 선택", "Add a scope",
)} )}
</p> </p>
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
{t( {t(
"msg.dev.clients.general.scopes.picker_help", "ui.dev.clients.general.scopes.picker_help",
"지원 scope와 Custom Claim key를 선택해 scope 목록에 추가합니다.", "Choose a supported scope or custom claim key to add it to the scope list.",
)} )}
</p> </p>
</div> </div>
@@ -2427,13 +2427,13 @@ function ClientGeneralPage() {
{tenantAccessRestricted ? ( {tenantAccessRestricted ? (
<div className="grid gap-4 lg:grid-cols-[0.8fr_1.2fr]"> <div className="grid gap-4 lg:grid-cols-[0.8fr_1.2fr]">
<div className="space-y-3"> <div className="space-y-3">
<Label className="text-sm font-semibold"> <Label className="text-sm font-semibold">
{t( {t(
"ui.dev.clients.general.tenant_access.picker_label", "ui.dev.clients.general.tenant_access.picker_label",
"허용 테넌트 추가", "Add allowed tenant",
)}{" "} )}{" "}
<span className="text-destructive">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<TenantAccessPicker <TenantAccessPicker
disabled={isGeneralSettingsReadOnly} disabled={isGeneralSettingsReadOnly}
selectedCount={allowedTenantIds.length} selectedCount={allowedTenantIds.length}
@@ -3069,8 +3069,8 @@ function ClientGeneralPage() {
</Label> </Label>
<p className="text-[10px] text-muted-foreground"> <p className="text-[10px] text-muted-foreground">
{t( {t(
"msg.dev.clients.general.security.headless_login_enable_help", "ui.dev.clients.general.security.headless_login_enable_help",
"Baron SSO 로그인 창 대신 RP 자체 로그인 UI를 사용하고, RP backend의 서명 키로 클라이언트를 검증하려는 경우 활성화합니다.", "Enable this when the RP uses its own login UI instead of the Baron SSO login page and the RP backend validates the client with a signing key.",
)} )}
</p> </p>
</div> </div>

View File

@@ -57,7 +57,7 @@ export function TenantAccessPicker({
aria-modal="true" aria-modal="true"
aria-label={t( aria-label={t(
"ui.dev.clients.general.tenant_access.picker_title", "ui.dev.clients.general.tenant_access.picker_title",
"테넌트 선택", "Select tenant",
)} )}
> >
<div className="flex h-[92vh] w-[min(96vw,1200px)] flex-col overflow-hidden rounded-2xl border border-border bg-background p-4 shadow-2xl"> <div className="flex h-[92vh] w-[min(96vw,1200px)] flex-col overflow-hidden rounded-2xl border border-border bg-background p-4 shadow-2xl">
@@ -66,13 +66,13 @@ export function TenantAccessPicker({
<h2 className="text-lg font-semibold"> <h2 className="text-lg font-semibold">
{t( {t(
"ui.dev.clients.general.tenant_access.picker_title", "ui.dev.clients.general.tenant_access.picker_title",
"테넌트 선택", "Select tenant",
)} )}
</h2> </h2>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
{t( {t(
"msg.dev.clients.general.tenant_access.picker_description", "ui.dev.clients.general.tenant_access.picker_description",
"orgfront 조직도에서 허용할 테넌트를 선택하면 목록에 추가됩니다.", "Choose the tenants to allow from the orgfront org chart and add them to the list.",
)} )}
</p> </p>
</div> </div>
@@ -83,7 +83,7 @@ export function TenantAccessPicker({
className="shrink-0" className="shrink-0"
onClick={() => setPickerOpen(false)} onClick={() => setPickerOpen(false)}
> >
{t("ui.common.close", "닫기")} {t("ui.common.close", "Close")}
</Button> </Button>
</div> </div>
<div className="mt-4 min-h-0 flex-1 overflow-hidden rounded-md border"> <div className="mt-4 min-h-0 flex-1 overflow-hidden rounded-md border">
@@ -102,7 +102,7 @@ export function TenantAccessPicker({
variant="outline" variant="outline"
onClick={() => setPickerOpen(false)} onClick={() => setPickerOpen(false)}
> >
{t("ui.common.close", "닫기")} {t("ui.common.close", "Close")}
</Button> </Button>
</div> </div>
</div> </div>
@@ -123,7 +123,7 @@ export function TenantAccessPicker({
<Building2 className="h-4 w-4" /> <Building2 className="h-4 w-4" />
{t( {t(
"ui.dev.clients.general.tenant_access.open_picker", "ui.dev.clients.general.tenant_access.open_picker",
"테넌트 선택기 열기", "Open tenant picker",
)} )}
</Button> </Button>
@@ -132,13 +132,13 @@ export function TenantAccessPicker({
<div className="rounded-xl border border-dashed border-border bg-muted/20 px-4 py-3 text-sm text-muted-foreground"> <div className="rounded-xl border border-dashed border-border bg-muted/20 px-4 py-3 text-sm text-muted-foreground">
{selectedCount > 0 {selectedCount > 0
? t( ? t(
"msg.dev.clients.general.tenant_access.picker_hint_with_count", "ui.dev.clients.general.tenant_access.picker_hint_with_count",
"현재 {{count}}개가 선택되어 있습니다.", "{{count}} tenants selected.",
{ count: selectedCount }, { count: selectedCount },
) )
: t( : t(
"msg.dev.clients.general.tenant_access.picker_hint", "ui.dev.clients.general.tenant_access.picker_hint",
"선택기를 열어 허용 테넌트를 추가하세요.", "Open the picker to add allowed tenants.",
)} )}
</div> </div>
</div> </div>

View File

@@ -1,5 +1,18 @@
import { describe, expect, it } from "vitest"; import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { normalizeDeveloperAccessPageSelection } from "./developerAccessPages"; import {
developerAccessPagesToLabel,
getDeveloperAccessPageLabel,
normalizeDeveloperAccessPageSelection,
} from "./developerAccessPages";
beforeEach(() => {
window.localStorage.clear();
window.localStorage.setItem("locale", "ko");
});
afterEach(() => {
window.localStorage.clear();
});
describe("developer access pages", () => { describe("developer access pages", () => {
it("collapses all non-all pages into all", () => { it("collapses all non-all pages into all", () => {
@@ -21,4 +34,20 @@ describe("developer access pages", () => {
it("keeps explicit all selection", () => { it("keeps explicit all selection", () => {
expect(normalizeDeveloperAccessPageSelection(["all"])).toEqual(["all"]); expect(normalizeDeveloperAccessPageSelection(["all"])).toEqual(["all"]);
}); });
it("returns localized labels for access pages", () => {
expect(getDeveloperAccessPageLabel("all")).toBe("전체");
expect(developerAccessPagesToLabel(["overview", "audit"])).toBe(
"개요, 감사로그",
);
window.localStorage.setItem("locale", "en");
expect(getDeveloperAccessPageLabel("client_create")).toBe(
"Add linked app",
);
expect(developerAccessPagesToLabel(["overview", "audit"])).toBe(
"Overview, Audit Logs",
);
});
}); });

View File

@@ -1,3 +1,5 @@
import { t } from "../../lib/i18n";
export type DeveloperAccessPage = export type DeveloperAccessPage =
| "all" | "all"
| "overview" | "overview"
@@ -10,15 +12,40 @@ export const developerAccessPageOrder: DeveloperAccessPage[] = [
"audit", "audit",
]; ];
export const developerAccessPageOptions: Array<{ export function getDeveloperAccessPageLabel(page: DeveloperAccessPage): string {
switch (page) {
case "all":
return t("ui.dev.access_pages.all", "전체");
case "overview":
return t("ui.dev.access_pages.overview", "개요");
case "client_create":
return t("ui.dev.access_pages.client_create", "연동 앱 추가");
case "audit":
return t("ui.dev.access_pages.audit", "감사로그");
default:
return page;
}
}
export function getDeveloperAccessPageOptions(): Array<{
value: DeveloperAccessPage; value: DeveloperAccessPage;
label: string; label: string;
}> = [ }> {
{ value: "all", label: "전체" }, return developerAccessPageOrder.length > 0
{ value: "overview", label: "개요" }, ? [
{ value: "client_create", label: "연동 앱 추가" }, { value: "all", label: getDeveloperAccessPageLabel("all") },
{ value: "audit", label: "감사로그" }, {
]; value: "overview",
label: getDeveloperAccessPageLabel("overview"),
},
{
value: "client_create",
label: getDeveloperAccessPageLabel("client_create"),
},
{ value: "audit", label: getDeveloperAccessPageLabel("audit") },
]
: [];
}
export function normalizeDeveloperAccessPages( export function normalizeDeveloperAccessPages(
pages: Array<string | undefined | null>, pages: Array<string | undefined | null>,
@@ -61,20 +88,11 @@ export function normalizeDeveloperAccessPageSelection(
export function developerAccessPagesToLabel(pages?: Array<string | null>) { export function developerAccessPagesToLabel(pages?: Array<string | null>) {
const normalized = normalizeDeveloperAccessPages(pages ?? []); const normalized = normalizeDeveloperAccessPages(pages ?? []);
if (normalized.length === 0 || normalized.includes("all")) { if (normalized.length === 0 || normalized.includes("all")) {
return "전체"; return getDeveloperAccessPageLabel("all");
} }
return normalized return normalized
.map((page) => { .map((page) => {
switch (page) { return getDeveloperAccessPageLabel(page);
case "overview":
return "개요";
case "client_create":
return "연동 앱 추가";
case "audit":
return "감사로그";
default:
return page;
}
}) })
.join(", "); .join(", ");
} }

View File

@@ -38,7 +38,8 @@ import { resolveProfileRole } from "../../lib/role";
import { fetchMe } from "../auth/authApi"; import { fetchMe } from "../auth/authApi";
import { import {
type DeveloperAccessPage, type DeveloperAccessPage,
developerAccessPageOptions, developerAccessPagesToLabel,
getDeveloperAccessPageOptions,
normalizeDeveloperAccessPageSelection, normalizeDeveloperAccessPageSelection,
normalizeDeveloperAccessPages, normalizeDeveloperAccessPages,
} from "../developer-access/developerAccessPages"; } from "../developer-access/developerAccessPages";
@@ -62,6 +63,7 @@ export default function DeveloperGrantsPage() {
}); });
const profileRole = me?.role?.trim() || role; const profileRole = me?.role?.trim() || role;
const isSuperAdmin = profileRole === "super_admin"; const isSuperAdmin = profileRole === "super_admin";
const developerAccessPageOptions = getDeveloperAccessPageOptions();
const [userSearch, setUserSearch] = useState(""); const [userSearch, setUserSearch] = useState("");
const deferredUserSearch = useDeferredValue(userSearch.trim()); const deferredUserSearch = useDeferredValue(userSearch.trim());
@@ -621,9 +623,7 @@ export default function DeveloperGrantsPage() {
: ["all"] : ["all"]
).map((page) => ( ).map((page) => (
<Badge key={page} variant="outline"> <Badge key={page} variant="outline">
{developerAccessPageOptions.find( {developerAccessPagesToLabel([page])}
(option) => option.value === page,
)?.label ?? page}
</Badge> </Badge>
))} ))}
</div> </div>

View File

@@ -49,7 +49,8 @@ import { resolveProfileRole } from "../../lib/role";
import { fetchMe } from "../auth/authApi"; import { fetchMe } from "../auth/authApi";
import { import {
type DeveloperAccessPage, type DeveloperAccessPage,
developerAccessPageOptions, developerAccessPagesToLabel,
getDeveloperAccessPageOptions,
normalizeDeveloperAccessPageSelection, normalizeDeveloperAccessPageSelection,
normalizeDeveloperAccessPages, normalizeDeveloperAccessPages,
} from "../developer-access/developerAccessPages"; } from "../developer-access/developerAccessPages";
@@ -287,9 +288,7 @@ export default function DeveloperRequestPage() {
req.accessPages, req.accessPages,
).map((page) => ( ).map((page) => (
<Badge key={page} variant="outline"> <Badge key={page} variant="outline">
{developerAccessPageOptions.find( {developerAccessPagesToLabel([page])}
(option) => option.value === page,
)?.label ?? page}
</Badge> </Badge>
)) ))
) : ( ) : (
@@ -479,6 +478,7 @@ function RequestAccessModal({
const [accessPages, setAccessPages] = useState<DeveloperAccessPage[]>([ const [accessPages, setAccessPages] = useState<DeveloperAccessPage[]>([
"all", "all",
]); ]);
const developerAccessPageOptions = getDeveloperAccessPageOptions();
const organizationDisplay = organization.trim() || t("ui.common.na", "없음"); const organizationDisplay = organization.trim() || t("ui.common.na", "없음");
useEffect(() => { useEffect(() => {

View File

@@ -0,0 +1,29 @@
import { afterEach, describe, expect, it } from "vitest";
import { t } from "./i18n";
afterEach(() => {
window.localStorage.clear();
});
describe("i18n", () => {
it("returns English copy for the developer request and grants screens", () => {
window.localStorage.setItem("locale", "en");
expect(t("ui.dev.request.list.title", "신청 내역")).toBe("Request History");
expect(t("msg.dev.request.list.approved_count", "총 {{count}}명의 사용자가 승인되었습니다.", { count: 0 })).toBe(
"0 users have been approved.",
);
expect(t("ui.dev.grants.form.title", "직접 부여")).toBe("Direct Grant");
expect(
t(
"msg.dev.grants.form.description",
"사용자를 선택하면 현재 소속 정보가 표시되고, 그 사용자에게 개발자 권한을 즉시 부여합니다.",
),
).toBe(
"Select a user to view their current tenant, email, and phone, then grant developer access immediately.",
);
expect(
t("msg.dev.grants.list.description", "현재 부여된 개발자 권한 목록입니다."),
).toBe("Current developer access grants.");
});
});

View File

@@ -321,12 +321,14 @@ admin_desc = "Manage developer access requests submitted by users."
approved = "Approved." approved = "Approved."
cancelled = "Approval has been cancelled." cancelled = "Approval has been cancelled."
empty = "No requests found." empty = "No requests found."
list.approved_count = "{{count}} users have been approved."
need_cancel_notes = "Please enter a reason for cancelling approval." need_cancel_notes = "Please enter a reason for cancelling approval."
need_notes = "Please enter a rejection reason." need_notes = "Please enter a rejection reason."
rejected = "Rejected." rejected = "Rejected."
user_desc = "Review your request history and submit a new access request." user_desc = "Review your request history and submit a new access request."
[msg.dev.request.list]
approved_count = "{{count}} users have been approved."
[msg.dev.request.modal] [msg.dev.request.modal]
desc = "Please enter the reason for your request. It will be approved after administrator review." desc = "Please enter the reason for your request. It will be approved after administrator review."
tenant_required = "Please submit a developer access request." tenant_required = "Please submit a developer access request."
@@ -573,10 +575,8 @@ admin_notes_placeholder = "e.g. Grant access after verifying the test environmen
empty = "There are no granted permissions." empty = "There are no granted permissions."
forbidden = "Only super admin can directly grant developer access." forbidden = "Only super admin can directly grant developer access."
forbidden_desc = "This screen is available only to super admin." forbidden_desc = "This screen is available only to super admin."
form.description = "Select a user to view their current tenant, email, and phone, then grant developer access immediately."
selected_info_description = "Review the selected user's tenant, email, and phone." selected_info_description = "Review the selected user's tenant, email, and phone."
user_section_description = "Enter a search term to select a user. The next-step information stays empty until a user is chosen." user_section_description = "Enter a search term to select a user. The next-step information stays empty until a user is chosen."
list.description = "Current developer access grants."
load_error = "Failed to load developer access grants." load_error = "Failed to load developer access grants."
reason = "Grant reason" reason = "Grant reason"
revoke = "Revoke" revoke = "Revoke"
@@ -588,6 +588,13 @@ tenant_required = "The selected user's tenant information is unavailable."
tenant_missing = "No tenant information is available for the selected user." tenant_missing = "No tenant information is available for the selected user."
user_required = "Select a user before granting access." user_required = "Select a user before granting access."
phone_missing = "No phone number is registered." phone_missing = "No phone number is registered."
pages_hint = "If you select All, Overview, Add linked app, and Audit Logs are all included."
[msg.dev.grants.form]
description = "Select a user to view their current tenant, email, and phone, then grant developer access immediately."
[msg.dev.grants.list]
description = "Current developer access grants."
[msg.dev.dashboard.notice] [msg.dev.dashboard.notice]
consent_audit = "Consent Audit" consent_audit = "Consent Audit"
@@ -1351,10 +1358,8 @@ admin_notes = "Grant Reason"
all_tenants = "All Tenants" all_tenants = "All Tenants"
approved = "Approved" approved = "Approved"
date = "Granted At" date = "Granted At"
form.title = "Direct Grant"
grant = "Grant Directly" grant = "Grant Directly"
input_section = "Input" input_section = "Input"
list.title = "Granted Access"
pages = "Access Pages" pages = "Access Pages"
read_only = "Read Only" read_only = "Read Only"
reason = "Grant Reason" reason = "Grant Reason"
@@ -1369,6 +1374,12 @@ user_section = "User Selection"
user = "User" user = "User"
user_search_placeholder = "Search by name or email..." user_search_placeholder = "Search by name or email..."
[ui.dev.grants.form]
title = "Direct Grant"
[ui.dev.grants.list]
title = "Granted Access"
[ui.dev.request.modal] [ui.dev.request.modal]
email = "Email" email = "Email"
name = "Name" name = "Name"
@@ -1380,6 +1391,12 @@ reason_placeholder = "e.g. I need to create an OIDC client for internal service
role = "Role" role = "Role"
title = "Developer Access Request" title = "Developer Access Request"
[ui.dev.access_pages]
all = "All"
overview = "Overview"
client_create = "Add linked app"
audit = "Audit Logs"
[ui.dev.request.status] [ui.dev.request.status]
approved = "Approved" approved = "Approved"
cancelled = "Approval Cancelled" cancelled = "Approval Cancelled"
@@ -1597,6 +1614,8 @@ name_placeholder = "e.g. profile"
title = "Scopes" title = "Scopes"
offline_access_title = "offline_access scope is required when using refresh tokens." offline_access_title = "offline_access scope is required when using refresh tokens."
offline_access_toggle = "Show details" offline_access_toggle = "Show details"
picker_title = "Select a scope to add"
picker_help = "Choose a supported scope or custom claim key to add it to the scope list."
[ui.dev.clients.general.scopes.table] [ui.dev.clients.general.scopes.table]
description = "Scope Description" description = "Scope Description"
@@ -1617,6 +1636,12 @@ empty = "No tenants match your search."
hint = "Turning this on adds the tenant scope automatically and requires at least one allowed tenant." hint = "Turning this on adds the tenant scope automatically and requires at least one allowed tenant."
autocomplete_hint = "Type a tenant name to see autocomplete suggestions. Click one to add it to the allowed list." autocomplete_hint = "Type a tenant name to see autocomplete suggestions. Click one to add it to the allowed list."
validation_required = "Select at least one allowed tenant when tenant access restriction is enabled." validation_required = "Select at least one allowed tenant when tenant access restriction is enabled."
picker_title = "Select tenant"
picker_label = "Add allowed tenant"
open_picker = "Open tenant picker"
picker_description = "Choose the tenants to allow from the orgfront org chart and add them to the list."
picker_hint = "Open the picker to add allowed tenants."
picker_hint_with_count = "{{count}} tenants selected."
[ui.dev.clients.general.id_token_claims] [ui.dev.clients.general.id_token_claims]
title = "Custom Claims" title = "Custom Claims"
@@ -1652,7 +1677,7 @@ pkce = "PKCE"
headless_login = "Headless Login" headless_login = "Headless Login"
title = "Security Settings" title = "Security Settings"
headless_login_enable = "Headless Login (Custom Login UI)" headless_login_enable = "Headless Login (Custom Login UI)"
headless_login_enable_help = "Enable this when the RP uses its own login UI and the RP backend proves the client with signed keys instead of the Baron SSO login page." headless_login_enable_help = "Enable this when the RP uses its own login UI instead of the Baron SSO login page and the RP backend validates the client with a signing key."
[ui.dev.clients.general.public_key] [ui.dev.clients.general.public_key]
auth_method = "Token Endpoint Auth Method" auth_method = "Token Endpoint Auth Method"
@@ -1683,6 +1708,12 @@ cache_status = "Status"
cache_uri = "JWKS URI" cache_uri = "JWKS URI"
revoke_cache = "Revoke Cache" revoke_cache = "Revoke Cache"
[ui.dev.clients.general.public_key.validation]
missing_jwks_uri = "Enter a JWKS URI."
invalid_jwks_uri = "JWKS URI format is invalid."
unsupported_parsed_algorithms = "The JWKS contains unsupported algorithms: {{details}}"
missing_parsed_algorithms = "The JWKS contains keys without an `alg` declaration: {{details}}"
[ui.dev.clients.relationships] [ui.dev.clients.relationships]
title = "Client Relationships" title = "Client Relationships"
add_title = "Add Relationship" add_title = "Add Relationship"

View File

@@ -321,12 +321,14 @@ admin_desc = "사용자들의 개발자 권한 신청 내역을 관리합니다.
approved = "승인되었습니다." approved = "승인되었습니다."
cancelled = "승인이 취소되었습니다." cancelled = "승인이 취소되었습니다."
empty = "신청 내역이 없습니다." empty = "신청 내역이 없습니다."
list.approved_count = "총 {{count}}명의 사용자가 승인되었습니다."
need_cancel_notes = "승인 취소 사유를 입력해주세요." need_cancel_notes = "승인 취소 사유를 입력해주세요."
need_notes = "반려 사유를 입력해주세요." need_notes = "반려 사유를 입력해주세요."
rejected = "반려되었습니다." rejected = "반려되었습니다."
user_desc = "내 신청 내역을 확인하고 새로운 권한을 신청할 수 있습니다." user_desc = "내 신청 내역을 확인하고 새로운 권한을 신청할 수 있습니다."
[msg.dev.request.list]
approved_count = "총 {{count}}명의 사용자가 승인되었습니다."
[msg.dev.request.modal] [msg.dev.request.modal]
desc = "신청 사유를 입력해 주세요. 관리자 확인 후 승인됩니다." desc = "신청 사유를 입력해 주세요. 관리자 확인 후 승인됩니다."
tenant_required = "개발자 권한 신청을 진행해 주세요." tenant_required = "개발자 권한 신청을 진행해 주세요."
@@ -573,10 +575,8 @@ admin_notes_placeholder = "예: 테스트 환경 확인 후 권한 부여"
empty = "부여된 권한이 없습니다." empty = "부여된 권한이 없습니다."
forbidden = "개발자 권한 직접 부여는 super admin만 사용할 수 있습니다." forbidden = "개발자 권한 직접 부여는 super admin만 사용할 수 있습니다."
forbidden_desc = "이 화면은 super admin만 사용할 수 있습니다." forbidden_desc = "이 화면은 super admin만 사용할 수 있습니다."
form.description = "사용자를 선택하면 현재 소속 테넌트, 이메일, 전화번호를 확인한 뒤 개발자 권한을 즉시 부여합니다."
selected_info_description = "선택된 사용자의 소속, 이메일, 전화번호를 확인합니다." selected_info_description = "선택된 사용자의 소속, 이메일, 전화번호를 확인합니다."
user_section_description = "검색어를 입력해 사용자를 선택합니다. 선택 전에는 다음 단계 정보가 비어 있습니다." user_section_description = "검색어를 입력해 사용자를 선택합니다. 선택 전에는 다음 단계 정보가 비어 있습니다."
list.description = "현재 부여된 개발자 권한 목록입니다."
load_error = "개발자 권한 목록을 불러오지 못했습니다." load_error = "개발자 권한 목록을 불러오지 못했습니다."
reason = "부여 사유" reason = "부여 사유"
revoke = "회수" revoke = "회수"
@@ -588,6 +588,13 @@ tenant_required = "선택한 사용자의 테넌트 정보를 확인할 수 없
tenant_missing = "선택한 사용자의 테넌트 정보를 확인할 수 없습니다." tenant_missing = "선택한 사용자의 테넌트 정보를 확인할 수 없습니다."
user_required = "부여할 사용자를 선택해주세요." user_required = "부여할 사용자를 선택해주세요."
phone_missing = "등록된 전화번호가 없습니다." phone_missing = "등록된 전화번호가 없습니다."
pages_hint = "전체를 선택하면 개요, 연동 앱 추가, 감사로그가 모두 포함됩니다."
[msg.dev.grants.form]
description = "사용자를 선택하면 현재 소속 테넌트, 이메일, 전화번호를 확인한 뒤 개발자 권한을 즉시 부여합니다."
[msg.dev.grants.list]
description = "현재 부여된 개발자 권한 목록입니다."
[msg.dev.dashboard.notice] [msg.dev.dashboard.notice]
consent_audit = "Consent 회수는 감사 로그와 연계" consent_audit = "Consent 회수는 감사 로그와 연계"
@@ -1351,10 +1358,8 @@ admin_notes = "부여 사유"
all_tenants = "전체 테넌트" all_tenants = "전체 테넌트"
approved = "승인됨" approved = "승인됨"
date = "부여일" date = "부여일"
form.title = "직접 부여"
grant = "직접 부여" grant = "직접 부여"
input_section = "입력" input_section = "입력"
list.title = "부여된 권한"
pages = "권한 페이지" pages = "권한 페이지"
read_only = "읽기 전용" read_only = "읽기 전용"
reason = "부여 사유" reason = "부여 사유"
@@ -1369,6 +1374,12 @@ user_section = "사용자 선택"
user = "사용자" user = "사용자"
user_search_placeholder = "이름 또는 이메일 검색..." user_search_placeholder = "이름 또는 이메일 검색..."
[ui.dev.grants.form]
title = "직접 부여"
[ui.dev.grants.list]
title = "부여된 권한"
[ui.dev.request.modal] [ui.dev.request.modal]
email = "이메일" email = "이메일"
name = "성함" name = "성함"
@@ -1380,6 +1391,12 @@ reason_placeholder = "예: 자체 서비스 연동 및 테스트용 OIDC 클라
role = "역할" role = "역할"
title = "개발자 등록 신청" title = "개발자 등록 신청"
[ui.dev.access_pages]
all = "전체"
overview = "개요"
client_create = "연동 앱 추가"
audit = "감사로그"
[ui.dev.request.status] [ui.dev.request.status]
approved = "승인됨" approved = "승인됨"
cancelled = "승인 취소됨" cancelled = "승인 취소됨"
@@ -1596,6 +1613,8 @@ name_placeholder = "e.g. profile"
title = "스코프" title = "스코프"
offline_access_title = "Refresh token 사용 시 offline_access scope가 필요합니다." offline_access_title = "Refresh token 사용 시 offline_access scope가 필요합니다."
offline_access_toggle = "상세 안내 보기" offline_access_toggle = "상세 안내 보기"
picker_title = "추가할 scope 선택"
picker_help = "지원 scope와 Custom Claim key를 선택해 scope 목록에 추가합니다."
[ui.dev.clients.general.scopes.table] [ui.dev.clients.general.scopes.table]
description = "설명" description = "설명"
@@ -1616,6 +1635,12 @@ empty = "검색 결과가 없습니다."
hint = "제한을 켜면 tenant 스코프가 자동으로 포함되며, 허용 테넌트를 하나 이상 선택해야 합니다." hint = "제한을 켜면 tenant 스코프가 자동으로 포함되며, 허용 테넌트를 하나 이상 선택해야 합니다."
autocomplete_hint = "테넌트 이름을 입력하면 자동 완성 후보가 나타납니다. 클릭하면 허용 목록에 추가됩니다." autocomplete_hint = "테넌트 이름을 입력하면 자동 완성 후보가 나타납니다. 클릭하면 허용 목록에 추가됩니다."
validation_required = "테넌트 접근 제한을 사용할 경우 허용 테넌트를 하나 이상 선택해야 합니다." validation_required = "테넌트 접근 제한을 사용할 경우 허용 테넌트를 하나 이상 선택해야 합니다."
picker_title = "테넌트 선택"
picker_label = "허용 테넌트 추가"
open_picker = "테넌트 선택기 열기"
picker_description = "orgfront 조직도에서 허용할 테넌트를 선택하면 목록에 추가됩니다."
picker_hint = "선택기를 열어 허용 테넌트를 추가하세요."
picker_hint_with_count = "현재 {{count}}개가 선택되어 있습니다."
[ui.dev.clients.general.id_token_claims] [ui.dev.clients.general.id_token_claims]
title = "커스텀 클레임" title = "커스텀 클레임"
@@ -1682,6 +1707,12 @@ cache_status = "상태"
cache_uri = "JWKS URI" cache_uri = "JWKS URI"
revoke_cache = "캐시 삭제" revoke_cache = "캐시 삭제"
[ui.dev.clients.general.public_key.validation]
missing_jwks_uri = "JWKS URI를 입력해야 합니다."
invalid_jwks_uri = "JWKS URI 형식이 올바르지 않습니다."
unsupported_parsed_algorithms = "JWKS에 지원하지 않는 알고리즘이 있습니다: {{details}}"
missing_parsed_algorithms = "JWKS에 알고리즘(`alg`)이 선언되지 않은 키가 있습니다: {{details}}"
[ui.dev.clients.relationships] [ui.dev.clients.relationships]
title = "클라이언트 관계" title = "클라이언트 관계"
add_title = "관계 추가" add_title = "관계 추가"

View File

@@ -335,16 +335,19 @@ admin_desc = ""
approved = "" approved = ""
cancelled = "" cancelled = ""
empty = "" empty = ""
list.approved_count = ""
need_cancel_notes = "" need_cancel_notes = ""
need_notes = "" need_notes = ""
rejected = "" rejected = ""
user_desc = "" user_desc = ""
[msg.dev.request.list]
approved_count = ""
[msg.dev.request.modal] [msg.dev.request.modal]
desc = "" desc = ""
tenant_required = "" tenant_required = ""
tenant_required_detail = "" tenant_required_detail = ""
pages_hint = ""
[msg.dev.request.status] [msg.dev.request.status]
approved = "" approved = ""
@@ -610,14 +613,13 @@ admin_notes_placeholder = ""
empty = "" empty = ""
forbidden = "" forbidden = ""
forbidden_desc = "" forbidden_desc = ""
form.description = ""
selected_info_description = "" selected_info_description = ""
user_section_description = "" user_section_description = ""
list.description = ""
load_error = "" load_error = ""
reason = "" reason = ""
revoke = "" revoke = ""
revoke_success = "" revoke_success = ""
pages_hint = ""
search_empty = "" search_empty = ""
search_loading = "" search_loading = ""
selected_user = "" selected_user = ""
@@ -627,6 +629,12 @@ user_required = ""
phone_missing = "" phone_missing = ""
required = "" required = ""
[msg.dev.grants.form]
description = ""
[msg.dev.grants.list]
description = ""
[msg.dev.dashboard.notice] [msg.dev.dashboard.notice]
consent_audit = "" consent_audit = ""
dev_scope = "" dev_scope = ""
@@ -1403,10 +1411,8 @@ admin_notes = ""
all_tenants = "" all_tenants = ""
approved = "" approved = ""
date = "" date = ""
form.title = ""
grant = "" grant = ""
input_section = "" input_section = ""
list.title = ""
read_only = "" read_only = ""
reason = "" reason = ""
reason_placeholder = "" reason_placeholder = ""
@@ -1420,6 +1426,12 @@ user_section = ""
user = "" user = ""
user_search_placeholder = "" user_search_placeholder = ""
[ui.dev.grants.form]
title = ""
[ui.dev.grants.list]
title = ""
[ui.dev.request.modal] [ui.dev.request.modal]
email = "" email = ""
name = "" name = ""
@@ -1430,6 +1442,12 @@ reason_placeholder = ""
role = "" role = ""
title = "" title = ""
[ui.dev.access_pages]
all = ""
overview = ""
client_create = ""
audit = ""
[ui.dev.request.status] [ui.dev.request.status]
approved = "" approved = ""
cancelled = "" cancelled = ""
@@ -1645,6 +1663,8 @@ name_placeholder = ""
title = "" title = ""
offline_access_title = "" offline_access_title = ""
offline_access_toggle = "" offline_access_toggle = ""
picker_title = ""
picker_help = ""
[ui.dev.clients.general.scopes.table] [ui.dev.clients.general.scopes.table]
description = "" description = ""
@@ -1665,6 +1685,12 @@ empty = ""
hint = "" hint = ""
autocomplete_hint = "" autocomplete_hint = ""
validation_required = "" validation_required = ""
picker_title = ""
picker_label = ""
open_picker = ""
picker_description = ""
picker_hint = ""
picker_hint_with_count = ""
[ui.dev.clients.general.id_token_claims] [ui.dev.clients.general.id_token_claims]
title = "" title = ""
@@ -1730,6 +1756,12 @@ cache_status = ""
cache_uri = "" cache_uri = ""
revoke_cache = "" revoke_cache = ""
[ui.dev.clients.general.public_key.validation]
missing_jwks_uri = ""
invalid_jwks_uri = ""
unsupported_parsed_algorithms = ""
missing_parsed_algorithms = ""
[ui.dev.clients.relationships] [ui.dev.clients.relationships]
title = "" title = ""
add_title = "" add_title = ""