0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-24 22:05:56 -05:00

Merge pull request #941 from logto-io/charles-log-2588-add-integration-guide-for-native-sdks

refactor(console): refactor integration guide to support native SDKs
This commit is contained in:
Charles Zhao 2022-05-25 16:00:00 +08:00 committed by GitHub
commit 95e394ba1c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 521 additions and 110 deletions

View file

@ -1,3 +1,4 @@
import MultiTextInputField from '@mdx/components/MultiTextInputField';
import Step from '@mdx/components/Step'; import Step from '@mdx/components/Step';
import Tabs from '@mdx/components/Tabs'; import Tabs from '@mdx/components/Tabs';
import TabItem from '@mdx/components/TabItem'; import TabItem from '@mdx/components/TabItem';
@ -12,8 +13,16 @@ import TabItem from '@mdx/components/TabItem';
> >
### Prerequisite ### Prerequisite
* Minimal Android SDK: Level 24 (TBD) * Minimal Android SDK: Level 24
Add the `mavenCentral()` repository to your Gradle project build file:
```kotlin
repositories {
mavenCentral()
}
```
Add the Logto Android SDK to your dependencies:
<Tabs> <Tabs>
<TabItem value="kotlin" label="Kotlin"> <TabItem value="kotlin" label="Kotlin">
@ -55,7 +64,7 @@ The `Redirect URI` indicates the endpoint that the application used to receive a
### Configure Redirect URI in Admin Console ### Configure Redirect URI in Admin Console
Add the following value to Redirect URIs on the application settings page. Add the following value to Redirect URIs input field
```bash ```bash
$(LOGTO_REDIRECT_SCHEME)://$(YOUR_APP_PACKAGE)/callback $(LOGTO_REDIRECT_SCHEME)://$(YOUR_APP_PACKAGE)/callback
@ -67,6 +76,8 @@ Notes:
e.g. `io.logto.android://io.logto.sample/callback` e.g. `io.logto.android://io.logto.sample/callback`
<MultiTextInputField name="redirectUris" title="Redirect URI" onError={() => props.onError(2)} />
### Configure Logto Android SDK ### Configure Logto Android SDK
<Tabs> <Tabs>
@ -132,7 +143,7 @@ Notes:
<Step <Step
title="Sign In" title="Sign In"
subtitle="Sin In to your application by Logto and do some extra works" subtitle="Sign In to your application by Logto and do some extra works"
index={2} index={2}
activeIndex={props.activeStepIndex} activeIndex={props.activeStepIndex}
invalidIndex={props.invalidStepIndex} invalidIndex={props.invalidStepIndex}

View file

