1
0
forked from baron/baron-sso

i18n 누락 정리 및 하드코딩 텍스트 치환

This commit is contained in:
2026-05-06 16:31:40 +09:00
parent 9a87af93f1
commit 64cdef81a6
6 changed files with 154 additions and 81 deletions

View File

@@ -14,34 +14,34 @@ export function ForbiddenMessage({ resourceToken }: Props) {
let explanation = t(
"msg.dev.forbidden.default",
"해당 리소스에 접근할 권한이 없습니다. 관리자에게 문의하세요.",
"You do not have permission to access this resource. Contact your administrator.",
);
if (role === "rp_admin") {
explanation = t(
"msg.dev.forbidden.rp_admin",
"RP 관리자는 담당 앱의 리소스만 조회할 수 있습니다.",
"RP administrators can only access resources for their assigned applications.",
);
} else if (role === "tenant_admin") {
explanation = t(
"msg.dev.forbidden.tenant_admin",
"테넌트 관리자 권한이 올바르게 설정되지 않았거나 만료되었습니다.",
"Your tenant administrator permission is missing, misconfigured, or expired.",
);
} else if (role === "user" || role === "tenant_member") {
if (resourceToken === "consents") {
explanation = t(
"msg.dev.forbidden.user.consents",
"해당 앱(RP)에 대한 동의 내역 조회는 'RP 관리자', '동의 조회', '동의 회수' 관계가 부여된 경우에만 사용할 수 있습니다. 권한이 필요하면 관리자에게 요청하세요.",
"Viewing consent records for this application requires an RP administrator, consent read, or consent revoke relationship. Request access from an administrator if needed.",
);
} else if (resourceToken === "audit") {
explanation = t(
"msg.dev.forbidden.user.audit",
"해당 앱(RP)에 대한 감사 로그 조회는 'RP 관리자', '감사 조회' 관계가 부여된 경우에만 사용할 수 있습니다. 권한이 필요하면 관리자에게 요청하세요.",
"Viewing audit logs for this application requires an RP administrator or audit read relationship. Request access from an administrator if needed.",
);
} else {
explanation = t(
"msg.dev.forbidden.user.clients",
"일반 사용자 계정은 담당 RP(앱)에 대한 운영 또는 관리 관계가 부여된 경우에만 해당 기능을 사용할 수 있습니다. 권한이 필요하면 관리자에게 요청하세요.",
"Standard user accounts can use this feature only when an operational or administrative relationship is granted for the target RP. Request access from an administrator if needed.",
);
}
}
@@ -51,9 +51,9 @@ export function ForbiddenMessage({ resourceToken }: Props) {
? t("ui.dev.audit.title", "Audit Logs")
: resourceToken === "consents"
? t("ui.dev.clients.consents.title", "User Consent Grants")
: t("ui.dev.clients.registry.subtitle", "연동 앱");
: t("ui.dev.clients.registry.subtitle", "Connected Applications");
const title = t("msg.dev.forbidden.title", "{{resource}} 접근 권한 없음", {
const title = t("msg.dev.forbidden.title", "Access denied: {{resource}}", {
resource: resourceLabel,
});

View File

@@ -32,7 +32,7 @@ const navItems = [
},
{
labelKey: "ui.dev.nav.developer_request",
labelFallback: "개발자 권한 신청",
labelFallback: "Developer Access Request",
to: "/developer-requests",
icon: ClipboardCheck,
},
@@ -71,7 +71,11 @@ function AppLayout() {
});
const handleLogout = () => {
if (window.confirm(t("msg.dev.logout_confirm", "로그아웃 하시겠습니까?"))) {
if (
window.confirm(
t("msg.dev.logout_confirm", "Are you sure you want to log out?"),
)
) {
auth.removeUser();
navigate("/login");
}
@@ -136,7 +140,7 @@ function AppLayout() {
try {
await auth.signinSilent();
} catch (error) {
console.error("세션 자동 연장에 실패했습니다.", error);
console.error("Silent session renewal failed.", error);
} finally {
isRenewInFlightRef.current = false;
}
@@ -184,7 +188,7 @@ function AppLayout() {
try {
await auth.signinSilent();
} catch (error) {
console.error("세션 무제한 유지 갱신에 실패했습니다.", error);
console.error("Unlimited session keepalive renewal failed.", error);
} finally {
isRenewInFlightRef.current = false;
}
@@ -241,7 +245,7 @@ function AppLayout() {
void auth
.signinSilent()
.catch((error) => {
console.error("세션 자동 연장에 실패했습니다.", error);
console.error("Silent session renewal failed.", error);
})
.finally(() => {
isRenewInFlightRef.current = false;
@@ -289,15 +293,15 @@ function AppLayout() {
let sessionToneClass =
"border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300";
let sessionText = t("ui.dev.session.active", "세션 활성");
let sessionText = t("ui.dev.session.active", "Session active");
if (remainingMs === null) {
sessionToneClass = "border-border bg-card text-muted-foreground";
sessionText = t("ui.dev.session.unknown", "알 수 없음");
sessionText = t("ui.dev.session.unknown", "Unknown");
} else if (remainingMs <= 0) {
sessionToneClass =
"border-rose-500/30 bg-rose-500/10 text-rose-700 dark:text-rose-300";
sessionText = t("ui.dev.session.expired", "세션 만료");
sessionText = t("ui.dev.session.expired", "Session expired");
} else if (
remainingMinutes !== null &&
remainingSeconds !== null &&
@@ -307,7 +311,7 @@ function AppLayout() {
"border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300";
sessionText = t(
"ui.dev.session.expiring",
"만료 임박: {{minutes}} {{seconds}}초 남음",
"Expiring soon: {{minutes}}m {{seconds}}s left",
{
minutes: remainingMinutes,
seconds: remainingSeconds,
@@ -316,7 +320,7 @@ function AppLayout() {
} else {
sessionText = t(
"ui.dev.session.remaining",
"만료 예정: {{minutes}} {{seconds}}초 남음",
"Expires in {{minutes}}m {{seconds}}s",
{
minutes: remainingMinutes ?? 0,
seconds: remainingSeconds ?? 0,
@@ -343,7 +347,7 @@ function AppLayout() {
</div>
<div>
<p className="text-xs uppercase tracking-[0.18em] text-muted-foreground">
{t("ui.dev.brand", "Baron 로그인")}
{t("ui.dev.brand", "Baron Sign In")}
</p>
<h1 className="text-lg font-semibold">
{t("ui.dev.console_title", "Developer Console")}
@@ -423,7 +427,7 @@ function AppLayout() {
type="button"
onClick={toggleTheme}
className="inline-flex items-center gap-2 rounded-full border border-border px-3 py-2 text-muted-foreground transition hover:bg-muted/20"
aria-label={t("ui.common.theme_toggle", "테마 전환")}
aria-label={t("ui.common.theme_toggle", "Toggle theme")}
>
{theme === "light" ? <Sun size={16} /> : <Moon size={16} />}
{theme === "light"
@@ -447,7 +451,7 @@ function AppLayout() {
className="inline-flex items-center gap-3 rounded-full border border-border bg-card px-3 py-2 transition hover:bg-muted/20"
aria-haspopup="menu"
aria-expanded={isProfileMenuOpen}
aria-label={t("ui.dev.profile.menu_aria", "계정 메뉴 열기")}
aria-label={t("ui.dev.profile.menu_aria", "Open account menu")}
>
<div className="grid h-8 w-8 place-items-center rounded-full bg-primary/15 text-xs font-semibold text-primary">
{profileInitial}
@@ -496,14 +500,14 @@ function AppLayout() {
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-sm font-medium text-foreground">
{t("ui.dev.session.auto_extend", "세션 만료 관리")}
{t("ui.dev.session.auto_extend", "Session expiry")}
</p>
<p className="text-xs text-muted-foreground">
{isSessionExpiryEnabled
? sessionText
: t(
"ui.dev.session.disabled",
"세션 만료 비활성화",
"Session expiry disabled",
)}
</p>
</div>
@@ -539,7 +543,7 @@ function AppLayout() {
}}
>
<UserIcon size={16} className="text-muted-foreground" />
<span>{t("ui.dev.profile.title", "내 정보")}</span>
<span>{t("ui.dev.profile.title", "My Profile")}</span>
</button>
<button
type="button"

View File

@@ -1,14 +1,15 @@
const Map<String, String> internalErrorWhitelistMessages = {
'settings_disabled': '현재 계정 설정 화면은 준비 중입니다.',
'invalid_session': '세션이 만료되었습니다. 다시 로그인해 주세요.',
'verification_required': '추가 인증이 필요합니다. 안내에 따라 진행해 주세요.',
'recovery_expired': '재설정 링크가 만료되었습니다. 다시 요청해 주세요.',
'recovery_invalid': '재설정 링크가 유효하지 않습니다.',
'rate_limited': '요청이 많습니다. 잠시 후 다시 시도해 주세요.',
'not_found': '요청한 페이지를 찾을 수 없습니다.',
'bad_request': '입력값을 확인해 주세요.',
'password_or_email_mismatch': '이메일 혹은 비밀번호가 일치하지 않습니다.',
'tenant_not_allowed': '허용되지 않은 테넌트입니다.',
const Map<String, String> internalErrorWhitelistMessageKeys = {
'settings_disabled': 'msg.userfront.error.whitelist.settings_disabled',
'invalid_session': 'msg.userfront.error.whitelist.invalid_session',
'verification_required': 'msg.userfront.error.whitelist.verification_required',
'recovery_expired': 'msg.userfront.error.whitelist.recovery_expired',
'recovery_invalid': 'msg.userfront.error.whitelist.recovery_invalid',
'rate_limited': 'msg.userfront.error.whitelist.rate_limited',
'not_found': 'msg.userfront.error.whitelist.not_found',
'bad_request': 'msg.userfront.error.whitelist.bad_request',
'password_or_email_mismatch':
'msg.userfront.error.whitelist.password_or_email_mismatch',
'tenant_not_allowed': 'msg.userfront.error.whitelist.tenant_not_allowed',
};
const Set<String> oryBypassErrorCodes = {

View File

@@ -65,7 +65,7 @@ class AuthProxyService {
} else {
throw _error(
'err.userfront.auth_proxy.password_policy_fetch',
'비밀번호 정책을 불러오지 못했습니다.',
'Failed to load the password policy.',
);
}
}
@@ -84,7 +84,7 @@ class AuthProxyService {
}
throw _error(
'err.userfront.auth_proxy.profile_load',
'프로필을 불러오지 못했습니다: {{error}}',
'Failed to load the profile: {{error}}',
detail: response.body,
);
} finally {
@@ -110,7 +110,7 @@ class AuthProxyService {
}
throw _error(
'err.userfront.auth_proxy.profile_load',
'프로필을 불러오지 못했습니다: {{error}}',
'Failed to load the profile: {{error}}',
detail: response.body,
);
} finally {
@@ -144,7 +144,7 @@ class AuthProxyService {
} else {
throw _error(
'err.userfront.auth_proxy.tenant_info_fetch',
'테넌트 정보를 불러오지 못했습니다.',
'Failed to load tenant information.',
);
}
}
@@ -180,7 +180,7 @@ class AuthProxyService {
} else {
throw _error(
'err.userfront.auth_proxy.login_init',
'로그인 초기화에 실패했습니다: {{error}}',
'Failed to initialize login: {{error}}',
detail: response.body,
);
}
@@ -205,7 +205,7 @@ class AuthProxyService {
}
throw _error(
'err.userfront.auth_proxy.login_poll',
'로그인 상태 확인에 실패했습니다: {{error}}',
'Failed to check login status: {{error}}',
detail: response.body,
);
}
@@ -227,7 +227,7 @@ class AuthProxyService {
} else {
throw _error(
'err.userfront.auth_proxy.verify_failed',
'검증에 실패했습니다: {{error}}',
'Verification failed: {{error}}',
detail: response.body,
);
}
@@ -261,7 +261,7 @@ class AuthProxyService {
} else {
throw _error(
'err.userfront.auth_proxy.verify_failed',
'검증에 실패했습니다: {{error}}',
'Verification failed: {{error}}',
detail: response.body,
);
}
@@ -281,7 +281,7 @@ class AuthProxyService {
if (response.statusCode != 200) {
throw _error(
'err.userfront.dashboard.sessions.revoke',
'세션 종료에 실패했습니다: {{error}}',
'Failed to revoke the session: {{error}}',
detail: response.body,
);
}
@@ -304,7 +304,7 @@ class AuthProxyService {
if (response.statusCode != 200) {
throw _error(
'err.userfront.dashboard.sessions.load',
'활성 세션을 불러오지 못했습니다: {{error}}',
'Failed to load the active sessions: {{error}}',
detail: response.body,
);
}
@@ -342,7 +342,7 @@ class AuthProxyService {
} else {
throw _error(
'err.userfront.auth_proxy.verify_failed',
'검증에 실패했습니다: {{error}}',
'Verification failed: {{error}}',
detail: response.body,
);
}
@@ -568,7 +568,7 @@ class AuthProxyService {
if (response.statusCode != 200) {
throw _error(
'err.userfront.auth_proxy.sms_send',
'SMS 전송에 실패했습니다: {{error}}',
'Failed to send SMS: {{error}}',
detail: response.body,
);
}
@@ -591,7 +591,7 @@ class AuthProxyService {
} else {
throw _error(
'err.userfront.auth_proxy.code_verify',
'인증 코드 확인에 실패했습니다: {{error}}',
'Failed to verify the code: {{error}}',
detail: response.body,
);
}
@@ -609,7 +609,7 @@ class AuthProxyService {
} else {
throw _error(
'err.userfront.auth_proxy.qr_init',
'QR 로그인을 시작하지 못했습니다: {{error}}',
'Failed to start QR login: {{error}}',
detail: response.body,
);
}
@@ -631,7 +631,7 @@ class AuthProxyService {
}
throw _error(
'err.userfront.auth_proxy.qr_poll',
'QR 상태 확인에 실패했습니다: {{error}}',
'Failed to check QR status: {{error}}',
detail: response.body,
);
}
@@ -669,7 +669,7 @@ class AuthProxyService {
if (response.statusCode != 200) {
throw _error(
'err.userfront.auth_proxy.qr_approve',
'QR 승인에 실패했습니다: {{error}}',
'Failed to approve QR login: {{error}}',
detail: response.body,
);
}
@@ -720,7 +720,7 @@ class AuthProxyService {
if (response.statusCode != 200) {
throw _error(
'err.userfront.auth_proxy.user_create',
'사용자 생성에 실패했습니다: {{error}}',
'Failed to create the user: {{error}}',
detail: response.body,
);
}
@@ -749,7 +749,7 @@ class AuthProxyService {
} else {
throw _error(
'err.userfront.auth_proxy.user_list',
'사용자 목록 조회에 실패했습니다: {{error}}',
'Failed to load the user list: {{error}}',
detail: response.body,
);
}
@@ -770,7 +770,7 @@ class AuthProxyService {
if (response.statusCode != 200) {
throw _error(
'err.userfront.auth_proxy.user_delete',
'사용자 삭제에 실패했습니다: {{error}}',
'Failed to delete the user: {{error}}',
detail: response.body,
);
}
@@ -796,7 +796,7 @@ class AuthProxyService {
if (response.statusCode != 200) {
throw _error(
'err.userfront.auth_proxy.user_status_update',
'상태 업데이트에 실패했습니다: {{error}}',
'Failed to update the user status: {{error}}',
detail: response.body,
);
}
@@ -829,7 +829,7 @@ class AuthProxyService {
if (response.statusCode != 200) {
throw _error(
'err.userfront.auth_proxy.user_update',
'사용자 수정에 실패했습니다: {{error}}',
'Failed to update the user: {{error}}',
detail: response.body,
);
}
@@ -855,7 +855,7 @@ class AuthProxyService {
} else {
throw _error(
'err.userfront.auth_proxy.linked_apps_load',
'연동된 앱 목록을 불러오지 못했습니다.',
'Failed to load linked applications.',
);
}
} finally {
@@ -1043,7 +1043,7 @@ class AuthProxyService {
if (response.statusCode != 200) {
throw _error(
'err.userfront.auth_proxy.phone_code_send',
'인증 코드 전송에 실패했습니다: {{error}}',
'Failed to send the verification code: {{error}}',
detail: response.body,
);
}

View File

@@ -282,9 +282,9 @@ class _ErrorScreenState extends State<ErrorScreen> {
final isProd = widget.isProdOverride ?? AuthProxyService.isProdEnv;
final normalizedCode = (widget.errorCode ?? '').trim();
final hasCode = normalizedCode.isNotEmpty;
final internalWhitelistFallback =
internalErrorWhitelistMessages[normalizedCode];
final isInternalWhitelisted = internalWhitelistFallback != null;
final internalWhitelistKey =
internalErrorWhitelistMessageKeys[normalizedCode];
final isInternalWhitelisted = internalWhitelistKey != null;
final isOryBypass = hasCode && oryBypassErrorCodes.contains(normalizedCode);
final isKnownProdCode = hasCode && (isInternalWhitelisted || isOryBypass);
final isTenantAccessBlocked = normalizedCode == 'tenant_not_allowed';
@@ -294,7 +294,7 @@ class _ErrorScreenState extends State<ErrorScreen> {
final title = isTenantAccessBlocked
? tr(
'msg.userfront.error.tenant.page_title',
fallback: '애플리케이션 접근이 제한되었습니다',
fallback: 'Application access is restricted',
)
: isProd
? tr('msg.userfront.error.title')
@@ -335,14 +335,11 @@ class _ErrorScreenState extends State<ErrorScreen> {
final detail = isTenantAccessBlocked
? tr(
'msg.userfront.error.tenant.detail',
fallback: '현재 로그인된 계정은 이 애플리케이션에 접근할 수 없습니다.',
fallback: 'The current signed-in account cannot access this application.',
)
: isProd
? (isInternalWhitelisted
? tr(
'msg.userfront.error.whitelist.$normalizedCode',
fallback: internalWhitelistFallback,
)
? tr(internalWhitelistKey!)
: (isOryBypass
? tr(
'msg.userfront.error.ory.$normalizedCode',
@@ -422,7 +419,7 @@ class _ErrorScreenState extends State<ErrorScreen> {
Text(
tr(
'msg.userfront.error.tenant.title',
fallback: '접근 제한 정보',
fallback: 'Access restriction details',
),
style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w700,
@@ -447,7 +444,7 @@ class _ErrorScreenState extends State<ErrorScreen> {
child: Text(
tr(
'msg.userfront.error.tenant.loading',
fallback: '현재 계정 정보를 불러오는 중입니다.',
fallback: 'Loading the current account details.',
),
style: theme.textTheme.bodySmall
?.copyWith(
@@ -462,39 +459,39 @@ class _ErrorScreenState extends State<ErrorScreen> {
_InfoRow(
label: tr(
'msg.userfront.error.tenant.account',
fallback: '계정',
fallback: 'Account',
),
value: emailLabel.isNotEmpty
? emailLabel
: tr(
'msg.userfront.error.tenant.account_unknown',
fallback: '알 수 없음',
fallback: 'Unknown',
),
),
const SizedBox(height: 8),
_InfoRow(
label: tr(
'msg.userfront.error.tenant.primary_tenant',
fallback: '대표 소속 테넌트',
fallback: 'Primary affiliated tenant',
),
value: tenantLabel.isNotEmpty
? tenantLabel
: tr(
'msg.userfront.error.tenant.tenant_unknown',
fallback: '알 수 없음',
fallback: 'Unknown',
),
),
const SizedBox(height: 8),
_InfoRow(
label: tr(
'msg.userfront.error.tenant.affiliated_tenants',
fallback: '전체 소속 테넌트',
fallback: 'All affiliated tenants',
),
value: affiliatedTenantLabels.isNotEmpty
? affiliatedTenantLabels.join(', ')
: tr(
'msg.userfront.error.tenant.tenant_unknown',
fallback: '알 수 없음',
fallback: 'Unknown',
),
),
if (showTenantLookupFallback) ...[
@@ -503,7 +500,7 @@ class _ErrorScreenState extends State<ErrorScreen> {
tr(
'msg.userfront.error.tenant.lookup_fallback',
fallback:
'표시 정보가 충분하지 않아 일부 항목은 확인되지 않을 수 있습니다.',
'Some fields may be unavailable because there is not enough profile information to display.',
),
style: theme.textTheme.bodySmall
?.copyWith(
@@ -518,7 +515,7 @@ class _ErrorScreenState extends State<ErrorScreen> {
tr(
'msg.userfront.error.tenant.load_failed',
fallback:
'계정 정보를 확인하지 못했습니다. 다시 시도해 주세요.',
'Failed to load account details. Please try again.',
),
style: theme.textTheme.bodySmall
?.copyWith(
@@ -548,7 +545,7 @@ class _ErrorScreenState extends State<ErrorScreen> {
Text(
tr(
'msg.userfront.error.tenant.allowed_box_title',
fallback: '접속 가능 테넌트',
fallback: 'Allowed tenants',
),
style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w700,
@@ -559,7 +556,7 @@ class _ErrorScreenState extends State<ErrorScreen> {
_InfoRow(
label: tr(
'msg.userfront.error.tenant.allowed_tenants',
fallback: '접속 가능 테넌트',
fallback: 'Allowed tenants',
),
value: allowedTenantLabels.join(', '),
),
@@ -567,11 +564,11 @@ class _ErrorScreenState extends State<ErrorScreen> {
_InfoRow(
label: tr(
'msg.userfront.error.tenant.allowed_tenants',
fallback: '접속 가능 테넌트',
fallback: 'Allowed tenants',
),
value: tr(
'msg.userfront.error.tenant.tenant_unknown',
fallback: '알 수 없음',
fallback: 'Unknown',
),
),
],

View File

@@ -34,9 +34,41 @@ const Map<String, String> koStrings = {
"err.userfront.auth_proxy.consent_reject": "동의 거부에 실패했습니다.",
"err.userfront.auth_proxy.linked_app_revoke": "연동 해지에 실패했습니다.",
"err.userfront.auth_proxy.login_failed": "로그인에 실패했습니다.",
"err.userfront.auth_proxy.login_init": "로그인 초기화에 실패했습니다: {{error}}",
"err.userfront.auth_proxy.login_poll":
"로그인 상태 확인에 실패했습니다: {{error}}",
"err.userfront.auth_proxy.oidc_accept": "OIDC 로그인 승인에 실패했습니다.",
"err.userfront.auth_proxy.password_reset_complete": "비밀번호 재설정에 실패했습니다.",
"err.userfront.auth_proxy.password_policy_fetch":
"비밀번호 정책을 불러오지 못했습니다.",
"err.userfront.auth_proxy.password_reset_init": "비밀번호 재설정을 시작하지 못했습니다.",
"err.userfront.auth_proxy.profile_load":
"프로필을 불러오지 못했습니다: {{error}}",
"err.userfront.auth_proxy.tenant_info_fetch":
"테넌트 정보를 불러오지 못했습니다.",
"err.userfront.auth_proxy.verify_failed": "검증에 실패했습니다: {{error}}",
"err.userfront.auth_proxy.sms_send": "SMS 전송에 실패했습니다: {{error}}",
"err.userfront.auth_proxy.code_verify":
"인증 코드 확인에 실패했습니다: {{error}}",
"err.userfront.auth_proxy.qr_init":
"QR 로그인을 시작하지 못했습니다: {{error}}",
"err.userfront.auth_proxy.qr_poll":
"QR 상태 확인에 실패했습니다: {{error}}",
"err.userfront.auth_proxy.qr_approve": "QR 승인에 실패했습니다: {{error}}",
"err.userfront.auth_proxy.user_create":
"사용자 생성에 실패했습니다: {{error}}",
"err.userfront.auth_proxy.user_list":
"사용자 목록 조회에 실패했습니다: {{error}}",
"err.userfront.auth_proxy.user_delete":
"사용자 삭제에 실패했습니다: {{error}}",
"err.userfront.auth_proxy.user_status_update":
"상태 업데이트에 실패했습니다: {{error}}",
"err.userfront.auth_proxy.user_update":
"사용자 수정에 실패했습니다: {{error}}",
"err.userfront.auth_proxy.linked_apps_load":
"연동된 앱 목록을 불러오지 못했습니다.",
"err.userfront.auth_proxy.phone_code_send":
"인증 코드 전송에 실패했습니다: {{error}}",
"err.userfront.profile.load_failed": "프로필을 불러오지 못했습니다: {{error}}",
"err.userfront.profile.password_change_failed": "비밀번호 변경에 실패했습니다: {{error}}",
"err.userfront.profile.send_code_failed": "인증번호 전송 실패: {{error}}",
@@ -589,6 +621,8 @@ const Map<String, String> koStrings = {
"재설정 링크가 만료되었습니다. 다시 요청해 주세요.",
"msg.userfront.error.whitelist.recovery_invalid": "재설정 링크가 유효하지 않습니다.",
"msg.userfront.error.whitelist.settings_disabled": "현재 계정 설정 화면은 준비 중입니다.",
"msg.userfront.error.whitelist.tenant_not_allowed":
"허용되지 않은 테넌트입니다.",
"msg.userfront.error.whitelist.verification_required":
"추가 인증이 필요합니다. 안내에 따라 진행해 주세요.",
"msg.userfront.forgot.description":
@@ -2010,11 +2044,46 @@ const Map<String, String> enStrings = {
"err.userfront.auth_proxy.linked_app_revoke":
"Failed to revoke the linked application.",
"err.userfront.auth_proxy.login_failed": "Login failed.",
"err.userfront.auth_proxy.login_init":
"Failed to initialize login: {{error}}",
"err.userfront.auth_proxy.login_poll":
"Failed to check login status: {{error}}",
"err.userfront.auth_proxy.oidc_accept": "OIDC Accept",
"err.userfront.auth_proxy.password_reset_complete":
"Failed to complete the password reset.",
"err.userfront.auth_proxy.password_policy_fetch":
"Failed to load the password policy.",
"err.userfront.auth_proxy.password_reset_init":
"Failed to start the password reset.",
"err.userfront.auth_proxy.profile_load":
"Failed to load the profile: {{error}}",
"err.userfront.auth_proxy.tenant_info_fetch":
"Failed to load tenant information.",
"err.userfront.auth_proxy.verify_failed":
"Verification failed: {{error}}",
"err.userfront.auth_proxy.sms_send": "Failed to send SMS: {{error}}",
"err.userfront.auth_proxy.code_verify":
"Failed to verify the code: {{error}}",
"err.userfront.auth_proxy.qr_init":
"Failed to start QR login: {{error}}",
"err.userfront.auth_proxy.qr_poll":
"Failed to check QR status: {{error}}",
"err.userfront.auth_proxy.qr_approve":
"Failed to approve QR login: {{error}}",
"err.userfront.auth_proxy.user_create":
"Failed to create the user: {{error}}",
"err.userfront.auth_proxy.user_list":
"Failed to load the user list: {{error}}",
"err.userfront.auth_proxy.user_delete":
"Failed to delete the user: {{error}}",
"err.userfront.auth_proxy.user_status_update":
"Failed to update the user status: {{error}}",
"err.userfront.auth_proxy.user_update":
"Failed to update the user: {{error}}",
"err.userfront.auth_proxy.linked_apps_load":
"Failed to load linked applications.",
"err.userfront.auth_proxy.phone_code_send":
"Failed to send the verification code: {{error}}",
"err.userfront.profile.load_failed": "Failed to load the profile.",
"err.userfront.profile.password_change_failed": "Password Change Failed",
"err.userfront.profile.send_code_failed":
@@ -2669,6 +2738,8 @@ const Map<String, String> enStrings = {
"The recovery link is invalid.",
"msg.userfront.error.whitelist.settings_disabled":
"Account settings are currently unavailable.",
"msg.userfront.error.whitelist.tenant_not_allowed":
"This tenant is not allowed.",
"msg.userfront.error.whitelist.verification_required":
"Additional verification is required. Please follow the instructions.",
"msg.userfront.forgot.description":