@strapi/admin 4.1.4-alpha.2 → 4.1.5
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/admin/src/components/GuidedTour/Homepage/index.js +13 -3
- package/admin/src/components/GuidedTour/Modal/index.js +4 -1
- package/admin/src/components/GuidedTour/layout.js +9 -0
- package/admin/src/components/PrivateRoute/index.js +23 -17
- package/admin/src/content-manager/components/Inputs/index.js +3 -4
- package/admin/src/pages/AuthPage/index.js +28 -5
- package/admin/src/pages/HomePage/SocialLinks.js +0 -3
- package/admin/src/translations/en.json +2 -2
- package/admin/src/translations/ja.json +2 -2
- package/admin/src/translations/ko.json +2 -2
- package/admin/src/translations/vi.json +30 -4
- package/build/{4362.5c92d240.chunk.js → 4362.cf5b578d.chunk.js} +1 -1
- package/build/Admin-authenticatedApp.a23bde1b.chunk.js +1 -0
- package/build/Admin_homePage.fd1fc572.chunk.js +1 -0
- package/build/content-manager.31be1448.chunk.js +1 -0
- package/build/en-json.2bc27a3d.chunk.js +1 -0
- package/build/index.html +1 -1
- package/build/ja-json.e13f04e8.chunk.js +1 -0
- package/build/ko-json.2200c9c9.chunk.js +1 -0
- package/build/{main.7fa5bc38.js → main.25a14c1f.js} +2 -2
- package/build/{main.7fa5bc38.js.LICENSE.txt → main.25a14c1f.js.LICENSE.txt} +0 -0
- package/build/{runtime~main.7bb09ab1.js → runtime~main.385dfcf4.js} +1 -1
- package/build/{upload-translation-en-json.c3373c8d.chunk.js → upload-translation-en-json.c334dd82.chunk.js} +1 -1
- package/build/vi-json.1e850069.chunk.js +1 -0
- package/package.json +5 -5
- package/build/Admin-authenticatedApp.6d27d55a.chunk.js +0 -1
- package/build/Admin_homePage.964ff5d7.chunk.js +0 -1
- package/build/content-manager.e1189026.chunk.js +0 -1
- package/build/en-json.086acf41.chunk.js +0 -1
- package/build/ja-json.46e29f04.chunk.js +0 -1
- package/build/ko-json.dd36fdc0.chunk.js +0 -1
- package/build/vi-json.55a11ac0.chunk.js +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { useGuidedTour } from '@strapi/helper-plugin';
|
|
2
|
+
import { useGuidedTour, useTracking } from '@strapi/helper-plugin';
|
|
3
3
|
import { useIntl } from 'react-intl';
|
|
4
4
|
import { Stack } from '@strapi/design-system/Stack';
|
|
5
5
|
import { Flex } from '@strapi/design-system/Flex';
|
|
@@ -14,12 +14,17 @@ import layout from '../layout';
|
|
|
14
14
|
const GuidedTourHomepage = () => {
|
|
15
15
|
const { guidedTourState, setSkipped } = useGuidedTour();
|
|
16
16
|
const { formatMessage } = useIntl();
|
|
17
|
+
const { trackUsage } = useTracking();
|
|
17
18
|
|
|
18
19
|
const sections = Object.entries(layout).map(([key, val]) => ({
|
|
19
20
|
key,
|
|
20
21
|
title: val.home.title,
|
|
21
22
|
content: (
|
|
22
|
-
<LinkButton
|
|
23
|
+
<LinkButton
|
|
24
|
+
onClick={() => trackUsage(val.home.trackingEvent)}
|
|
25
|
+
to={val.home.cta.target}
|
|
26
|
+
endIcon={<ArrowRight />}
|
|
27
|
+
>
|
|
23
28
|
{formatMessage(val.home.cta.title)}
|
|
24
29
|
</LinkButton>
|
|
25
30
|
),
|
|
@@ -32,6 +37,11 @@ const GuidedTourHomepage = () => {
|
|
|
32
37
|
|
|
33
38
|
const activeSection = enrichedSections.find(section => !section.isDone)?.key;
|
|
34
39
|
|
|
40
|
+
const handleSkip = () => {
|
|
41
|
+
setSkipped(true)
|
|
42
|
+
trackUsage('didSkipGuidedtour');
|
|
43
|
+
}
|
|
44
|
+
|
|
35
45
|
return (
|
|
36
46
|
<Box
|
|
37
47
|
hasRadius
|
|
@@ -52,7 +62,7 @@ const GuidedTourHomepage = () => {
|
|
|
52
62
|
<StepperHomepage sections={sections} currentSectionKey={activeSection} />
|
|
53
63
|
</Stack>
|
|
54
64
|
<Flex justifyContent="flex-end">
|
|
55
|
-
<Button variant="tertiary" onClick={
|
|
65
|
+
<Button variant="tertiary" onClick={handleSkip}>
|
|
56
66
|
{formatMessage({ id: 'app.components.GuidedTour.skip', defaultMessage: 'Skip the tour' })}
|
|
57
67
|
</Button>
|
|
58
68
|
</Flex>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useState, useReducer } from 'react';
|
|
2
2
|
import at from 'lodash/at';
|
|
3
|
-
import { useGuidedTour } from '@strapi/helper-plugin';
|
|
3
|
+
import { useGuidedTour, useTracking } from '@strapi/helper-plugin';
|
|
4
4
|
import layout from '../layout';
|
|
5
5
|
import Modal from './components/Modal';
|
|
6
6
|
import reducer, { initialState } from './reducer';
|
|
@@ -20,6 +20,7 @@ const GuidedTourModal = () => {
|
|
|
20
20
|
{ stepContent, sectionIndex, stepIndex, hasSectionAfter, hasStepAfter },
|
|
21
21
|
dispatch,
|
|
22
22
|
] = useReducer(reducer, initialState);
|
|
23
|
+
const { trackUsage } = useTracking();
|
|
23
24
|
|
|
24
25
|
useEffect(() => {
|
|
25
26
|
if (!currentStep) {
|
|
@@ -56,6 +57,7 @@ const GuidedTourModal = () => {
|
|
|
56
57
|
|
|
57
58
|
const handleCtaClick = () => {
|
|
58
59
|
setStepState(currentStep, true);
|
|
60
|
+
trackUsage(stepContent.trackingEvent);
|
|
59
61
|
|
|
60
62
|
setCurrentStep(null);
|
|
61
63
|
};
|
|
@@ -63,6 +65,7 @@ const GuidedTourModal = () => {
|
|
|
63
65
|
const handleSkip = () => {
|
|
64
66
|
setSkipped(true);
|
|
65
67
|
setCurrentStep(null);
|
|
68
|
+
trackUsage('didSkipGuidedtour');
|
|
66
69
|
};
|
|
67
70
|
|
|
68
71
|
if (isVisible && stepContent) {
|
|
@@ -13,6 +13,7 @@ const layout = {
|
|
|
13
13
|
type: 'REDIRECT',
|
|
14
14
|
target: '/plugins/content-type-builder',
|
|
15
15
|
},
|
|
16
|
+
trackingEvent: 'didClickGuidedTourHomepageContentTypeBuilder',
|
|
16
17
|
},
|
|
17
18
|
create: {
|
|
18
19
|
title: {
|
|
@@ -31,6 +32,7 @@ const layout = {
|
|
|
31
32
|
},
|
|
32
33
|
type: 'CLOSE',
|
|
33
34
|
},
|
|
35
|
+
trackingEvent: 'didClickGuidedTourStep1CollectionType',
|
|
34
36
|
},
|
|
35
37
|
success: {
|
|
36
38
|
title: {
|
|
@@ -49,6 +51,7 @@ const layout = {
|
|
|
49
51
|
type: 'REDIRECT',
|
|
50
52
|
target: '/content-manager',
|
|
51
53
|
},
|
|
54
|
+
trackingEvent: 'didCreateGuidedTourCollectionType',
|
|
52
55
|
},
|
|
53
56
|
},
|
|
54
57
|
contentManager: {
|
|
@@ -65,6 +68,7 @@ const layout = {
|
|
|
65
68
|
type: 'REDIRECT',
|
|
66
69
|
target: '/content-manager',
|
|
67
70
|
},
|
|
71
|
+
trackingEvent: 'didClickGuidedTourHomepageContentManager',
|
|
68
72
|
},
|
|
69
73
|
create: {
|
|
70
74
|
title: {
|
|
@@ -83,6 +87,7 @@ const layout = {
|
|
|
83
87
|
},
|
|
84
88
|
type: 'CLOSE',
|
|
85
89
|
},
|
|
90
|
+
trackingEvent: 'didClickGuidedTourStep2ContentManager',
|
|
86
91
|
},
|
|
87
92
|
success: {
|
|
88
93
|
title: {
|
|
@@ -101,6 +106,7 @@ const layout = {
|
|
|
101
106
|
type: 'REDIRECT',
|
|
102
107
|
target: '/settings/api-tokens',
|
|
103
108
|
},
|
|
109
|
+
trackingEvent: 'didCreateGuidedTourEntry',
|
|
104
110
|
},
|
|
105
111
|
},
|
|
106
112
|
apiTokens: {
|
|
@@ -117,6 +123,7 @@ const layout = {
|
|
|
117
123
|
type: 'REDIRECT',
|
|
118
124
|
target: '/settings/api-tokens',
|
|
119
125
|
},
|
|
126
|
+
trackingEvent: 'didClickGuidedTourHomepageApiTokens',
|
|
120
127
|
},
|
|
121
128
|
create: {
|
|
122
129
|
title: {
|
|
@@ -135,6 +142,7 @@ const layout = {
|
|
|
135
142
|
},
|
|
136
143
|
type: 'CLOSE',
|
|
137
144
|
},
|
|
145
|
+
trackingEvent: 'didClickGuidedTourStep3ApiTokens',
|
|
138
146
|
},
|
|
139
147
|
success: {
|
|
140
148
|
title: {
|
|
@@ -146,6 +154,7 @@ const layout = {
|
|
|
146
154
|
defaultMessage:
|
|
147
155
|
"<p>See content in action by making an HTTP request:</p><ul><li><p>To this URL: <light>https://'<'YOUR_DOMAIN'>'/api/'<'YOUR_CT'>'</light></p></li><li><p>With the header: <light>Authorization: bearer '<'YOUR_API_TOKEN'>'</light></p></li></ul><p>For more ways to interact with content, see the <documentationLink>documentation</documentationLink>.</p>",
|
|
148
156
|
},
|
|
157
|
+
trackingEvent: 'didGenerateGuidedTourApiTokens',
|
|
149
158
|
},
|
|
150
159
|
},
|
|
151
160
|
};
|
|
@@ -8,28 +8,34 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import React, { memo } from 'react';
|
|
11
|
-
import { Redirect, Route } from 'react-router-dom';
|
|
11
|
+
import { Redirect, Route, useLocation } from 'react-router-dom';
|
|
12
12
|
import PropTypes from 'prop-types';
|
|
13
13
|
import { auth } from '@strapi/helper-plugin';
|
|
14
14
|
|
|
15
15
|
/* eslint-disable react/jsx-curly-newline */
|
|
16
16
|
|
|
17
|
-
const PrivateRoute = ({ component: Component, path, ...rest }) =>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
17
|
+
const PrivateRoute = ({ component: Component, path, ...rest }) => {
|
|
18
|
+
const { pathname, search } = useLocation();
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Route
|
|
22
|
+
path={path}
|
|
23
|
+
render={props =>
|
|
24
|
+
auth.getToken() !== null ? (
|
|
25
|
+
<Component {...rest} {...props} />
|
|
26
|
+
) : (
|
|
27
|
+
<Redirect
|
|
28
|
+
to={{
|
|
29
|
+
pathname: '/auth/login',
|
|
30
|
+
search:
|
|
31
|
+
pathname !== '/' && `?redirectTo=${encodeURIComponent(`${pathname}${search}`)}`,
|
|
32
|
+
}}
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
/>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
33
39
|
|
|
34
40
|
PrivateRoute.propTypes = {
|
|
35
41
|
component: PropTypes.any.isRequired,
|
|
@@ -224,6 +224,8 @@ function Inputs({
|
|
|
224
224
|
attribute={fieldSchema}
|
|
225
225
|
autoComplete="new-password"
|
|
226
226
|
intlLabel={{ id: label, defaultMessage: label }}
|
|
227
|
+
// in case the default value of the boolean is null, attribute.default doesn't exist
|
|
228
|
+
isNullable={inputType === 'bool' && [null, undefined].includes(fieldSchema.default)}
|
|
227
229
|
description={description ? { id: description, defaultMessage: description } : null}
|
|
228
230
|
disabled={shouldDisableField}
|
|
229
231
|
error={errorId}
|
|
@@ -279,7 +281,4 @@ Inputs.propTypes = {
|
|
|
279
281
|
|
|
280
282
|
const Memoized = memo(Inputs, isEqual);
|
|
281
283
|
|
|
282
|
-
export default connect(
|
|
283
|
-
Memoized,
|
|
284
|
-
select
|
|
285
|
-
);
|
|
284
|
+
export default connect(Memoized, select);
|
|
@@ -14,7 +14,10 @@ import init from './init';
|
|
|
14
14
|
import { initialState, reducer } from './reducer';
|
|
15
15
|
|
|
16
16
|
const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
|
17
|
-
const {
|
|
17
|
+
const {
|
|
18
|
+
push,
|
|
19
|
+
location: { search },
|
|
20
|
+
} = useHistory();
|
|
18
21
|
const { changeLocale } = useLocalesProvider();
|
|
19
22
|
const { setSkipped } = useGuidedTour();
|
|
20
23
|
const { trackUsage } = useTracking();
|
|
@@ -119,7 +122,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
|
|
119
122
|
auth.setToken(token, body.rememberMe);
|
|
120
123
|
auth.setUserInfo(user, body.rememberMe);
|
|
121
124
|
|
|
122
|
-
|
|
125
|
+
redirectToPreviousLocation();
|
|
123
126
|
} catch (err) {
|
|
124
127
|
if (err.response) {
|
|
125
128
|
const errorMessage = get(
|
|
@@ -174,6 +177,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
|
|
174
177
|
if (isUserSuperAdmin) {
|
|
175
178
|
persistStateToLocaleStorage.setSkipped(false);
|
|
176
179
|
setSkipped(false);
|
|
180
|
+
trackUsage('didLaunchGuidedtour');
|
|
177
181
|
}
|
|
178
182
|
}
|
|
179
183
|
|
|
@@ -189,8 +193,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
|
|
189
193
|
return;
|
|
190
194
|
}
|
|
191
195
|
|
|
192
|
-
|
|
193
|
-
push('/');
|
|
196
|
+
redirectToPreviousLocation();
|
|
194
197
|
} catch (err) {
|
|
195
198
|
trackUsage('didNotCreateFirstAdmin');
|
|
196
199
|
|
|
@@ -238,6 +241,17 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
|
|
238
241
|
}
|
|
239
242
|
};
|
|
240
243
|
|
|
244
|
+
const redirectToPreviousLocation = () => {
|
|
245
|
+
if (authType === 'login') {
|
|
246
|
+
const redirectTo = query.get('redirectTo');
|
|
247
|
+
const redirectUrl = redirectTo ? decodeURIComponent(redirectTo) : '/';
|
|
248
|
+
|
|
249
|
+
push(redirectUrl);
|
|
250
|
+
} else {
|
|
251
|
+
push('/');
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
241
255
|
// Redirect the user to the login page if
|
|
242
256
|
// the endpoint does not exist or
|
|
243
257
|
// there is already an admin user oo
|
|
@@ -248,7 +262,16 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
|
|
248
262
|
|
|
249
263
|
// Redirect the user to the register-admin if it is the first user
|
|
250
264
|
if (!hasAdmin && authType !== 'register-admin') {
|
|
251
|
-
return
|
|
265
|
+
return (
|
|
266
|
+
<Redirect
|
|
267
|
+
to={{
|
|
268
|
+
pathname: '/auth/register-admin',
|
|
269
|
+
// Forward the `?redirectTo` from /auth/login
|
|
270
|
+
// /abc => /auth/login?redirectTo=%2Fabc => /auth/register-admin?redirectTo=%2Fabc
|
|
271
|
+
search,
|
|
272
|
+
}}
|
|
273
|
+
/>
|
|
274
|
+
);
|
|
252
275
|
}
|
|
253
276
|
|
|
254
277
|
return (
|
|
@@ -357,8 +357,8 @@
|
|
|
357
357
|
"app.components.PluginCard.compatible": "Compatible with your app",
|
|
358
358
|
"app.components.PluginCard.compatibleCommunity": "Compatible with the community",
|
|
359
359
|
"app.components.PluginCard.more-details": "More details",
|
|
360
|
-
"app.components.ToggleCheckbox.off-label": "
|
|
361
|
-
"app.components.ToggleCheckbox.on-label": "
|
|
360
|
+
"app.components.ToggleCheckbox.off-label": "False",
|
|
361
|
+
"app.components.ToggleCheckbox.on-label": "True",
|
|
362
362
|
"app.components.UpgradePlanModal.button": "Learn more",
|
|
363
363
|
"app.components.UpgradePlanModal.limit-reached": "You have reached the limit",
|
|
364
364
|
"app.components.UpgradePlanModal.text-ce": "Community Edition",
|
|
@@ -315,8 +315,8 @@
|
|
|
315
315
|
"app.components.PluginCard.compatible": "アプリとの互換性",
|
|
316
316
|
"app.components.PluginCard.compatibleCommunity": "コミュニティとの互換性",
|
|
317
317
|
"app.components.PluginCard.more-details": "詳細を見る",
|
|
318
|
-
"app.components.ToggleCheckbox.off-label": "
|
|
319
|
-
"app.components.ToggleCheckbox.on-label": "
|
|
318
|
+
"app.components.ToggleCheckbox.off-label": "False",
|
|
319
|
+
"app.components.ToggleCheckbox.on-label": "True",
|
|
320
320
|
"app.components.UpgradePlanModal.button": "Learn more",
|
|
321
321
|
"app.components.UpgradePlanModal.limit-reached": "You have reached the limit",
|
|
322
322
|
"app.components.UpgradePlanModal.text-ce": "Community Edition",
|
|
@@ -317,8 +317,8 @@
|
|
|
317
317
|
"app.components.PluginCard.compatible": "이 애플리케이션에 호환됩니다.",
|
|
318
318
|
"app.components.PluginCard.compatibleCommunity": "션뮤니티에 호환됩니다.",
|
|
319
319
|
"app.components.PluginCard.more-details": "[더보기]",
|
|
320
|
-
"app.components.ToggleCheckbox.off-label": "
|
|
321
|
-
"app.components.ToggleCheckbox.on-label": "
|
|
320
|
+
"app.components.ToggleCheckbox.off-label": "False",
|
|
321
|
+
"app.components.ToggleCheckbox.on-label": "True",
|
|
322
322
|
"app.components.UpgradePlanModal.button": "Learn more",
|
|
323
323
|
"app.components.UpgradePlanModal.limit-reached": "You have reached the limit",
|
|
324
324
|
"app.components.UpgradePlanModal.text-ce": "Community Edition",
|
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"Analytics": "Phân Tích",
|
|
3
|
+
"Auth.components.Oops.text": "Tài khoản của bạn đã bị khoá",
|
|
4
|
+
"Auth.components.Oops.text.admin": "Nếu có sự nhầm lẫn, hãy liên hệ người quản trị",
|
|
3
5
|
"Auth.form.button.forgot-password": "Gửi Email",
|
|
6
|
+
"Auth.form.button.go-home": "QUAY VỀ TRANG CHỦ",
|
|
4
7
|
"Auth.form.button.login": "Đăng nhập",
|
|
8
|
+
"Auth.form.button.login.providers.error": "Không thể kết nối bạn với dịch vụ đã chọn.",
|
|
9
|
+
"Auth.form.button.login.providers.see-more": "Xem thêm",
|
|
10
|
+
"Auth.form.button.login.strapi": "Đăng nhập với Strapi",
|
|
11
|
+
"Auth.form.button.password-recovery": "Khôi phục mật khẩu",
|
|
5
12
|
"Auth.form.button.register": "Sẵn sàng để bắt đầu",
|
|
6
13
|
"Auth.form.button.reset-password": "Đổi mật khẩu",
|
|
14
|
+
"Auth.form.confirmPassword.label": "Nhập lại mật khẩu",
|
|
15
|
+
"Auth.form.currentPassword.label": "Mật khẩu hiện tại",
|
|
7
16
|
"Auth.form.email.label": "Email",
|
|
8
17
|
"Auth.form.email.placeholder": "kai@doe.com",
|
|
9
|
-
"Auth.form.error.blocked": "Tài khoản của bạn bị khóa bởi người quản
|
|
18
|
+
"Auth.form.error.blocked": "Tài khoản của bạn bị khóa bởi người quản trị.",
|
|
10
19
|
"Auth.form.error.code.provide": "Mã sai đã được cung cấp.",
|
|
11
20
|
"Auth.form.error.confirmed": "Email của tài khoản của bạn chưa được xác nhận.",
|
|
12
|
-
"Auth.form.error.email.invalid": "
|
|
21
|
+
"Auth.form.error.email.invalid": "Email sai.",
|
|
13
22
|
"Auth.form.error.email.provide": "Vui lòng cung cấp tên đăng nhập hoặc email.",
|
|
14
23
|
"Auth.form.error.email.taken": "Email đã được dùng.",
|
|
15
24
|
"Auth.form.error.invalid": "Định danh hoặc mật khẩu sai.",
|
|
@@ -21,18 +30,35 @@
|
|
|
21
30
|
"Auth.form.error.ratelimit": "Thử quá nhiều lần, vui lòng thử lại trong một phút",
|
|
22
31
|
"Auth.form.error.user.not-exist": "Email này chưa tồn tại.",
|
|
23
32
|
"Auth.form.error.username.taken": "Tên đăng nhập đã bị lấy.",
|
|
33
|
+
"Auth.form.firstname.label": "Họ",
|
|
34
|
+
"Auth.form.firstname.placeholder": "v.d. Kai",
|
|
24
35
|
"Auth.form.forgot-password.email.label": "Nhập email của bạn",
|
|
25
36
|
"Auth.form.forgot-password.email.label.success": "Email đã gửi thành công đến",
|
|
37
|
+
"Auth.form.lastname.label": "Tên",
|
|
38
|
+
"Auth.form.lastname.placeholder": "v.d. Doe",
|
|
39
|
+
"Auth.form.password.hide-password": "Ẩn mật khẩu",
|
|
40
|
+
"Auth.form.password.hint": "Mật khẩu phải chứa ít nhất 8 ký tự, 1 viết hoa, 1 viết thường, và 1 số",
|
|
26
41
|
"Auth.form.password.label": "Mật khẩu",
|
|
42
|
+
"Auth.form.password.show-password": "Hiển thị password",
|
|
27
43
|
"Auth.form.register.news.label": "Cập nhật cho tôi về chức năng mới và những cải thiện sắp tới (thông qua việc này bạn đã chấp nhận {terms} và {policy}).",
|
|
44
|
+
"Auth.form.register.subtitle": "Thông tin của bạn chỉ được sử dụng để đăng nhập vào trang quản trị. Tất cả các dữ liệu được lưu ở cơ sở dữ liệu của bạn.",
|
|
28
45
|
"Auth.form.rememberMe.label": "Nhớ tôi",
|
|
29
46
|
"Auth.form.username.label": "Tên đăng nhập",
|
|
30
47
|
"Auth.form.username.placeholder": "Kai Doe",
|
|
31
|
-
"Auth.
|
|
48
|
+
"Auth.form.welcome.subtitle": "Đăng nhập vào tài khoản Strapi của bạn",
|
|
49
|
+
"Auth.form.welcome.title": "Chào mừng!",
|
|
50
|
+
"Auth.link.forgot-password": "Quên mật khẩu?",
|
|
32
51
|
"Auth.link.ready": "Sẵn sàng đăng nhập?",
|
|
52
|
+
"Auth.link.signin": "Đăng nhập",
|
|
53
|
+
"Auth.link.signin.account": "Đã có tài khoản?",
|
|
54
|
+
"Auth.login.sso.divider": "Hoặc đăng nhập với",
|
|
55
|
+
"Auth.login.sso.loading": "Đang tải các dịch vụ cung cấp...",
|
|
56
|
+
"Auth.login.sso.subtitle": "Đăng nhập qua SSO",
|
|
33
57
|
"Auth.privacy-policy-agreement.policy": "chính sách bảo mật",
|
|
34
58
|
"Auth.privacy-policy-agreement.terms": "các điều khoản",
|
|
59
|
+
"Auth.reset-password.title": "Đặt lại mật khẩu",
|
|
35
60
|
"Content Manager": "Quản Lý Nội Dung",
|
|
61
|
+
"Documentation": "Tài liệu",
|
|
36
62
|
"Email": "Email",
|
|
37
63
|
"Files Upload": "Tải Tâp Tin Lên",
|
|
38
64
|
"HomePage.helmet.title": "Trang chủ",
|
|
@@ -257,4 +283,4 @@
|
|
|
257
283
|
"notification.error.layout": "Không thể khôi phục",
|
|
258
284
|
"notification.form.error.fields": "Bảng nhập liệu có vài lỗi",
|
|
259
285
|
"request.error.model.unknown": "Cấu trúc này không tồn tại"
|
|
260
|
-
}
|
|
286
|
+
}
|