@ -0,0 +1,284 @@
import MultiTextInputField from '@mdx/components/MultiTextInputField';
import Step from '@mdx/components/Step';
import Tabs from '@mdx/components/Tabs';
import TabItem from '@mdx/components/TabItem';
<Step
title="安装 SDK"
subtitle="从 Gradle 安装 Logto Android SDK"
index={0}
activeIndex={props.activeStepIndex}
invalidIndex={props.invalidStepIndex}
onNext={() => props.onNext(1)}
>
### 前提条件
* 最小 Android SDK 版本: Level 24
将 `mavenCentral()` 添加到构建脚本中:
```kotlin
repositories {
mavenCentral()
}
```
添加 Logto Android SDK 依赖:
<Tabs>
<TabItem value="kotlin" label="Kotlin">
```kotlin
dependencies {
implementation("io.logto.sdk:android:1.0.0")
}
```
</TabItem>
<TabItem value="groovy" label="Groovy">
```groovy
dependencies {
implementation 'io.logto.sdk:android:1.0.0'
}
```
</TabItem>
</Tabs>
</Step>
<Step
title="配置"
subtitle="Configure your application and LogtoClient"
index={1}
activeIndex={props.activeStepIndex}
invalidIndex={props.invalidStepIndex}
onNext={() => props.onNext(2)}
>
### 配置 Redirect URI
Redirect URI 指定了应用用来接受授权结果的端口Logto Android SDK 内部实现了该重定向的功能
### 在管理员控制台中配置 Redirect URI
将以下值添加到下面的 Redirect URI 输入框中:
```bash
$(LOGTO_REDIRECT_SCHEME)://$(YOUR_APP_PACKAGE)/callback
```
注意:
- `LOGTO_REDIRECT_SCHEME` 应为自定义的反向域名格式的一串字符。
- 将上述的 `$(LOGTO_REDIRECT_SCHEME)` 替换成你定义的值。
例: `io.logto.android://io.logto.sample/callback`
<MultiTextInputField name="redirectUris" title="Redirect URI" onError={() => props.onError(1)} />
### 配置 Logto Android SDK
<Tabs>
<TabItem value="kotlin" label="Kotlin">
```kotlin
import io.logto.sdk.android.LogtoClient
import io.logto.sdk.android.type.LogtoConfig
class MainActivity : AppCompatActivity() {
val logtoConfig = LogtoConfig(
endpoint = "<your-logto-endpoint>",
appId = "<your-app-id>",
scopes = null,
resources = null,
usingPersistStorage = true,
)
val logtoClient = LogtoClient(logtoConfig, application)
}
```
</TabItem>
<TabItem value="java" label="Java">
```java
import io.logto.sdk.android.LogtoClient;
import io.logto.sdk.android.type.LogtoConfig;
public class MainActivity extends AppCompatActivity {
private LogtoClient logtoClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LogtoConfig logtoConfig = new LogtoConfig(
"<your-logto-endpoint>",
"<your-app-id>",
null,
null,
true
);
logtoClient = new LogtoClient(logtoConfig, getApplication());
}
}
```
</TabItem>
</Tabs>
Notes:
- `<your-logto-endpoint>` 是你运行 Logto 服务所在的地址. 若你的 Logto 服务运行在 `http://localhost:300`,则 `<your-logto-endpoint` 为 `http://localhost:300`
- `<your-app-id>` 是你为自己的应用在管理员控制台中所创建的客户端ID.
</Step>
<Step
title="登录"
subtitle="Sign In to your application by Logto and do some extra works"
index={2}
activeIndex={props.activeStepIndex}
invalidIndex={props.invalidStepIndex}
onNext={() => props.onNext(3)}
>
### 执行登录
<Tabs>
<TabItem value="kotlin" label="Kotlin">
```kotlin
logtoClient.signInWithBrowser(
this,
"io.logto.android://io.logto.sample/callback",
) { logtoException: LogtoException? ->
// 后续处理逻辑
}
```
</TabItem>
<TabItem value="java" label="Java">
```java
logtoClient.signInWithBrowser(
this,
"io.logto.android://io.logto.sample/callback",
logtoException -> {
// 后续处理逻辑
}
);
```
</TabItem>
</Tabs>
### 登录成功后,你可以使用 SDK 提供的一些 API 来实现自己的业务逻辑
<Tabs>
<TabItem value="kotlin" label="Kotlin">
```kotlin
logtoClient.getAccessToken { logtoException: LogtoException?, result: AccessToken? ->
// 后续处理逻辑
}
logtoClient.getIdTokenClaims { logtoException: LogtoException?, result: IdTokenClaims? ->
// 后续处理逻辑
}
logtoClient.fetchUserInfo { logtoException: LogtoException?, userInfoResponse: UserInfoResponse? ->
// 后续处理逻辑
}
```
</TabItem>
<TabItem value="java" label="Java">
```java
logtoClient.getAccessToken((logtoException, accessToken) -> {
// 后续处理逻辑
});
logtoClient.getIdTokenClaims((logtoException, idTokenClaims) -> {
// 后续处理逻辑
});
logtoClient.fetchUserInfo((logtoException, userInfoResponse) -> {
// 后续处理逻辑
});
```
</TabItem>
</Tabs>
</Step>
<Step
title="登出"
index={3}
activeIndex={props.activeStepIndex}
invalidIndex={props.invalidStepIndex}
onNext={() => props.onNext(4)}
>
### 执行登出
<Tabs>
<TabItem value="kotlin" label="Kotlin">
```kotlin
logtoClient.signOut { logtoException: LogtoException? ->
// 后续处理逻辑
}
```
</TabItem>
<TabItem value="java" label="Java">
```java
logtoClient.signOut(logtoException -> {
// 后续处理逻辑
});
```
</TabItem>
</Tabs>
注意:
- 登出操作会清除本地存储的用户相关凭据,即使在登出过程中发生了异常。
</Step>
<Step
title="延伸阅读"
subtitle="3 steps"
index={4}
activeIndex={props.activeStepIndex}
invalidIndex={props.invalidStepIndex}
buttonText="general.done"
buttonHtmlType="submit"
>
- 获取用户信息
- 配置社会化登录
- 访问受保护的 API 资源
</Step>

