@podosoft/podokit 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/dist/add.d.ts +16 -0
- package/dist/add.js +63 -20
- package/dist/assemble.js +9 -9
- package/dist/dev.d.ts +26 -0
- package/dist/dev.js +297 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +82 -2
- package/dist/lib.d.ts +2 -0
- package/dist/lib.js +7 -1
- package/dist/locale.d.ts +22 -0
- package/dist/locale.js +184 -0
- package/dist/lockfile.d.ts +5 -3
- package/dist/lockfile.js +21 -5
- package/dist/remove.js +26 -23
- package/dist/templates/fullstack-nest-svelte/README.md +42 -0
- package/dist/templates/fullstack-nest-svelte/apps/api/Dockerfile +1 -0
- package/dist/templates/fullstack-nest-svelte/apps/api/scripts/.keep +1 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/README.md +7 -5
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/catalogs/app/en.json +1 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/catalogs/app/ko.json +1 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/locales/en.json +6 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/locales/ko.json +6 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/vite.config.ts +3 -9
- package/dist/templates/fullstack-nest-svelte/compose.dev.yaml +8 -11
- package/dist/templates/fullstack-nest-svelte/dot-claude/skills/podokit-add-locale/SKILL.md +63 -0
- package/dist/templates/fullstack-nest-svelte/dot-claude/skills/podokit-add-locale/agents/openai.yaml +4 -0
- package/dist/templates/fullstack-nest-svelte/dot-claude/skills/podokit-add-locale/references/catalog-format.md +49 -0
- package/dist/templates/fullstack-nest-svelte/dot-env.docker +2 -2
- package/dist/templates/fullstack-nest-svelte/dot-gitignore +3 -0
- package/dist/templates/fullstack-nest-svelte/dot-podokit/dev.json +4 -0
- package/dist/templates/fullstack-nest-svelte/tests/README.md +10 -4
- package/dist/templates/modules/admin-dashboard/files/apps/api/scripts/bootstrap-admin.mjs +187 -0
- package/dist/templates/modules/admin-dashboard/files/apps/api/src/site-settings/site-settings.controller.ts +9 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/hooks.server.ts +5 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/auth-errors.ts +3 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/auth-redirect.ts +27 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/account-menu.svelte +8 -3
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/sidebar-user-menu.svelte +1 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/catalogs/admin-dashboard/en.json +564 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/catalogs/admin-dashboard/ko.json +564 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/index.ts +13 -12
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/messages.ts +90 -722
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/site.svelte.ts +8 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/audit/+page.svelte +4 -4
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/organizations/+page.svelte +2 -2
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/sessions/+page.svelte +4 -4
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/+page.svelte +8 -2
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/general-settings.svelte +15 -4
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/users/+page.svelte +4 -4
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/login/+page.svelte +32 -35
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/signup/+page.svelte +10 -3
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/+layout.server.ts +11 -7
- package/dist/templates/modules/admin-dashboard/files/tests/api/auth-config.api.spec.ts +78 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/auth.ui.spec.ts +28 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/email.ui.spec.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/general-effects.ui.spec.ts +15 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/i18n.ui.spec.ts +59 -6
- package/dist/templates/modules/admin-dashboard/files/tests/ui/login-methods.ui.spec.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/passkey.ui.spec.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/settings-dialogs.ui.spec.ts +5 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/two-factor.ui.spec.ts +2 -2
- package/dist/templates/modules/admin-dashboard/module.manifest.json +14 -3
- package/dist/templates/modules/auth/files/apps/api/scripts/configure-auth.mjs +22 -11
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.ts +6 -2
- package/dist/templates/modules/auth/files/apps/api/src/auth/feature-gate.ts +50 -25
- package/dist/templates/modules/auth/files/dot-claude/skills/podokit-configure-auth/SKILL.md +15 -2
- package/dist/templates/modules/auth/files/dot-claude/skills/podokit-configure-auth/agents/openai.yaml +2 -2
- package/dist/templates/modules/auth/files/dot-claude/skills/podokit-configure-auth/references/bootstrap-admin.md +51 -0
- package/dist/templates/modules/auth/files/dot-claude/skills/podokit-configure-auth/references/google.md +44 -0
- package/dist/templates/modules/auth/module.manifest.json +3 -0
- package/dist/templates/todo/README.md +42 -0
- package/dist/templates/todo/apps/api/Dockerfile +1 -0
- package/dist/templates/todo/apps/api/scripts/.keep +1 -0
- package/dist/templates/todo/apps/web/src/lib/i18n/README.md +7 -5
- package/dist/templates/todo/apps/web/src/lib/i18n/catalogs/app/en.json +1 -0
- package/dist/templates/todo/apps/web/src/lib/i18n/catalogs/app/ko.json +1 -0
- package/dist/templates/todo/apps/web/src/lib/i18n/locales/en.json +6 -0
- package/dist/templates/todo/apps/web/src/lib/i18n/locales/ko.json +6 -0
- package/dist/templates/todo/apps/web/vite.config.ts +3 -9
- package/dist/templates/todo/compose.dev.yaml +8 -11
- package/dist/templates/todo/dot-claude/skills/podokit-add-locale/SKILL.md +63 -0
- package/dist/templates/todo/dot-claude/skills/podokit-add-locale/agents/openai.yaml +4 -0
- package/dist/templates/todo/dot-claude/skills/podokit-add-locale/references/catalog-format.md +49 -0
- package/dist/templates/todo/dot-env.docker +2 -2
- package/dist/templates/todo/dot-gitignore +3 -0
- package/dist/templates/todo/dot-podokit/dev.json +4 -0
- package/dist/templates/todo/tests/README.md +10 -4
- package/dist/update.js +48 -7
- package/package.json +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/en.ts +0 -10
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/i18n/ko.ts +0 -11
- package/dist/templates/todo/apps/web/src/lib/i18n/en.ts +0 -10
- package/dist/templates/todo/apps/web/src/lib/i18n/ko.ts +0 -11
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
{
|
|
2
|
+
"common": {
|
|
3
|
+
"appName": "관리자"
|
|
4
|
+
},
|
|
5
|
+
"landing": {
|
|
6
|
+
"title": "환영합니다",
|
|
7
|
+
"subtitle": "앱의 홈 페이지입니다. 원하는 내용으로 교체하세요 — 관리자 기능은 /admin 에 있습니다.",
|
|
8
|
+
"openAdmin": "관리자 열기"
|
|
9
|
+
},
|
|
10
|
+
"footer": {
|
|
11
|
+
"support": "고객지원",
|
|
12
|
+
"terms": "이용약관",
|
|
13
|
+
"privacy": "개인정보처리방침"
|
|
14
|
+
},
|
|
15
|
+
"maintenance": {
|
|
16
|
+
"title": "점검 중",
|
|
17
|
+
"subtitle": "사이트를 일시적으로 사용할 수 없습니다. 잠시 후 다시 확인해 주세요.",
|
|
18
|
+
"adminHint": "관리자는 로그인하여 사이트를 관리할 수 있습니다.",
|
|
19
|
+
"signIn": "관리자 로그인"
|
|
20
|
+
},
|
|
21
|
+
"language": {
|
|
22
|
+
"label": "언어"
|
|
23
|
+
},
|
|
24
|
+
"toolbar": {
|
|
25
|
+
"filter": "필터",
|
|
26
|
+
"search": "검색",
|
|
27
|
+
"searchButton": "검색",
|
|
28
|
+
"all": "전체"
|
|
29
|
+
},
|
|
30
|
+
"theme": {
|
|
31
|
+
"toggle": "테마 전환",
|
|
32
|
+
"light": "라이트",
|
|
33
|
+
"dark": "다크",
|
|
34
|
+
"system": "시스템"
|
|
35
|
+
},
|
|
36
|
+
"setup2fa": {
|
|
37
|
+
"title": "2단계 인증 설정",
|
|
38
|
+
"subtitle": "이 사이트는 2단계 인증이 필수입니다. 계속하려면 인증 앱을 등록하세요.",
|
|
39
|
+
"passwordLabel": "비밀번호 확인",
|
|
40
|
+
"begin": "계속",
|
|
41
|
+
"step1": "1. 인증 앱으로 스캔",
|
|
42
|
+
"step2": "2. 백업 코드 저장",
|
|
43
|
+
"step3": "3. 6자리 코드 입력",
|
|
44
|
+
"savedConfirm": "백업 코드를 저장했습니다",
|
|
45
|
+
"activate": "활성화하고 계속",
|
|
46
|
+
"signOut": "로그아웃",
|
|
47
|
+
"wrongPassword": "비밀번호가 올바르지 않습니다.",
|
|
48
|
+
"enableFailed": "설정을 시작할 수 없습니다. 다시 시도해 주세요."
|
|
49
|
+
},
|
|
50
|
+
"auth": {
|
|
51
|
+
"signInTitle": "로그인",
|
|
52
|
+
"signInDesc": "계속하려면 로그인하세요.",
|
|
53
|
+
"email": "이메일",
|
|
54
|
+
"emailOrUsername": "이메일 또는 사용자명",
|
|
55
|
+
"password": "비밀번호",
|
|
56
|
+
"forgot": "비밀번호 찾기",
|
|
57
|
+
"signIn": "로그인",
|
|
58
|
+
"signingIn": "로그인 중…",
|
|
59
|
+
"magicLinkButton": "로그인 링크 이메일로 받기",
|
|
60
|
+
"passkeyButton": "패스키 사용",
|
|
61
|
+
"magicLinkSent": "이메일의 로그인 링크를 확인하세요.",
|
|
62
|
+
"magicLinkNeedsEmail": "먼저 이메일을 입력하세요.",
|
|
63
|
+
"otpButton": "코드 이메일로 받기",
|
|
64
|
+
"otpCode": "일회용 코드",
|
|
65
|
+
"otpVerify": "코드 확인",
|
|
66
|
+
"verify": "확인",
|
|
67
|
+
"twoFactorPrompt": "인증 앱의 6자리 코드를 입력하세요.",
|
|
68
|
+
"twoFactorBackupPrompt": "백업 코드 중 하나를 입력하세요.",
|
|
69
|
+
"twoFactorCodeLabel": "인증 코드",
|
|
70
|
+
"backupCodeLabel": "백업 코드",
|
|
71
|
+
"twoFactorUseBackup": "백업 코드로 대신 인증",
|
|
72
|
+
"twoFactorUseAuthenticator": "인증 앱으로 대신 인증",
|
|
73
|
+
"twoFactorInvalidCode": "코드가 올바르지 않습니다. 다시 확인해 주세요.",
|
|
74
|
+
"twoFactorLocked": "시도가 너무 많습니다. 잠시 후 다시 시도해 주세요.",
|
|
75
|
+
"noAccount": "계정이 없으신가요?",
|
|
76
|
+
"signUp": "회원가입",
|
|
77
|
+
"signInFailed": "로그인에 실패했습니다",
|
|
78
|
+
"createTitle": "회원가입",
|
|
79
|
+
"createDesc": "가입하고 시작하세요.",
|
|
80
|
+
"name": "이름",
|
|
81
|
+
"create": "계정 만들기",
|
|
82
|
+
"creating": "생성 중…",
|
|
83
|
+
"haveAccount": "이미 계정이 있으신가요?",
|
|
84
|
+
"signUpFailed": "회원가입에 실패했습니다",
|
|
85
|
+
"resetTitle": "비밀번호 재설정",
|
|
86
|
+
"resetDesc": "이메일로 재설정 링크를 보내드립니다.",
|
|
87
|
+
"sendReset": "재설정 링크 보내기",
|
|
88
|
+
"sending": "전송 중…",
|
|
89
|
+
"resetSent": "해당 이메일이 존재하면 재설정 링크가 전송됩니다.",
|
|
90
|
+
"backToSignIn": "로그인으로 돌아가기",
|
|
91
|
+
"requestFailed": "요청에 실패했습니다",
|
|
92
|
+
"newTitle": "새 비밀번호 설정",
|
|
93
|
+
"newPassword": "새 비밀번호",
|
|
94
|
+
"updatePassword": "비밀번호 변경",
|
|
95
|
+
"updating": "변경 중…",
|
|
96
|
+
"missingToken": "재설정 토큰이 없거나 유효하지 않습니다.",
|
|
97
|
+
"verifyTitle": "이메일 인증",
|
|
98
|
+
"verifyDesc": "{email} 주소로 인증 링크를 보냈습니다. 링크를 열어 계정을 활성화하세요.",
|
|
99
|
+
"verifyDescNoEmail": "새 인증 링크를 받을 이메일을 입력하세요.",
|
|
100
|
+
"verifySent": "인증 메일을 보냈습니다 — 받은편지함을 확인하세요.",
|
|
101
|
+
"verifyLinkError": "인증 링크가 유효하지 않거나 만료되었습니다. 아래에서 다시 요청하세요.",
|
|
102
|
+
"resendVerification": "인증 메일 다시 보내기",
|
|
103
|
+
"emailNotVerified": "로그인 전에 이메일을 인증해 주세요.",
|
|
104
|
+
"approvalPendingTitle": "승인 대기 중",
|
|
105
|
+
"approvalPendingDesc": "계정이 생성되었으며 관리자의 승인을 기다리고 있습니다. 승인 후 다시 로그인해 주세요.",
|
|
106
|
+
"approvalPending": "관리자의 계정 승인을 기다리고 있습니다.",
|
|
107
|
+
"publicSignupDisabled": "공개 가입이 닫혀 있습니다. 기존 사용자는 계속 로그인할 수 있습니다.",
|
|
108
|
+
"continueWith": "{provider}(으)로 계속",
|
|
109
|
+
"resetFailed": "재설정에 실패했습니다"
|
|
110
|
+
},
|
|
111
|
+
"nav": {
|
|
112
|
+
"overview": "개요",
|
|
113
|
+
"users": "회원",
|
|
114
|
+
"sessions": "세션",
|
|
115
|
+
"organizations": "조직",
|
|
116
|
+
"audit": "감사 로그",
|
|
117
|
+
"account": "계정",
|
|
118
|
+
"settings": "설정",
|
|
119
|
+
"home": "홈으로 돌아가기",
|
|
120
|
+
"signOut": "로그아웃"
|
|
121
|
+
},
|
|
122
|
+
"passkeys": {
|
|
123
|
+
"title": "패스키",
|
|
124
|
+
"subtitle": "기기로 비밀번호 없이 로그인(Face ID, 지문, 보안 키).",
|
|
125
|
+
"name": "이름",
|
|
126
|
+
"namePlaceholder": "예: MacBook",
|
|
127
|
+
"add": "패스키 추가",
|
|
128
|
+
"remove": "삭제",
|
|
129
|
+
"untitled": "패스키",
|
|
130
|
+
"added": "패스키를 추가했습니다",
|
|
131
|
+
"addFailed": "패스키 추가에 실패했습니다",
|
|
132
|
+
"removed": "패스키를 삭제했습니다",
|
|
133
|
+
"removeFailed": "패스키 삭제에 실패했습니다",
|
|
134
|
+
"empty": "아직 패스키가 없습니다.",
|
|
135
|
+
"created": "생성일",
|
|
136
|
+
"total": "패스키 {count}개"
|
|
137
|
+
},
|
|
138
|
+
"acceptInvitation": {
|
|
139
|
+
"title": "조직 초대",
|
|
140
|
+
"pending": "초대를 수락하는 중…",
|
|
141
|
+
"ok": "조직에 합류했습니다.",
|
|
142
|
+
"error": "유효하지 않거나 만료된 초대입니다.",
|
|
143
|
+
"goToOrgs": "조직으로 이동"
|
|
144
|
+
},
|
|
145
|
+
"organizations": {
|
|
146
|
+
"title": "조직",
|
|
147
|
+
"subtitle": "멀티테넌트 팀. 조직을 만들고 멤버를 초대합니다.",
|
|
148
|
+
"create": "새 조직",
|
|
149
|
+
"createTitle": "조직 만들기",
|
|
150
|
+
"name": "이름",
|
|
151
|
+
"slug": "슬러그",
|
|
152
|
+
"created": "생성일",
|
|
153
|
+
"delete": "삭제",
|
|
154
|
+
"cancel": "취소",
|
|
155
|
+
"empty": "아직 조직이 없습니다.",
|
|
156
|
+
"total": "조직 {count}개",
|
|
157
|
+
"submit": "만들기",
|
|
158
|
+
"createdMsg": "조직을 만들었습니다",
|
|
159
|
+
"deletedMsg": "조직을 삭제했습니다",
|
|
160
|
+
"actionFailed": "작업에 실패했습니다",
|
|
161
|
+
"manage": "관리",
|
|
162
|
+
"membersDesc": "멤버와 대기 중인 초대.",
|
|
163
|
+
"members": "멤버",
|
|
164
|
+
"pending": "대기 중 초대",
|
|
165
|
+
"parent": "상위 조직",
|
|
166
|
+
"noParent": "없음(최상위)",
|
|
167
|
+
"managers": "매니저",
|
|
168
|
+
"managersHint": "매니저로 추가할 기존 사용자(여러 명 가능).",
|
|
169
|
+
"inviteEmail": "이메일로 초대",
|
|
170
|
+
"invite": "초대",
|
|
171
|
+
"invited": "초대를 보냈습니다",
|
|
172
|
+
"remove": "제거",
|
|
173
|
+
"cancelInvite": "취소"
|
|
174
|
+
},
|
|
175
|
+
"consent": {
|
|
176
|
+
"title": "접근 승인",
|
|
177
|
+
"subtitle": "한 애플리케이션이 계정 접근을 요청합니다.",
|
|
178
|
+
"requests": "접근 가능 범위:",
|
|
179
|
+
"allow": "허용",
|
|
180
|
+
"deny": "거부"
|
|
181
|
+
},
|
|
182
|
+
"apiKeys": {
|
|
183
|
+
"title": "API 키",
|
|
184
|
+
"subtitle": "프로그래밍 방식 접근용 개인 키. x-api-key 헤더로 전송합니다.",
|
|
185
|
+
"name": "이름",
|
|
186
|
+
"namePlaceholder": "예: CI 파이프라인",
|
|
187
|
+
"create": "키 생성",
|
|
188
|
+
"keyColumn": "키",
|
|
189
|
+
"created": "생성일",
|
|
190
|
+
"untitled": "제목 없음",
|
|
191
|
+
"revoke": "폐기",
|
|
192
|
+
"revoked": "키를 폐기했습니다",
|
|
193
|
+
"revokeFailed": "키 폐기에 실패했습니다",
|
|
194
|
+
"empty": "아직 API 키가 없습니다.",
|
|
195
|
+
"total": "API 키 {count}개",
|
|
196
|
+
"createdTitle": "API 키 생성됨",
|
|
197
|
+
"createdDesc": "지금 복사하세요 — 다시 볼 수 없습니다.",
|
|
198
|
+
"copy": "복사",
|
|
199
|
+
"copied": "클립보드에 복사됨",
|
|
200
|
+
"done": "완료"
|
|
201
|
+
},
|
|
202
|
+
"general": {
|
|
203
|
+
"tab": "일반",
|
|
204
|
+
"brandingTitle": "브랜딩",
|
|
205
|
+
"brandingDesc": "브라우저와 앱 전반에 표시되는 사이트 이름과 아이콘입니다.",
|
|
206
|
+
"siteName": "사이트 이름",
|
|
207
|
+
"siteNamePlaceholder": "My App",
|
|
208
|
+
"siteNameHint": "브라우저 탭 제목과 앱 이름이 쓰이는 곳에 사용됩니다.",
|
|
209
|
+
"favicon": "브라우저 아이콘 (favicon)",
|
|
210
|
+
"faviconHint": "SVG(권장) 또는 정사각 PNG 512×512, 1 MB 이하. 모든 페이지에 적용됩니다.",
|
|
211
|
+
"iconSaved": "아이콘을 변경했습니다.",
|
|
212
|
+
"iconError": "아이콘 업로드에 실패했습니다.",
|
|
213
|
+
"iconTooBig": "아이콘은 1 MB 이하여야 합니다.",
|
|
214
|
+
"brandColor": "브랜드 색상",
|
|
215
|
+
"appearanceTitle": "화면 테마",
|
|
216
|
+
"appearanceDesc": "앱의 기본 색상과 모서리 형태를 설정합니다. 저장하면 모든 화면에 적용됩니다.",
|
|
217
|
+
"themePreset": "기본 테마",
|
|
218
|
+
"featuredThemes": "주요 테마",
|
|
219
|
+
"moreThemes": "테마 더 보기",
|
|
220
|
+
"hideThemes": "테마 접기",
|
|
221
|
+
"quickSettings": "빠른 설정",
|
|
222
|
+
"accentColor": "브랜드 색상",
|
|
223
|
+
"accentColorHint": "선택한 테마의 주요 색상을 바꿉니다. 비우면 테마 색상을 사용합니다.",
|
|
224
|
+
"clearColor": "브랜드 색상 지우기",
|
|
225
|
+
"radius": "모서리 형태",
|
|
226
|
+
"radiusDefault": "앱 기본값",
|
|
227
|
+
"radiusNone": "각지게",
|
|
228
|
+
"radiusSmall": "작게",
|
|
229
|
+
"radiusMedium": "보통",
|
|
230
|
+
"radiusLarge": "크게",
|
|
231
|
+
"advanced": "세부 색상 설정",
|
|
232
|
+
"advancedHide": "세부 설정 닫기",
|
|
233
|
+
"resetTheme": "세부 색상 지우기",
|
|
234
|
+
"saveTheme": "변경 저장",
|
|
235
|
+
"restoreDefaults": "기본값으로 복원",
|
|
236
|
+
"preview": "미리보기",
|
|
237
|
+
"previewLight": "라이트",
|
|
238
|
+
"previewDark": "다크",
|
|
239
|
+
"previewApp": "워크스페이스",
|
|
240
|
+
"previewNav": "대시보드",
|
|
241
|
+
"previewSettings": "설정",
|
|
242
|
+
"previewHeading": "테마 미리보기",
|
|
243
|
+
"previewBody": "저장하기 전에 색상과 모서리 형태를 확인할 수 있습니다.",
|
|
244
|
+
"previewPrimary": "저장",
|
|
245
|
+
"previewOutline": "취소",
|
|
246
|
+
"previewBadge": "사용 중",
|
|
247
|
+
"themeTokens": {
|
|
248
|
+
"background": "배경",
|
|
249
|
+
"card": "카드",
|
|
250
|
+
"foreground": "전경(글자)",
|
|
251
|
+
"mutedForeground": "흐린 글자",
|
|
252
|
+
"border": "테두리",
|
|
253
|
+
"secondary": "보조",
|
|
254
|
+
"accent": "강조",
|
|
255
|
+
"primary": "기본",
|
|
256
|
+
"primaryForeground": "기본 글자"
|
|
257
|
+
},
|
|
258
|
+
"infoTitle": "사이트 정보",
|
|
259
|
+
"infoDesc": "메타 태그·푸터·링크에 쓰이는 선택 항목입니다.",
|
|
260
|
+
"description": "설명",
|
|
261
|
+
"descriptionPlaceholder": "사이트를 한 줄로 소개",
|
|
262
|
+
"supportEmail": "지원 이메일",
|
|
263
|
+
"footerText": "푸터 문구",
|
|
264
|
+
"termsUrl": "약관 URL",
|
|
265
|
+
"privacyUrl": "개인정보 URL",
|
|
266
|
+
"locale": "기본 언어",
|
|
267
|
+
"timezone": "기본 시간대",
|
|
268
|
+
"opsTitle": "운영",
|
|
269
|
+
"maintenance": "유지보수 모드",
|
|
270
|
+
"maintenanceDesc": "관리자 외 사용자에게 점검 페이지를 표시합니다.",
|
|
271
|
+
"allowSignup": "공개 가입 허용",
|
|
272
|
+
"allowSignupDesc": "끄면 초대 전용으로 전환됩니다.",
|
|
273
|
+
"save": "변경 저장",
|
|
274
|
+
"saving": "저장 중…",
|
|
275
|
+
"saved": "일반 설정을 저장했습니다.",
|
|
276
|
+
"saveError": "설정 저장에 실패했습니다."
|
|
277
|
+
},
|
|
278
|
+
"settings": {
|
|
279
|
+
"tab": "인증",
|
|
280
|
+
"title": "설정",
|
|
281
|
+
"subtitle": "활성화된 인증 기능과 구성 방법입니다.",
|
|
282
|
+
"authFeaturesTitle": "인증 기능",
|
|
283
|
+
"authFeaturesSubtitle": "로그인 수단과 보안 기능을 켜고 끕니다. 변경은 즉시 적용됩니다.",
|
|
284
|
+
"serverConfigTitle": "서버 구성",
|
|
285
|
+
"serverConfigSubtitle": "환경변수로 설정되어 시작 시 적용됩니다(읽기 전용).",
|
|
286
|
+
"enabled": "활성",
|
|
287
|
+
"disabled": "비활성",
|
|
288
|
+
"configuredVia": "{env} 로 구성",
|
|
289
|
+
"manage": "관리",
|
|
290
|
+
"saved": "설정을 변경했습니다",
|
|
291
|
+
"saveFailed": "설정 변경에 실패했습니다",
|
|
292
|
+
"emailPassword": "이메일·비밀번호",
|
|
293
|
+
"emailPasswordDesc": "이메일과 비밀번호로 로그인.",
|
|
294
|
+
"emailVerification": "이메일 인증",
|
|
295
|
+
"emailVerificationDesc": "신규 가입 시 이메일 확인 요구.",
|
|
296
|
+
"signupApproval": "회원가입 승인",
|
|
297
|
+
"signupApprovalDesc": "신규 자가 가입 사용자를 관리자가 승인해야 로그인할 수 있습니다. 끄면 이후 가입부터 자동 승인됩니다.",
|
|
298
|
+
"magicLink": "매직 링크",
|
|
299
|
+
"magicLinkDesc": "이메일 링크로 비밀번호 없이 로그인.",
|
|
300
|
+
"emailOtp": "이메일 일회용 코드",
|
|
301
|
+
"emailOtpDesc": "이메일로 받은 코드로 비밀번호 없이 로그인.",
|
|
302
|
+
"username": "사용자명 로그인",
|
|
303
|
+
"usernameDesc": "이메일 대신 사용자명으로 로그인.",
|
|
304
|
+
"multiSession": "다중 계정 세션",
|
|
305
|
+
"multiSessionDesc": "한 브라우저에서 여러 계정에 로그인하고 전환.",
|
|
306
|
+
"phoneNumber": "전화번호",
|
|
307
|
+
"phoneNumberDesc": "SMS로 전화번호 등록·인증(실발송은 프로바이더 연동 필요).",
|
|
308
|
+
"apiKey": "API 키",
|
|
309
|
+
"apiKeyDesc": "프로그래밍 방식 접근을 위한 개인 API 키 발급을 허용.",
|
|
310
|
+
"passkey": "패스키",
|
|
311
|
+
"passkeyDesc": "비밀번호 없는 로그인을 위한 패스키 등록을 허용.",
|
|
312
|
+
"organization": "조직",
|
|
313
|
+
"organizationDesc": "멤버와 이메일 초대를 갖춘 멀티테넌트 조직.",
|
|
314
|
+
"oidcProvider": "OIDC 제공자",
|
|
315
|
+
"oidcProviderDesc": "다른 앱이 이 앱으로 로그인하도록 신원 제공자 역할.",
|
|
316
|
+
"twoFactor": "2단계 인증",
|
|
317
|
+
"twoFactorDesc": "TOTP 2차 인증 + 백업 코드.",
|
|
318
|
+
"require2fa": "2FA 필수화",
|
|
319
|
+
"require2faDesc": "모든 사용자가 가입 후 2FA를 등록해야 앱을 사용할 수 있습니다.",
|
|
320
|
+
"socialProviders": "소셜 로그인",
|
|
321
|
+
"socialProvidersDesc": "OAuth 로그인 (Google, GitHub).",
|
|
322
|
+
"breachCheck": "유출 비밀번호 검사",
|
|
323
|
+
"breachCheckDesc": "유출된 비밀번호 거부 (Have I Been Pwned).",
|
|
324
|
+
"accountDeletion": "계정 삭제",
|
|
325
|
+
"accountDeletionDesc": "사용자가 자신의 계정을 삭제.",
|
|
326
|
+
"auditLog": "감사 로그",
|
|
327
|
+
"auditLogDesc": "보안 관련 동작 기록.",
|
|
328
|
+
"signInTitle": "로그인 수단",
|
|
329
|
+
"signInSubtitle": "사용자가 인증하는 방법. 각 수단을 켜고 끄며, 소셜 로그인은 팝업에서 자격증명을 입력합니다.",
|
|
330
|
+
"emailTitle": "이메일",
|
|
331
|
+
"emailSubtitle": "메일 발송(SMTP) 설정과 이메일 인증 정책.",
|
|
332
|
+
"securityTitle": "보안·정책",
|
|
333
|
+
"securitySubtitle": "2단계 인증, 유출 비밀번호 거부, 계정 삭제, 감사 로그.",
|
|
334
|
+
"advancedTitle": "고급",
|
|
335
|
+
"advancedSubtitle": "API 키, 조직, OIDC 공급자(자체 IdP) 역할.",
|
|
336
|
+
"socialTitle": "소셜 로그인",
|
|
337
|
+
"socialSubtitle": "OAuth 자격증명을 붙여넣으면 해당 provider 로그인이 켜집니다. 재시작 없이 즉시 적용되며, 비밀은 암호화 저장됩니다.",
|
|
338
|
+
"smtpTitle": "SMTP(이메일 발송)",
|
|
339
|
+
"smtpSubtitle": "로그인 링크·인증·초대 발송용. 즉시 적용됩니다.",
|
|
340
|
+
"google": "Google",
|
|
341
|
+
"github": "GitHub",
|
|
342
|
+
"clientId": "Client ID",
|
|
343
|
+
"clientSecret": "Client secret",
|
|
344
|
+
"redirectUri": "Redirect URI",
|
|
345
|
+
"secretSet": "•••••••• (저장됨 — 비우면 유지)",
|
|
346
|
+
"save": "저장",
|
|
347
|
+
"cancel": "취소",
|
|
348
|
+
"configure": "설정",
|
|
349
|
+
"notConfigured": "미설정",
|
|
350
|
+
"configuredOff": "설정됨(꺼짐)",
|
|
351
|
+
"socialDialogDesc": "provider 콘솔의 OAuth client ID와 secret을 붙여넣고 사용을 켜세요. 즉시 적용됩니다.",
|
|
352
|
+
"smtpEnabled": "사용",
|
|
353
|
+
"smtpHost": "호스트",
|
|
354
|
+
"smtpPort": "포트",
|
|
355
|
+
"smtpUser": "사용자",
|
|
356
|
+
"smtpPass": "비밀번호",
|
|
357
|
+
"smtpFrom": "발신 주소",
|
|
358
|
+
"smtpSecure": "TLS(보안)",
|
|
359
|
+
"addProvider": "공급자 추가",
|
|
360
|
+
"provider": "공급자",
|
|
361
|
+
"remove": "삭제",
|
|
362
|
+
"smtpConfigure": "SMTP 설정",
|
|
363
|
+
"edit": "편집",
|
|
364
|
+
"close": "닫기",
|
|
365
|
+
"back": "뒤로",
|
|
366
|
+
"noProviders": "아직 추가된 공급자가 없습니다.",
|
|
367
|
+
"socialCardDesc": "Google, GitHub, Apple 등 OAuth 공급자로 로그인."
|
|
368
|
+
},
|
|
369
|
+
"dashboard": {
|
|
370
|
+
"title": "대시보드",
|
|
371
|
+
"welcome": "{name}님, 환영합니다.",
|
|
372
|
+
"yourRole": "내 권한",
|
|
373
|
+
"signedInAs": "로그인 계정",
|
|
374
|
+
"unverifiedUsers": "미인증 사용자"
|
|
375
|
+
},
|
|
376
|
+
"audit": {
|
|
377
|
+
"title": "감사 로그",
|
|
378
|
+
"when": "시각",
|
|
379
|
+
"actor": "수행자",
|
|
380
|
+
"action": "동작",
|
|
381
|
+
"target": "대상",
|
|
382
|
+
"ip": "IP",
|
|
383
|
+
"empty": "감사 기록이 없습니다.",
|
|
384
|
+
"loadFailed": "감사 로그를 불러오지 못했습니다",
|
|
385
|
+
"total": "항목 {count}개",
|
|
386
|
+
"searchField": "수행자·대상"
|
|
387
|
+
},
|
|
388
|
+
"users": {
|
|
389
|
+
"title": "회원",
|
|
390
|
+
"search": "이메일로 검색…",
|
|
391
|
+
"name": "이름",
|
|
392
|
+
"email": "이메일",
|
|
393
|
+
"role": "권한",
|
|
394
|
+
"status": "상태",
|
|
395
|
+
"active": "활성",
|
|
396
|
+
"banned": "차단됨",
|
|
397
|
+
"makeUser": "일반 사용자로",
|
|
398
|
+
"makeAdmin": "관리자로",
|
|
399
|
+
"ban": "차단",
|
|
400
|
+
"unban": "차단 해제",
|
|
401
|
+
"empty": "회원이 없습니다.",
|
|
402
|
+
"total": "회원 {count}명",
|
|
403
|
+
"previous": "이전",
|
|
404
|
+
"next": "다음",
|
|
405
|
+
"roleSet": "권한을 {role}(으)로 변경했습니다",
|
|
406
|
+
"userBanned": "회원을 차단했습니다",
|
|
407
|
+
"userUnbanned": "차단을 해제했습니다",
|
|
408
|
+
"loadFailed": "회원 목록을 불러오지 못했습니다",
|
|
409
|
+
"actionFailed": "작업에 실패했습니다",
|
|
410
|
+
"addUser": "사용자 추가",
|
|
411
|
+
"createTitle": "사용자 생성",
|
|
412
|
+
"password": "비밀번호",
|
|
413
|
+
"create": "생성",
|
|
414
|
+
"cancel": "취소",
|
|
415
|
+
"close": "닫기",
|
|
416
|
+
"roleAdmin": "관리자",
|
|
417
|
+
"roleUser": "사용자",
|
|
418
|
+
"roleModerator": "중재자",
|
|
419
|
+
"userCreated": "사용자를 생성했습니다",
|
|
420
|
+
"setPassword": "비밀번호 설정",
|
|
421
|
+
"newPassword": "새 비밀번호",
|
|
422
|
+
"save": "저장",
|
|
423
|
+
"passwordSet": "비밀번호를 변경했습니다",
|
|
424
|
+
"delete": "삭제",
|
|
425
|
+
"deleteTitle": "사용자 삭제",
|
|
426
|
+
"deleteConfirm": "{email} 사용자를 삭제할까요? 되돌릴 수 없습니다.",
|
|
427
|
+
"userDeleted": "사용자를 삭제했습니다",
|
|
428
|
+
"confirmPassword": "비밀번호 확인",
|
|
429
|
+
"confirmNewPassword": "새 비밀번호 확인",
|
|
430
|
+
"passwordMismatch": "비밀번호가 일치하지 않습니다",
|
|
431
|
+
"edit": "편집",
|
|
432
|
+
"editTitle": "사용자 편집",
|
|
433
|
+
"userUpdated": "사용자를 수정했습니다",
|
|
434
|
+
"impersonate": "이 사용자로 로그인",
|
|
435
|
+
"banTitle": "사용자 차단",
|
|
436
|
+
"banReason": "사유 (선택)",
|
|
437
|
+
"banExpiryDays": "만료 (일, 선택)",
|
|
438
|
+
"banExpiry": "차단 만료",
|
|
439
|
+
"banPermanent": "영구 차단",
|
|
440
|
+
"joined": "가입일",
|
|
441
|
+
"stopImpersonating": "대리 로그인 종료",
|
|
442
|
+
"impersonatingAs": "{email} 사용자로 대리 로그인 중",
|
|
443
|
+
"manage": "관리",
|
|
444
|
+
"revokeSession": "종료",
|
|
445
|
+
"verified": "인증됨",
|
|
446
|
+
"unverified": "미인증",
|
|
447
|
+
"sendVerification": "인증 메일 보내기",
|
|
448
|
+
"verificationSent": "인증 메일을 보냈습니다",
|
|
449
|
+
"markVerified": "인증됨으로 표시",
|
|
450
|
+
"markedVerified": "인증 처리했습니다",
|
|
451
|
+
"sendResetEmail": "비밀번호 재설정 메일 보내기",
|
|
452
|
+
"resetEmailSent": "재설정 메일을 보냈습니다",
|
|
453
|
+
"sendVerificationOnCreate": "인증 메일 보내기",
|
|
454
|
+
"pendingApproval": "승인 대기",
|
|
455
|
+
"approve": "회원가입 승인",
|
|
456
|
+
"approved": "회원가입을 승인했습니다"
|
|
457
|
+
},
|
|
458
|
+
"sessions": {
|
|
459
|
+
"title": "활성 세션",
|
|
460
|
+
"device": "기기",
|
|
461
|
+
"ip": "IP",
|
|
462
|
+
"since": "시작",
|
|
463
|
+
"revoke": "종료",
|
|
464
|
+
"unknown": "알 수 없음",
|
|
465
|
+
"empty": "활성 세션이 없습니다.",
|
|
466
|
+
"revoked": "세션을 종료했습니다",
|
|
467
|
+
"loadFailed": "세션을 불러오지 못했습니다",
|
|
468
|
+
"revokeFailed": "세션 종료에 실패했습니다",
|
|
469
|
+
"status": "상태",
|
|
470
|
+
"current": "현재",
|
|
471
|
+
"signOutOthers": "다른 세션 모두 종료",
|
|
472
|
+
"accountsTitle": "로그인된 계정",
|
|
473
|
+
"switchAccount": "전환",
|
|
474
|
+
"switchFailed": "계정 전환에 실패했습니다",
|
|
475
|
+
"othersRevoked": "다른 세션을 모두 종료했습니다",
|
|
476
|
+
"total": "세션 {count}개"
|
|
477
|
+
},
|
|
478
|
+
"adminSessions": {
|
|
479
|
+
"title": "사용자 세션",
|
|
480
|
+
"user": "사용자",
|
|
481
|
+
"device": "기기",
|
|
482
|
+
"ip": "IP",
|
|
483
|
+
"since": "시작",
|
|
484
|
+
"expires": "만료",
|
|
485
|
+
"impersonated": "임퍼스네이트",
|
|
486
|
+
"revoke": "세션 종료",
|
|
487
|
+
"revokeAll": "사용자 세션 모두 종료",
|
|
488
|
+
"unknown": "알 수 없음",
|
|
489
|
+
"empty": "활성 세션이 없습니다.",
|
|
490
|
+
"revoked": "세션을 종료했습니다",
|
|
491
|
+
"allRevoked": "사용자의 세션을 모두 종료했습니다",
|
|
492
|
+
"loadFailed": "세션을 불러오지 못했습니다",
|
|
493
|
+
"total": "세션 {count}개"
|
|
494
|
+
},
|
|
495
|
+
"account": {
|
|
496
|
+
"title": "계정",
|
|
497
|
+
"profile": "프로필",
|
|
498
|
+
"name": "이름",
|
|
499
|
+
"username": "사용자명",
|
|
500
|
+
"phone": "전화번호",
|
|
501
|
+
"phoneCode": "인증 코드",
|
|
502
|
+
"phoneSendCode": "코드 전송",
|
|
503
|
+
"phoneVerify": "인증",
|
|
504
|
+
"phoneCodeSent": "코드를 전송했습니다",
|
|
505
|
+
"phoneVerified": "전화번호 인증됨",
|
|
506
|
+
"email": "이메일",
|
|
507
|
+
"role": "권한",
|
|
508
|
+
"changePassword": "비밀번호 변경",
|
|
509
|
+
"currentPassword": "현재 비밀번호",
|
|
510
|
+
"newPassword": "새 비밀번호",
|
|
511
|
+
"update": "비밀번호 변경",
|
|
512
|
+
"updating": "변경 중…",
|
|
513
|
+
"changed": "비밀번호를 변경했습니다",
|
|
514
|
+
"changeFailed": "비밀번호 변경에 실패했습니다",
|
|
515
|
+
"nav": {
|
|
516
|
+
"profile": "프로필",
|
|
517
|
+
"security": "보안",
|
|
518
|
+
"connected": "연결",
|
|
519
|
+
"sessions": "세션",
|
|
520
|
+
"apiKeys": "API 키",
|
|
521
|
+
"danger": "위험 영역"
|
|
522
|
+
},
|
|
523
|
+
"twoFactorTitle": "2단계 인증",
|
|
524
|
+
"twoFactorDesc": "로그인 시 인증 앱의 일회용 코드를 추가로 요구합니다.",
|
|
525
|
+
"twoFactorOn": "사용 중",
|
|
526
|
+
"twoFactorOff": "사용 안 함",
|
|
527
|
+
"enable": "사용",
|
|
528
|
+
"disable": "해제",
|
|
529
|
+
"password": "비밀번호",
|
|
530
|
+
"scanHint": "인증 앱에 등록한 뒤 6자리 코드를 입력하세요.",
|
|
531
|
+
"setupKey": "설정 키 (otpauth URI)",
|
|
532
|
+
"backupCodes": "백업 코드 — 안전한 곳에 보관하세요",
|
|
533
|
+
"backupCodesHint": "인증 앱을 분실했을 때 각 코드는 한 번씩 사용할 수 있습니다. 지금 저장하세요 — 다시 표시되지 않습니다.",
|
|
534
|
+
"downloadBackupCodes": "다운로드",
|
|
535
|
+
"regenerateBackupCodes": "백업 코드 재발급",
|
|
536
|
+
"regenerateBackupCodesHint": "새로 발급하면 이전 백업 코드는 무효화됩니다.",
|
|
537
|
+
"backupCodesRegenerated": "새 백업 코드를 발급했습니다",
|
|
538
|
+
"code": "인증 코드",
|
|
539
|
+
"verify": "확인",
|
|
540
|
+
"twoFactorEnabled": "2단계 인증을 사용합니다",
|
|
541
|
+
"twoFactorDisabled": "2단계 인증을 해제했습니다",
|
|
542
|
+
"connectedTitle": "연결된 계정",
|
|
543
|
+
"connectedDesc": "소셜 계정을 연결하면 해당 계정으로 로그인할 수 있습니다.",
|
|
544
|
+
"connect": "연결",
|
|
545
|
+
"disconnect": "연결 해제",
|
|
546
|
+
"connected": "연결됨",
|
|
547
|
+
"notConnected": "연결 안 됨",
|
|
548
|
+
"disconnected": "연결을 해제했습니다",
|
|
549
|
+
"dangerTitle": "계정 삭제",
|
|
550
|
+
"dangerDesc": "계정과 모든 관련 데이터를 영구히 삭제합니다.",
|
|
551
|
+
"deleteAccount": "계정 삭제",
|
|
552
|
+
"deleteAccountConfirm": "계정이 영구히 삭제되며 되돌릴 수 없습니다. 확인하려면 비밀번호를 입력하세요.",
|
|
553
|
+
"accountDeleted": "계정을 삭제했습니다",
|
|
554
|
+
"save": "변경 저장",
|
|
555
|
+
"saved": "프로필을 저장했습니다",
|
|
556
|
+
"saveFailed": "프로필 저장에 실패했습니다",
|
|
557
|
+
"saving": "저장 중…",
|
|
558
|
+
"changeEmail": "이메일 변경",
|
|
559
|
+
"emailChanged": "이메일 주소를 변경했습니다",
|
|
560
|
+
"emailChangePending": "현재 메일함에서 변경을 승인해 주세요",
|
|
561
|
+
"verified": "인증됨",
|
|
562
|
+
"unverified": "미인증"
|
|
563
|
+
}
|
|
564
|
+
}
|
|
@@ -1,44 +1,45 @@
|
|
|
1
1
|
import { page } from "$app/state";
|
|
2
2
|
import { site } from "$lib/site.svelte";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveLocale, type Locale, type Messages } from "./messages";
|
|
4
4
|
|
|
5
5
|
export type I18nContext = { readonly t: Messages; readonly locale: Locale };
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
interface I18nPageData {
|
|
8
|
+
locale?: string;
|
|
9
|
+
messages?: Messages;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function data(): I18nPageData {
|
|
13
|
+
return page.data as I18nPageData;
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
export function getI18n(): I18nContext {
|
|
12
17
|
return {
|
|
13
18
|
get t() {
|
|
14
|
-
|
|
19
|
+
const messages = data().messages;
|
|
20
|
+
if (!messages) throw new Error("Locale messages were not loaded by the root layout");
|
|
21
|
+
return messages;
|
|
15
22
|
},
|
|
16
23
|
get locale() {
|
|
17
|
-
return
|
|
24
|
+
return resolveLocale(data().locale);
|
|
18
25
|
},
|
|
19
26
|
};
|
|
20
27
|
}
|
|
21
28
|
|
|
22
|
-
// Fill {name}-style placeholders in a message.
|
|
23
29
|
export function fmt(template: string, values: Record<string, string | number>): string {
|
|
24
30
|
return template.replace(/\{(\w+)\}/g, (match, key: string) =>
|
|
25
31
|
key in values ? String(values[key]) : match,
|
|
26
32
|
);
|
|
27
33
|
}
|
|
28
34
|
|
|
29
|
-
// Render a timestamp in the site's configured timezone when an admin has set one
|
|
30
|
-
// (Settings → General), otherwise in the viewer's own browser timezone. Used
|
|
31
|
-
// across all pages.
|
|
32
35
|
export function formatDateTime(value: string | Date): string {
|
|
33
36
|
const date = new Date(value);
|
|
34
37
|
const timeZone = site.value.timezone || undefined;
|
|
35
38
|
if (timeZone) {
|
|
36
|
-
// A misconfigured timezone would throw a RangeError — fall back to the
|
|
37
|
-
// browser default rather than breaking the page.
|
|
38
39
|
try {
|
|
39
40
|
return date.toLocaleString(undefined, { timeZone });
|
|
40
41
|
} catch {
|
|
41
|
-
|
|
42
|
+
// Fall through to the browser timezone when an administrator entered an invalid value.
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
return date.toLocaleString();
|