View file

@ -0,0 +1,118 @@
import MultiTextInputField from '@mdx/components/MultiTextInputField';
import Step from '@mdx/components/Step';
import Tabs from '@mdx/components/Tabs';
import TabItem from '@mdx/components/TabItem';
<Step
title="Install SDK"
subtitle="Add Logto SDK as a Dependency"
index={0}
activeIndex={props.activeStepIndex}
invalidIndex={props.invalidStepIndex}
onNext={() => props.onNext(1)}
>
Use the following URL to add Logto SDK as a dependency in Swift Package Manager.
```bash
https://github.com/logto-io/swift.git
```
Since Xcode 11, you can [directly import a swift package](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app) w/o any additional tool.
We do not support **Carthage** and **CocoaPods** at the time due to some technical issues.
<details>
<summary>Carthage</summary>
Carthage <a href="https://github.com/Carthage/Carthage/issues/1226#issuecomment-290931385">needs a `xcodeproj` file to build</a>, but `swift package generate-xcodeproj` will report a failure since we are using binary targets for native social plugins. We will try to find a workaround later.
</details>
<details>
<summary>CocoaPods</summary>
CocoaPods <a href="https://github.com/CocoaPods/CocoaPods/issues/3276">does not support local dependency</a> and monorepo, thus it's hard to create a `.podspec` for this repo.
</details>
</Step>
<Step
title="Init LogtoClient"
index={1}
activeIndex={props.activeStepIndex}
invalidIndex={props.invalidStepIndex}
onNext={() => props.onNext(2)}
>
```swift
import Logto
let config = try? LogtoConfig(
endpoint: "<your-logto-endpoint>",
appId: "<your-application-id>"
)
let logtoClient = LogtoClient(useConfig: config)
```
By default, we store credentials like ID Token and Refresh Token in Keychain. Thus the user doesn't need to sign in again when he returns.
To turn off this behavior, set `usingPersistStorage` to `false`:
```swift
let config = try? LogtoConfig(
// ...
usingPersistStorage: false
)
```
</Step>
<Step
title="Sign In"
index={2}
activeIndex={props.activeStepIndex}
invalidIndex={props.invalidStepIndex}
onNext={() => props.onNext(3)}
>
First, lets configure your redirect URI
<MultiTextInputField name="redirectUris" title="Redirect URI" onError={() => props.onError(2)} />
```swift
do {
try await client.signInWithBrowser(redirectUri: "<your-redirect-uri>")
print(client.isAuthenticated) // true
} catch let error as LogtoClientErrors.SignIn {
// error occured during sign in
}
```
</Step>
<Step
title="Sign Out"
subtitle="1 step"
index={3}
activeIndex={props.activeStepIndex}
invalidIndex={props.invalidStepIndex}
onNext={() => props.onNext(4)}
>
Calling `.signOut()` will clean all the Logto data in Keychain, if it has.
```swift
await client.signOut()
```
</Step>
<Step
title="Further Readings"
subtitle="3 steps"
index={4}
activeIndex={props.activeStepIndex}
invalidIndex={props.invalidStepIndex}
buttonText="general.done"
buttonHtmlType="submit"
>
- [SDK Documentation](https://link-url-here.org)
- [OIDC Documentation](https://link-url-here.org)
- [Calling API to fetch accessToken](https://link-url-here.org)
</Step>

View file

@ -43,7 +43,6 @@ pnpm add @logto/vue
</TabItem> </TabItem>
</Tabs> </Tabs>
</Step> </Step>
<Step <Step
title="Initiate LogtoClient" title="Initiate LogtoClient"
subtitle="1 step" subtitle="1 step"
@ -70,7 +69,6 @@ app.mount("#app");
``` ```
</Step> </Step>
<Step <Step
title="Sign In" title="Sign In"
subtitle="2 steps" subtitle="2 steps"
@ -149,7 +147,6 @@ const { isAuthenticated } = useLogto();
``` ```
</Step> </Step>
<Step <Step
title="Sign Out" title="Sign Out"
subtitle="1 step" subtitle="1 step"
@ -178,7 +175,6 @@ const onClickSignOut = () => signOut('http://localhost:1234');
``` ```
</Step> </Step>
<Step <Step
title="Further Readings" title="Further Readings"
subtitle="3 steps" subtitle="3 steps"

View file

@ -43,7 +43,6 @@ pnpm add @logto/vue
</TabItem> </TabItem>
</Tabs> </Tabs>
</Step> </Step>
<Step <Step
title="初始化 LogtoClient" title="初始化 LogtoClient"
subtitle="1 step" subtitle="1 step"
@ -70,7 +69,6 @@ app.mount("#app");
``` ```
</Step> </Step>
<Step <Step
title="Sign In" title="Sign In"
subtitle="2 steps" subtitle="2 steps"
@ -150,7 +148,6 @@ const { isAuthenticated } = useLogto();
``` ```
</Step> </Step>
<Step <Step
title="Sign Out" title="Sign Out"
subtitle="1 step" subtitle="1 step"
@ -178,7 +175,6 @@ const onClickSignOut = () => signOut('http://localhost:1234');
``` ```
</Step> </Step>
<Step <Step
title="延伸阅读" title="延伸阅读"
subtitle="3 steps" subtitle="3 steps"

View file

@ -130,6 +130,7 @@ const CreateForm = ({ onClose }: Props) => {
{createdApp && ( {createdApp && (
<GuideModal <GuideModal
appName={createdApp.name} appName={createdApp.name}
appType={createdApp.type}
isOpen={isGetStartedModalOpen} isOpen={isGetStartedModalOpen}
onClose={closeModal} onClose={closeModal}
onComplete={onComplete} onComplete={onComplete}

View file

@ -1,3 +1,4 @@
import { ApplicationType } from '@logto/schemas';
import { MDXProvider } from '@mdx-js/react'; import { MDXProvider } from '@mdx-js/react';
import i18next from 'i18next'; import i18next from 'i18next';
import { MDXProps } from 'mdx/types'; import { MDXProps } from 'mdx/types';
@ -13,23 +14,29 @@ import IconButton from '@/components/IconButton';
import Spacer from '@/components/Spacer'; import Spacer from '@/components/Spacer';
import Close from '@/icons/Close'; import Close from '@/icons/Close';
import * as modalStyles from '@/scss/modal.module.scss'; import * as modalStyles from '@/scss/modal.module.scss';
import { SupportedJavascriptLibraries } from '@/types/applications'; import { applicationTypeAndSdkTypeMappings, SupportedSdk } from '@/types/applications';
import { GuideForm } from '@/types/guide'; import { GuideForm } from '@/types/guide';
import LibrarySelector from '../LibrarySelector'; import SdkSelector from '../SdkSelector';
import StepsSkeleton from '../StepsSkeleton'; import StepsSkeleton from '../StepsSkeleton';
import * as styles from './index.module.scss'; import * as styles from './index.module.scss';
type Props = { type Props = {
appName: string; appName: string;
appType: ApplicationType;
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
onComplete: (data: GuideForm) => Promise<void>; onComplete: (data: GuideForm) => Promise<void>;
}; };
const Guides: Record<string, LazyExoticComponent<(props: MDXProps) => JSX.Element>> = { const Guides: Record<string, LazyExoticComponent<(props: MDXProps) => JSX.Element>> = {
ios: lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/ios.mdx')),
android: lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/android.mdx')),
react: lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/react.mdx')), react: lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/react.mdx')),
vue: lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/vue.mdx')), vue: lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/vue.mdx')),
'android_zh-cn': lazy(
async () => import('@/assets/docs/tutorial/integrate-sdk/android_zh-cn.mdx')
),
'react_zh-cn': lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/react_zh-cn.mdx')), 'react_zh-cn': lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/react_zh-cn.mdx')),
'vue_zh-cn': lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/vue_zh-cn.mdx')), 'vue_zh-cn': lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/vue_zh-cn.mdx')),
}; };
@ -39,16 +46,15 @@ const onClickFetchSampleProject = (projectName: string) => {
window.open(sampleUrl, '_blank'); window.open(sampleUrl, '_blank');
}; };
const GuideModal = ({ appName, isOpen, onClose, onComplete }: Props) => { const GuideModal = ({ appName, appType, isOpen, onClose, onComplete }: Props) => {
const [subtype, setSubtype] = useState<SupportedJavascriptLibraries>( const sdks = applicationTypeAndSdkTypeMappings[appType];
SupportedJavascriptLibraries.React const [selectedSdk, setSelectedSdk] = useState<SupportedSdk>(sdks[0]);
);
const [activeStepIndex, setActiveStepIndex] = useState(-1); const [activeStepIndex, setActiveStepIndex] = useState(-1);
const [invalidStepIndex, setInvalidStepIndex] = useState(-1); const [invalidStepIndex, setInvalidStepIndex] = useState(-1);
const locale = i18next.language; const locale = i18next.language;
const guideI18nKey = `${subtype}_${locale}`.toLowerCase(); const guideI18nKey = `${selectedSdk}_${locale}`.toLowerCase();
const GuideComponent = Guides[guideI18nKey] ?? Guides[subtype]; const GuideComponent = Guides[guideI18nKey] ?? Guides[selectedSdk.toLowerCase()];
const methods = useForm<GuideForm>({ mode: 'onSubmit', reValidateMode: 'onChange' }); const methods = useForm<GuideForm>({ mode: 'onSubmit', reValidateMode: 'onChange' });
const { const {
@ -82,16 +88,16 @@ const GuideModal = ({ appName, isOpen, onClose, onComplete }: Props) => {
type="outline" type="outline"
title="admin_console.applications.guide.get_sample_file" title="admin_console.applications.guide.get_sample_file"
onClick={() => { onClick={() => {
onClickFetchSampleProject(subtype); onClickFetchSampleProject(selectedSdk);
}} }}
/> />
</div> </div>
<div className={styles.content}> <div className={styles.content}>
<FormProvider {...methods}> <FormProvider {...methods}>
<form onSubmit={onSubmit}> <form onSubmit={onSubmit}>
{cloneElement(<LibrarySelector libraryName={subtype} />, { {cloneElement(<SdkSelector sdks={sdks} selectedSdk={selectedSdk} />, {
className: styles.banner, className: styles.banner,
onChange: setSubtype, onChange: setSelectedSdk,
onToggle: () => { onToggle: () => {
setActiveStepIndex(0); setActiveStepIndex(0);
}, },

View file

@ -1,79 +0,0 @@
import classNames from 'classnames';
import React, { useState, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import congrats from '@/assets/images/congrats.svg';
import tada from '@/assets/images/tada.svg';
import Button from '@/components/Button';
import Card from '@/components/Card';
import RadioGroup, { Radio } from '@/components/RadioGroup';
import { SupportedJavascriptLibraries } from '@/types/applications';
import * as styles from './index.module.scss';
type Props = {
className?: string;
libraryName?: SupportedJavascriptLibraries;
onChange?: (value: string) => void;
onToggle?: () => void;
};
const LibrarySelector = ({
className,
libraryName = SupportedJavascriptLibraries.React,
onChange,
onToggle,
}: Props) => {
const [isFolded, setIsFolded] = useState(false);
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const librarySelector = useMemo(
() => (
<Card className={classNames(styles.card, className)}>
<img src={congrats} alt="success" />
<div>
<div className={styles.title}>{t('applications.guide.title')}</div>
<div className={styles.subtitle}>{t('applications.guide.subtitle')}</div>
</div>
<RadioGroup
className={styles.radioGroup}
name="libraryName"
value={libraryName}
type="card"
onChange={onChange}
>
{Object.values(SupportedJavascriptLibraries).map((library) => (
<Radio key={library} className={styles.radio} value={library}>
{library}
</Radio>
))}
</RadioGroup>
<div className={styles.buttonWrapper}>
<Button
type="primary"
title="general.next"
onClick={() => {
setIsFolded(true);
onToggle?.();
}}
/>
</div>
</Card>
),
[className, libraryName, onChange, onToggle, t]
);
const librarySelectorFolded = useMemo(
() => (
<div className={classNames(styles.card, styles.folded, className)}>
<img src={tada} alt="Tada!" />
<span>{t('applications.guide.description_by_library', { library: libraryName })}</span>
</div>
),
[className, libraryName, t]
);
return isFolded ? librarySelectorFolded : librarySelector;
};
export default LibrarySelector;

View file

@ -0,0 +1,70 @@
import classNames from 'classnames';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import congrats from '@/assets/images/congrats.svg';
import tada from '@/assets/images/tada.svg';
import Button from '@/components/Button';
import Card from '@/components/Card';
import RadioGroup, { Radio } from '@/components/RadioGroup';
import { SupportedSdk } from '@/types/applications';
import * as styles from './index.module.scss';
type Props = {
className?: string;
sdks: readonly SupportedSdk[];
selectedSdk: SupportedSdk;
onChange?: (value: string) => void;
onToggle?: () => void;
};
const SdkSelector = ({ className, sdks, selectedSdk, onChange, onToggle }: Props) => {
const [isFolded, setIsFolded] = useState(false);
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
if (isFolded) {
return (
<div className={classNames(styles.card, styles.folded, className)}>
<img src={tada} alt="Tada!" />
<span>{t('applications.guide.description_by_sdk', { sdk: selectedSdk })}</span>
</div>
);
}
return (
<Card className={classNames(styles.card, className)}>
<img src={congrats} alt="success" />
<div>
<div className={styles.title}>{t('applications.guide.title')}</div>
<div className={styles.subtitle}>{t('applications.guide.subtitle')}</div>
</div>
<RadioGroup
className={styles.radioGroup}
name="selectedSdk"
value={selectedSdk}
type="card"
onChange={onChange}
>
{sdks.length > 1 &&
sdks.map((sdk) => (
<Radio key={sdk} className={styles.radio} value={sdk}>
{sdk}
</Radio>
))}
</RadioGroup>
<div className={styles.buttonWrapper}>
<Button
type="primary"
title="general.next"
onClick={() => {
setIsFolded(true);
onToggle?.();
}}
/>
</div>
</Card>
);
};
export default SdkSelector;

View file

@ -6,8 +6,17 @@ export const applicationTypeI18nKey = Object.freeze({
[ApplicationType.Traditional]: 'applications.type.traditional', [ApplicationType.Traditional]: 'applications.type.traditional',
} as const); } as const);
export enum SupportedJavascriptLibraries { export enum SupportedSdk {
Angular = 'angular', iOS = 'iOS',
React = 'react', Android = 'Android',
Vue = 'vue', Angular = 'Angular',
React = 'React',
Vue = 'Vue',
Traditional = 'Traditional',
} }
export const applicationTypeAndSdkTypeMappings = Object.freeze({
[ApplicationType.Native]: [SupportedSdk.iOS, SupportedSdk.Android],
[ApplicationType.SPA]: [SupportedSdk.Angular, SupportedSdk.React, SupportedSdk.Vue],
[ApplicationType.Traditional]: [SupportedSdk.Traditional],
} as const);

View file

@ -169,9 +169,8 @@ const translation = {
'Follow a step by step guide to integrate your application or get a sample configured with your account settings', 'Follow a step by step guide to integrate your application or get a sample configured with your account settings',
title: 'Congratulations! The application has been created successfully.', title: 'Congratulations! The application has been created successfully.',
subtitle: subtitle:
'Now follow the steps below to finish your app settings. Please select the JS library to continue.', 'Now follow the steps below to finish your app settings. Please select the SDK type to continue.',
description_by_library: description_by_sdk: 'This quickstart demonstrates how to add Logto to {{sdk}} application.',
'This quickstart demonstrates how to add Logto to {{library}} application.',
}, },
}, },
application_details: { application_details: {

View file

@ -168,8 +168,8 @@ const translation = {
header_description: header_description:
'参考如下教程,将 Logto 集成到您的应用中。您也可以点击右侧链接,获取我们为您准备好的示范工程。', '参考如下教程,将 Logto 集成到您的应用中。您也可以点击右侧链接,获取我们为您准备好的示范工程。',
title: '恭喜!您的应用已成功创建。', title: '恭喜!您的应用已成功创建。',
subtitle: '请参考以下步骤完成您的应用设置。首先,请选择您要使用的 Javascript 框架', subtitle: '请参考以下步骤完成您的应用设置。首先,请选择您要使用的 SDK 类型',
description_by_library: '本教程向您演示如何在 {{library}} 应用中集成 Logto 登录功能', description_by_sdk: '本教程向您演示如何在 {{sdk}} 应用中集成 Logto 登录功能',
}, },
}, },
application_details: { application_details: {