0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-31 22:51:25 -05:00

refactor(console): update quota pricing info (#5237)

This commit is contained in:
Xiao Yijun 2024-01-16 18:19:15 +08:00 committed by GitHub
parent e1bbbd9ebf
commit 366adb2ff1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 319 additions and 245 deletions

View file

@ -165,11 +165,7 @@ function CreateConnectorForm({ onClose, isOpen: isFormOpen, type }: Props) {
<div className={styles.standardLabel}>
<DynamicT forKey="connectors.standard_connectors" />
{isCloud && (
<FeatureTag
isVisible={isStandardConnectorDisabled}
for="upsell"
plan={ReservedPlanId.Pro}
/>
<FeatureTag isVisible={isStandardConnectorDisabled} plan={ReservedPlanId.Pro} />
)}
</div>
<ConnectorRadioGroup

View file

@ -9,8 +9,4 @@
padding: _.unit(0.5) _.unit(2);
color: var(--color-white);
text-transform: capitalize;
&.beta {
background-color: var(--color-specific-tag-test);
}
}

View file

@ -6,33 +6,22 @@ import { TenantsContext } from '@/contexts/TenantsProvider';
import * as styles from './index.module.scss';
type BaseProps = {
type Props = {
/**
* Whether the tag should be visible. It should be `true` if the tenant's subscription
* plan has NO access to the feature (paywall), but it will always be visible for dev
* tenants.
*/
isVisible: boolean;
/** The minimum plan required to use the feature. */
plan: Exclude<ReservedPlanId, ReservedPlanId.Free | ReservedPlanId.Development>;
className?: string;
};
type Props =
| (BaseProps & {
/** What the tag is for. */
for: 'upsell';
/**
* Whether the tag should be visible. It should be `true` if the tenant's subscription
* plan has NO access to the feature (paywall), but it will always be visible for dev
* tenants.
*/
isVisible: boolean;
/** The minimum plan required to use the feature. */
plan: Exclude<ReservedPlanId, ReservedPlanId.Free | ReservedPlanId.Development>;
})
| (BaseProps & {
/** What the tag is for. */
for: 'beta';
});
/**
* A tag that indicates whether a feature is in beta or requires a paid plan.
* A tag that indicates whether a feature requires a paid plan.
*
* - **For beta tags**: The tag will always be visible.
* - **For paid plan tags**: The tag will be visible if `isVisible` is `true`, which means that
* The tag will be visible if `isVisible` is `true`, which means that
* the tenant's subscription plan has no access to the feature (paywall). However, it will always
* be visible for dev tenants since they have access to almost all features, and it's useful for
* developers to know which features need to be paid for in production.
@ -40,42 +29,26 @@ type Props =
* CAUTION: You should only render this component when the feature has a paywall.
*
* @example
* Use as a beta tag:
*
* ```tsx
* <FeatureTag for="beta" />
* ```
*
* Use as a paid plan tag:
*
* ```tsx
* // In a production tenant, the tag will be visible when there's no access to the feature
* <FeatureTag for="upsell" isVisible={noAccessToFeature} plan={ReservedPlanId.Pro} />
* <FeatureTag isVisible={noAccessToFeature} plan={ReservedPlanId.Pro} />
*
* // In a dev tenant, the tag will always be visible even if `isVisible` is `false`
* <FeatureTag for="upsell" isVisible={false} plan={ReservedPlanId.Pro} />
* <FeatureTag isVisible={false} plan={ReservedPlanId.Pro} />
*
* // For conditionally rendering the tag, usually in an iteration on a list which contains
* // both free and paid features
* {features.map((feature) => (
* hasPaywall(feature) &&
* <FeatureTag for="upsell" isVisible={hasAccess(feature)} plan={ReservedPlanId.Hobby} />
* <FeatureTag isVisible={hasAccess(feature)} plan={ReservedPlanId.Hobby} />
* ))}
* ```
*/
function FeatureTag(props: Props) {
const { className, for: forType } = props;
const { className } = props;
const { isDevTenant } = useContext(TenantsContext);
// Beta tag should always be visible.
if (forType === 'beta') {
return (
<div className={classNames(styles.tag, styles.beta, className)}>
<span>Beta</span>
</div>
);
}
const { isVisible, plan } = props;
// Dev tenant should always see the tag since they have access to almost all features, and it's

View file

@ -21,8 +21,6 @@ export type Props = {
learnMoreLink?: Pick<TextLinkProps, 'href' | 'targetBlank'>;
isWordWrapEnabled?: boolean;
className?: string;
/** If a beta tag should be shown next to the title. */
isBeta?: boolean;
/**
* If a paywall tag should be shown next to the title. The value is the plan type.
*
@ -41,7 +39,6 @@ function CardTitle({
isWordWrapEnabled = false,
learnMoreLink,
className,
isBeta,
paywall,
}: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
@ -50,8 +47,7 @@ function CardTitle({
<div className={classNames(styles.container, styles[size], className)}>
<div className={classNames(styles.title, !isWordWrapEnabled && styles.titleEllipsis)}>
{typeof title === 'string' ? <DynamicT forKey={title} /> : title}
{paywall && isCloud && <FeatureTag isVisible for="upsell" plan={paywall} />}
{isBeta && isCloud && <FeatureTag for="beta" />}
{paywall && isCloud && <FeatureTag isVisible plan={paywall} />}
</div>
{Boolean(subtitle ?? learnMoreLink) && (
<div className={styles.subtitle}>

View file

@ -115,7 +115,6 @@ function GuideLibrary({ className, hasCardBorder, hasCardButton, hasFilters }: P
{isCloud && (
<FeatureTag
isVisible={!currentPlan.quota.machineToMachineLimit}
for="upsell"
plan={ReservedPlanId.Pro}
className={styles.proTag}
/>

View file

@ -31,7 +31,6 @@ function TypeDescription({ title, subtitle, description, type, size = 'large' }:
<div className={styles.proTag}>
<FeatureTag
isVisible={!currentPlan.quota.machineToMachineLimit}
for="upsell"
plan={ReservedPlanId.Pro}
/>
</div>

View file

@ -65,7 +65,6 @@ function EnterpriseSsoConnectors() {
return (
<ListPage
title={{
isBeta: true,
paywall: conditional((!isSsoEnabled || isDevTenant) && ReservedPlanId.Pro),
title: 'enterprise_sso.title',
subtitle: 'enterprise_sso.subtitle',

View file

@ -27,7 +27,6 @@ function PageWrapper({ children }: Props) {
<div className={styles.container}>
<PageMeta titleKey="mfa.title" />
<CardTitle
isBeta
paywall={cond((!isMfaEnabled || isDevTenant) && ReservedPlanId.Pro)}
title="mfa.title"
subtitle="mfa.description"

View file

@ -70,7 +70,6 @@ function Organizations({ tab }: Props) {
<PageMeta titleKey="organizations.page_title" />
<div className={pageLayout.headline}>
<CardTitle
isBeta
paywall={cond((isOrganizationsDisabled || isDevTenant) && ReservedPlanId.Pro)}
title="organizations.title"
subtitle="organizations.subtitle"

View file

@ -153,7 +153,6 @@ function CreateRoleForm({ onClose }: Props) {
isCloud && (
<FeatureTag
isVisible={!currentPlan.quota.machineToMachineLimit}
for="upsell"
plan={ReservedPlanId.Pro}
className={styles.proTag}
/>

View file

@ -10,3 +10,8 @@
justify-content: flex-start;
}
}
.extraInfo {
font: var(--font-body-3);
color: var(--color-text-secondary);
}

View file

@ -11,19 +11,23 @@ type Props = {
children: ReactNode;
tip?: ReactNode;
isLeftAligned?: boolean;
extraInfo?: ReactNode;
};
function TableDataWrapper({ children, tip, isLeftAligned }: Props) {
function TableDataWrapper({ children, tip, isLeftAligned, extraInfo }: Props) {
return (
<div className={classNames(styles.quotaValue, isLeftAligned && styles.leftAligned)}>
{children}
{tip && (
<ToggleTip content={tip}>
<IconButton size="small">
<Tip />
</IconButton>
</ToggleTip>
)}
<div>
<div className={classNames(styles.quotaValue, isLeftAligned && styles.leftAligned)}>
{children}
{tip && (
<ToggleTip content={tip}>
<IconButton size="small">
<Tip />
</IconButton>
</ToggleTip>
)}
</div>
{extraInfo && <div className={styles.extraInfo}>{extraInfo}</div>}
</div>
);
}

View file

@ -13,43 +13,32 @@ type Props = {
*/
isEnabled?: boolean;
/**
* Whether the feature is in beta.
* Often used with the `paymentType` prop.
* - `false`:
* - When `isEnabled` is `true`, it will display a checkmark.
* - When `isEnabled` is `false`, it will display '-'.
* - `true`:
* - when the `paymentType` is `add-on` or `undefined`, it will display 'Add-on (Beta)', or it will display '-'.
* - when the `paymentType` is `usage`, it will display 'Beta'.
* The payment type of the feature.
*/
isBeta?: boolean;
/**
* Used with the `isBeta` prop to indicate the payment type of the feature.
*/
paymentType?: 'add-on' | 'usage';
isAddOnForPlan?: boolean;
/**
* The tip phrase key to show
*/
tipPhraseKey?: TFuncKey<'translation', 'admin_console.subscription.quota_table'>;
customContent?: React.ReactNode;
};
/**
* Render a feature flag to indicate whether a feature is enabled or not in the plan comparison table.
* - **For normal features**:
* - If `isEnabled` is `undefined`, it will display 'Contact us'.
* - If `isEnabled` is `true`, it will display a checkmark.
* - **For beta features**:
* - If `isBeta` is `true` and the feature is enabled:
* - If `paymentType` is `add-on` or `undefined`, it will display 'Add-on (Beta)'.
* - If `paymentType` is `usage`, it will display 'Beta'.
* - If `isBeta` is `false` or the feature is disabled: works the same as normal features.
*
* - If `isEnabled` is `undefined`, it will display 'Contact us'.
* - If `isEnabled` is `true`, it will display a checkmark.
* - If `isEnabled` is `false`, it will display a dash.
* @example
* ```tsx
* <GenericFeatureFlag isEnabled={true} />
* ```
*/
function GenericFeatureFlag({ isEnabled, isBeta, tipPhraseKey, paymentType }: Props) {
function GenericFeatureFlag({
isEnabled,
tipPhraseKey,
isAddOnForPlan = false,
customContent,
}: Props) {
if (isEnabled === undefined) {
return <DynamicT forKey="subscription.quota_table.contact" />;
}
@ -57,18 +46,9 @@ function GenericFeatureFlag({ isEnabled, isBeta, tipPhraseKey, paymentType }: Pr
return (
<TableDataWrapper
tip={cond(tipPhraseKey && <DynamicT forKey={`subscription.quota_table.${tipPhraseKey}`} />)}
extraInfo={cond(isAddOnForPlan && <DynamicT forKey="subscription.quota_table.add_on" />)}
>
{isEnabled
? cond(!isBeta && <Success />) ?? (
<DynamicT
forKey={
paymentType === 'add-on'
? 'subscription.quota_table.add_on_beta'
: 'subscription.quota_table.beta'
}
/>
)
: '-'}
{customContent ?? (isEnabled ? <Success /> : '-')}
</TableDataWrapper>
);
}

View file

@ -12,6 +12,7 @@ type Props = {
tipPhraseKey?: TFuncKey<'translation', 'admin_console.subscription.quota_table'>;
tipInterpolation?: Record<string, unknown>;
hasCheckmark?: boolean;
extraInfo?: ReactNode;
formatter?: (quota: number) => string | ReactNode;
};
@ -20,6 +21,7 @@ function GenericQuotaLimit({
tipPhraseKey,
tipInterpolation,
hasCheckmark,
extraInfo,
formatter,
}: Props) {
if (quota === undefined) {
@ -37,7 +39,7 @@ function GenericQuotaLimit({
if (quota === null) {
return (
<TableDataWrapper tip={tipContent}>
<TableDataWrapper tip={tipContent} extraInfo={extraInfo}>
{hasCheckmark && <Success />}
<DynamicT forKey="subscription.quota_table.unlimited" />
</TableDataWrapper>
@ -45,7 +47,7 @@ function GenericQuotaLimit({
}
return (
<TableDataWrapper tip={tipContent}>
<TableDataWrapper tip={tipContent} extraInfo={extraInfo}>
{quota === 0 ? (
'-'
) : (

View file

@ -10,6 +10,13 @@ import BasePrice from './BasePrice';
import GenericFeatureFlag from './GenericFeatureFlag';
import GenericQuotaLimit from './GenericQuotaLimit';
const m2mAppUnitPrice = 8;
const resourceUnitPrice = 3;
const ssoUnitPrice = 48;
const mfaPrice = 48;
const maoDisplayLimit = 100;
const maoUnitPrice = 0.64;
export const quotaValueRenderer: Record<
keyof SubscriptionPlanTable,
(planTableData: SubscriptionPlanTableData) => ReactNode
@ -37,19 +44,60 @@ export const quotaValueRenderer: Record<
applicationsLimit: ({ table: { applicationsLimit } }) => (
<GenericQuotaLimit quota={applicationsLimit} />
),
machineToMachineLimit: ({ id, table: { machineToMachineLimit } }) => (
<GenericQuotaLimit
quota={machineToMachineLimit}
tipPhraseKey={cond(id !== ReservedPlanId.Free && 'paid_quota_limit_tip')}
/>
),
machineToMachineLimit: ({ id, table: { machineToMachineLimit } }) => {
const isPaidPlan = id === ReservedPlanId.Hobby;
return (
<GenericQuotaLimit
quota={machineToMachineLimit}
tipPhraseKey={cond(isPaidPlan && 'paid_quota_limit_tip')}
extraInfo={cond(
isPaidPlan && (
<DynamicT
forKey="subscription.quota_table.extra_quota_price"
interpolation={{ value: m2mAppUnitPrice }}
/>
)
)}
formatter={cond(
isPaidPlan &&
((quota) => (
<DynamicT
forKey="subscription.quota_table.included"
interpolation={{ value: quota }}
/>
))
)}
/>
);
},
// Resources
resourcesLimit: ({ id, table: { resourcesLimit } }) => (
<GenericQuotaLimit
quota={resourcesLimit}
tipPhraseKey={cond(id !== ReservedPlanId.Free && 'paid_quota_limit_tip')}
/>
),
resourcesLimit: ({ id, table: { resourcesLimit } }) => {
const isPaidPlan = id === ReservedPlanId.Hobby;
return (
<GenericQuotaLimit
quota={resourcesLimit}
tipPhraseKey={cond(isPaidPlan && 'paid_quota_limit_tip')}
extraInfo={cond(
isPaidPlan && (
<DynamicT
forKey="subscription.quota_table.extra_quota_price"
interpolation={{ value: resourceUnitPrice }}
/>
)
)}
formatter={cond(
isPaidPlan &&
((quota) => (
<DynamicT
forKey="subscription.quota_table.included"
interpolation={{ value: quota }}
/>
))
)}
/>
);
},
scopesPerResourceLimit: ({ table: { scopesPerResourceLimit } }) => (
<GenericQuotaLimit quota={scopesPerResourceLimit} />
),
@ -68,14 +116,24 @@ export const quotaValueRenderer: Record<
),
i18nEnabled: ({ table: { i18nEnabled } }) => <GenericFeatureFlag isEnabled={i18nEnabled} />,
// UserAuthentication
mfaEnabled: ({ table: { mfaEnabled } }) => (
<GenericFeatureFlag
isBeta
isEnabled={mfaEnabled}
paymentType="add-on"
tipPhraseKey={cond(mfaEnabled && 'beta_feature_tip')}
/>
),
mfaEnabled: ({ id, table: { mfaEnabled } }) => {
const isPaidPlan = id === ReservedPlanId.Hobby;
return (
<GenericFeatureFlag
isEnabled={mfaEnabled}
isAddOnForPlan={isPaidPlan}
tipPhraseKey={cond(isPaidPlan && 'paid_add_on_feature_tip')}
customContent={cond(
isPaidPlan && (
<DynamicT
forKey="subscription.quota_table.per_month"
interpolation={{ value: mfaPrice }}
/>
)
)}
/>
);
},
omniSignInEnabled: ({ table: { omniSignInEnabled } }) => (
<GenericFeatureFlag isEnabled={omniSignInEnabled} />
),
@ -97,14 +155,24 @@ export const quotaValueRenderer: Record<
standardConnectorsLimit: ({ table: { standardConnectorsLimit } }) => (
<GenericQuotaLimit quota={standardConnectorsLimit} />
),
ssoEnabled: ({ table: { ssoEnabled } }) => (
<GenericFeatureFlag
isBeta
isEnabled={ssoEnabled}
paymentType="add-on"
tipPhraseKey={cond(ssoEnabled && 'beta_feature_tip')}
/>
),
ssoEnabled: ({ id, table: { ssoEnabled } }) => {
const isPaidPlan = id === ReservedPlanId.Hobby;
return (
<GenericFeatureFlag
isEnabled={ssoEnabled}
isAddOnForPlan={isPaidPlan}
tipPhraseKey={cond(isPaidPlan && 'paid_add_on_feature_tip')}
customContent={cond(
isPaidPlan && (
<DynamicT
forKey="subscription.quota_table.per_month_each"
interpolation={{ value: ssoUnitPrice }}
/>
)
)}
/>
);
},
// Roles
userManagementEnabled: ({ table: { userManagementEnabled } }) => (
<GenericFeatureFlag isEnabled={userManagementEnabled} />
@ -117,14 +185,38 @@ export const quotaValueRenderer: Record<
<GenericQuotaLimit quota={scopesPerRoleLimit} />
),
// Organizations
organizationsEnabled: ({ table: { organizationsEnabled } }) => (
<GenericFeatureFlag
isBeta
isEnabled={organizationsEnabled}
paymentType="usage"
tipPhraseKey={cond(organizationsEnabled && 'usage_based_beta_feature_tip')}
/>
),
organizationsEnabled: ({ id, table: { organizationsEnabled } }) => {
const isPaidPlan = id === ReservedPlanId.Hobby;
return (
<GenericQuotaLimit
quota={
organizationsEnabled === undefined
? organizationsEnabled
: organizationsEnabled
? maoDisplayLimit
: 0
}
tipPhraseKey={cond(isPaidPlan && 'paid_quota_limit_tip')}
extraInfo={cond(
isPaidPlan && (
<DynamicT
forKey="subscription.quota_table.extra_mao_price"
interpolation={{ value: maoUnitPrice }}
/>
)
)}
formatter={cond(
isPaidPlan &&
((quota) => (
<DynamicT
forKey="subscription.quota_table.included_mao"
interpolation={{ value: quota }}
/>
))
)}
/>
);
},
allowedUsersPerOrganization: ({ table: { allowedUsersPerOrganization } }) => (
<GenericQuotaLimit quota={allowedUsersPerOrganization} />
),

View file

@ -81,18 +81,20 @@ const quota_table = {
paid_token_limit_tip:
'Kostenlos bis {{value}}M umlaufende Tokens. Wir können Gebühren hinzufügen, wenn Sie über {{value}}M Tokens hinausgehen, sobald wir die Preise finalisieren.',
paid_quota_limit_tip:
'Wir können Gebühren für Funktionen hinzufügen, die Ihre Kontingentgrenze überschreiten, sobald wir die Preise finalisieren.',
beta_feature_tip:
'Während der Beta-Phase kostenfrei zu benutzen. Wir werden mit der finalen Festlegung der Zusatzkosten beginnen.',
usage_based_beta_feature_tip:
'Während der Beta-Phase kostenfrei zu benutzen. Wir werden mit der finalen Festlegung der organisationsbasierten nutzungsabhängigen Preise beginnen.',
beta: 'Beta',
add_on_beta: 'Add-on (Beta)',
'Logto wird Gebühren für Funktionen hinzufügen, die über Ihr Kontingent hinausgehen. Sie können es kostenlos verwenden, bis wir etwa im 2. Quartal 2024 mit der Berechnung beginnen.',
paid_add_on_feature_tip:
'Dies ist eine Zusatzfunktion. Sie können sie kostenlos verwenden, bis wir etwa im 2. Quartal 2024 mit der Berechnung beginnen.',
million: '{{value, number}} Millionen',
mau_tip:
'MAU (monatlich aktive Benutzer) bedeutet die Anzahl der eindeutigen Benutzer, die in einem Abrechnungsmonat mindestens einen Token mit Logto ausgetauscht haben.',
tokens_tip:
'Alle Arten von Tokens, die von Logto ausgegeben wurden, einschließlich Zugriffstoken, Aktualisierungstoken, usw.',
included: '{{value, number}} inklusive',
included_mao: '{{value, number}} MAO enthalten',
extra_quota_price: 'Dann ${{value, number}} pro Monat / je danach',
per_month_each: '${{value, number}} pro Monat / je',
extra_mao_price: 'Dann ${{value, number}} pro MAO',
per_month: '${{value, number}} pro Monat',
};
export default Object.freeze(quota_table);

View file

@ -81,18 +81,20 @@ const quota_table = {
paid_token_limit_tip:
'Free for {{value}}M token issued. We may add charges if you go beyond {{value}}M tokens once we finalize the prices.',
paid_quota_limit_tip:
'We may add charges for features that go beyond your quota limit as add-ons once we finalize the prices.',
beta_feature_tip:
'Free to use during the beta phase. We will begin charging once we finalize the add-on pricing.',
usage_based_beta_feature_tip:
'Free to use during the beta phase. We will begin charging once we finalize the org usage-based pricing.',
beta: 'Beta',
add_on_beta: 'Add-on (Beta)',
"Logto will add charges for features that go beyond your quota limit. You can use it at no cost until we're beginning charging around Q2 2024.",
paid_add_on_feature_tip:
"This is an add-on feature. You can use it at no cost until we're beginning charging around Q2 2024.",
million: '{{value, number}} million',
mau_tip:
'MAU (monthly active user) means the number of unique users who have exchanged at least one token with Logto in a billing month.',
tokens_tip:
'All kinds of tokens that issued by Logto, including access token, refresh token, etc.',
included: '{{value, number}} included',
included_mao: '{{value, number}} MAO included',
extra_quota_price: 'Then ${{value, number}} per mo / ea after',
per_month_each: '${{value, number}} per mo / ea',
extra_mao_price: 'Then ${{value, number}} per MAO',
per_month: '${{value, number}} per mo',
};
export default Object.freeze(quota_table);

View file

@ -81,18 +81,20 @@ const quota_table = {
paid_token_limit_tip:
'Gratis para {{value}}M tokens emitidos. Es posible que agreguemos cargos si supera los {{value}}M tokens una vez que finalicemos los precios.',
paid_quota_limit_tip:
'Podemos agregar cargos por funciones que excedan su límite de cuota como complementos una vez que finalicemos los precios.',
beta_feature_tip:
'Gratis durante la fase beta. Comenzaremos a cobrar una vez que finalicemos la fijación de precios del complemento.',
usage_based_beta_feature_tip:
'Gratis durante la fase beta. Comenzaremos a cobrar una vez que finalicemos los precios basados en el uso de la organización.',
beta: 'Beta',
add_on_beta: 'Complemento (Beta)',
'Logto agregará cargos por funciones que excedan su límite de cuota. Puede usarlo sin costo hasta que comencemos a cobrar aproximadamente en el segundo trimestre de 2024.',
paid_add_on_feature_tip:
'Esta es una característica adicional. Puede usarla sin costo hasta que comencemos a cobrar aproximadamente en el segundo trimestre de 2024.',
million: '{{value, number}} millones',
mau_tip:
'MAU (usuario activo mensual) significa el número de usuarios únicos que han intercambiado al menos un token con Logto en un mes de facturación.',
tokens_tip:
'Todo tipo de tokens emitidos por Logto, incluyendo tokens de acceso, tokens de actualización, etc.',
included: 'incluido{{value, number}}',
included_mao: '{{value, number}} MAO incluido',
extra_quota_price: 'Luego ${{value, number}} por mes / cada uno después',
per_month_each: '${{value, number}} por mes / cada uno',
extra_mao_price: 'Luego ${{value, number}} por MAO',
per_month: '${{value, number}} por mes',
};
export default Object.freeze(quota_table);

View file

@ -81,18 +81,20 @@ const quota_table = {
paid_token_limit_tip:
'Gratuit pour {{value}}M jeton émis. Nous pouvons ajouter des frais si vous dépassez {{value}}M jetons une fois que nous aurons finalisé les prix.',
paid_quota_limit_tip:
'Nous pouvons facturer des fonctionnalités qui dépassent votre limite de quotas en tant que modules complémentaires une fois que nous aurons finalisé les prix.',
beta_feature_tip:
'Gratuit à utiliser pendant la phase bêta. Nous commencerons à facturer une fois que nous aurons finalisé les tarifs des modules complémentaires.',
usage_based_beta_feature_tip:
"Gratuit à utiliser pendant la phase bêta. Nous commencerons à facturer une fois que nous aurons finalisé les tarifs basés sur l'usage de l'organisation.",
beta: 'Bêta',
add_on_beta: 'Module complémentaire (Bêta)',
"Logto ajoutera des frais pour les fonctionnalités qui dépassent votre limite de quota. Vous pouvez l'utiliser gratuitement jusqu'à ce que nous commencions à facturer vers le deuxième trimestre 2024.",
paid_add_on_feature_tip:
"Il s'agit d'une fonctionnalité supplémentaire. Vous pouvez l'utiliser gratuitement jusqu'à ce que nous commencions à facturer vers le deuxième trimestre 2024.",
million: '{{value, number}} million',
mau_tip:
"MAU (utilisateur actif mensuel) signifie le nombre d'utilisateurs uniques qui ont échangé au moins un jeton avec Logto au cours d'un mois de facturation.",
tokens_tip:
"Tous types de jetons émis par Logto, y compris les jetons d'accès, les jetons de rafraîchissement, etc.",
included: '{{value, number}} inclus',
included_mao: '{{value, number}} MAO inclus',
extra_quota_price: 'Ensuite ${{value, number}} par mois / chacun après',
per_month_each: '${{value, number}} par mois / chacun',
extra_mao_price: 'Ensuite ${{value, number}} par MAO',
per_month: '${{value, number}} par mois',
};
export default Object.freeze(quota_table);

View file

@ -81,18 +81,20 @@ const quota_table = {
paid_token_limit_tip:
'Gratuito per {{value}}M token rilasciati. Potremmo aggiungere costi se superi {{value}}M token una volta che finalizziamo i prezzi.',
paid_quota_limit_tip:
'Potremmo aggiungere costi per funzionalità che superano il limite di quota come componenti aggiuntivi una volta che finalizziamo i prezzi.',
beta_feature_tip:
'Gratuito durante la fase beta. Inizieremo a addebitare una volta che finalizziamo i prezzi delle componenti aggiuntive.',
usage_based_beta_feature_tip:
"Gratuito durante la fase beta. Inizieremo a addebitare una volta che finalizziamo i prezzi basati sull'utilizzo dell'org.",
beta: 'Beta',
add_on_beta: 'Componente Aggiuntivo (Beta)',
'Logto addebiterà costi per le funzionalità che superano il limite della tua quota. Puoi usarlo gratuitamente fino a quando inizieremo a addebitare circa nel secondo trimestre del 2024.',
paid_add_on_feature_tip:
'Questa è una funzionalità aggiuntiva. Puoi usarla gratuitamente fino a quando inizieremo a addebitare circa nel secondo trimestre del 2024.',
million: '{{value, number}} milioni',
mau_tip:
'MAU (utente attivo mensile) significa il numero di utenti unici che hanno scambiato almeno un token con Logto in un mese di fatturazione.',
tokens_tip:
'Tutti i tipi di token emessi da Logto, inclusi token di accesso, token di aggiornamento, ecc.',
included: '{{value, number}} incluso',
included_mao: '{{value, number}} MAO inclusi',
extra_quota_price: 'Quindi ${{value, number}} al mese / ognuno dopo',
per_month_each: '${{value, number}} al mese / ognuno',
extra_mao_price: 'Quindi ${{value, number}} per MAO',
per_month: '${{value, number}} al mese',
};
export default Object.freeze(quota_table);

View file

@ -81,18 +81,20 @@ const quota_table = {
paid_token_limit_tip:
'無料 {{value}}M トークン発行。価格設定が最終決定した場合、{{value}}M トークンを超えると追加料金が発生する可能性があります。',
paid_quota_limit_tip:
'価格設定が最終決定した場合、クォータ制限を超える機能について追加料金が発生することがあります。',
beta_feature_tip:
'ベータフェーズ中は無料です。アドオンの価格設定が最終決定した後、請求を開始します。',
usage_based_beta_feature_tip:
'ベータフェーズ中は無料です。組織の使用に基づく価格設定が最終決定した後、請求を開始します。',
beta: 'ベータ',
add_on_beta: 'アドオン(ベータ版)',
'Logtoはクォータ制限を超える機能に対して料金を追加します。2024年第2四半期ごろまでは無料でご利用いただけます。',
paid_add_on_feature_tip:
'これはアドオン機能です。2024年第2四半期ごろまでは無料でご利用いただけます。',
million: '{{value, number}} 万',
mau_tip:
'MAU月間アクティブユーザーとは、請求月において少なくとも1つのトークンをLogtoと交換したユニークなユーザーの数を指します。',
tokens_tip:
'Logtoによって発行されたすべての種類のトークン、アクセストークン、リフレッシュトークンなどを含みます。',
included: '{{value, number}} 込み',
included_mao: '{{value, number}} MAO込み',
extra_quota_price: 'その後、各${{value, number}} / 月ごと',
per_month_each: '各${{value, number}} / 月ごと',
extra_mao_price: 'その後、MAOごとに${{value, number}}',
per_month: '${{value, number}} / 月ごと',
};
export default Object.freeze(quota_table);

View file

@ -81,16 +81,19 @@ const quota_table = {
paid_token_limit_tip:
'무료 {{value}}M 토큰 발급. 가격 확정 후 {{value}}M 토큰을 초과하면 추가 요금이 부과될 수 있습니다.',
paid_quota_limit_tip:
'가격 확정 후 할당량 제한을 초과하는 기능에 대해 부가 기능으로 요금이 부과될 수 있습니다.',
beta_feature_tip: '베타 단계에서 무료입니다. 부가 기능 가격 확정 후 요금이 부과됩니다.',
usage_based_beta_feature_tip:
'베타 단계에서 무료입니다. 조직 사용량 기반 요금 책정 후 요금이 부과됩니다.',
beta: '베타',
add_on_beta: '부가 기능 (베타)',
'Logto는 할당량 제한을 초과하는 기능에 대해 요금을 부과할 것입니다. 2024년 제2분기까지는 무료로 사용할 수 있습니다.',
paid_add_on_feature_tip:
'이것은 부가 기능입니다. 2024년 제2분기까지는 무료로 사용할 수 있습니다.',
million: '{{value, number}} 백만',
mau_tip:
'MAU (월간 활성 사용자)는 빌링 월에 Logto와 적어도 하나의 토큰을 교환한 고유 사용자 수를 의미합니다.',
tokens_tip: 'Logto에서 발행한 모든 종류의 토큰, 액세스 토큰, 리프레시 토큰 등을 포함합니다.',
included: '{{value, number}} 포함',
included_mao: '{{value, number}} MAO 포함',
extra_quota_price: '이후 월당 ${{value, number}} / 각각',
per_month_each: '월당 ${{value, number}} / 각각',
extra_mao_price: '이후 MAO당 ${{value, number}}',
per_month: '월당 ${{value, number}}',
};
export default Object.freeze(quota_table);

View file

@ -81,18 +81,20 @@ const quota_table = {
paid_token_limit_tip:
'Bezpłatne do wydania {{value}} M tokenów. Możemy doliczyć opłaty, jeśli przekroczysz limity {{value}} M tokenów, gdy ustalimy ostateczne ceny.',
paid_quota_limit_tip:
'Możemy doliczyć opłaty za funkcje po przekroczeniu limitów kwoty jako dodatki, gdy ustalimy ostateczne ceny.',
beta_feature_tip:
'Darmowe w trakcie fazy beta. Będziemy pobierać opłaty po zakończeniu fazy beta przy ustaleniu cen dodatków.',
usage_based_beta_feature_tip:
'Darmowe w trakcie fazy beta. Będziemy pobierać opłaty po zakończeniu fazy beta przy ustaleniu opłat w oparciu o użycie organizacji.',
beta: 'Beta',
add_on_beta: 'Dodatkowy (Beta)',
'Logto będzie naliczać opłaty za funkcje przekraczające limit kontyngentu. Możesz go używać bezpłatnie do czasu rozpoczęcia naliczania opłat, około II kwartał 2024 roku.',
paid_add_on_feature_tip:
'To jest funkcja dodatkowa. Możesz z niej korzystać bezpłatnie do czasu rozpoczęcia naliczania opłat, około II kwartał 2024 roku.',
million: '{{value, number}} milion',
mau_tip:
'MAU (miesięczny aktywny użytkownik) oznacza liczbę unikalnych użytkowników, którzy wymienili co najmniej jeden token z Logto w miesiącu rozliczeniowym.',
tokens_tip:
'Wszystkie rodzaje tokenów wydanych przez Logto, w tym tokeny dostępu, tokeny odświeżania, itp.',
included: '{{value, number}} zawarte',
included_mao: '{{value, number}} MAO wliczone',
extra_quota_price: 'Następnie ${{value, number}} za miesiąc / każdy po',
per_month_each: '${{value, number}} za miesiąc / każdy',
extra_mao_price: 'Następnie ${{value, number}} za MAO',
per_month: '${{value, number}} za miesiąc',
};
export default Object.freeze(quota_table);

View file

@ -81,18 +81,20 @@ const quota_table = {
paid_token_limit_tip:
'Grátis para {{value}}M tokens emitidos. Podemos cobrar se você ultrapassar {{value}}M tokens após finalizarmos os preços.',
paid_quota_limit_tip:
'Podemos adicionar cobranças para recursos que ultrapassarem o limite da sua cota como complementos, uma vez que finalizarmos os preços.',
beta_feature_tip:
'Grátis durante a fase beta. Começaremos a cobrar uma vez que finalizarmos os preços dos complementos.',
usage_based_beta_feature_tip:
'Grátis durante a fase beta. Começaremos a cobrar uma vez que finalizarmos os preços baseados no uso da organização.',
beta: 'Beta',
add_on_beta: 'Complemento (Beta)',
'O Logto adicionará cobranças por recursos que ultrapassarem seu limite de cota. Você pode usá-lo gratuitamente até começarmos a cobrar, aproximadamente no segundo trimestre de 2024.',
paid_add_on_feature_tip:
'Esta é uma função adicional. Você pode usá-la gratuitamente até começarmos a cobrar, aproximadamente no segundo trimestre de 2024.',
million: '{{value, number}} milhão',
mau_tip:
'MAU (usuário ativo mensal) significa o número de usuários únicos que trocaram pelo menos um token com o Logto em um mês de faturamento.',
tokens_tip:
'Todos os tipos de tokens emitidos pelo Logto, incluindo token de acesso, token de atualização, etc.',
included: 'incluído{{value, number}}',
included_mao: '{{value, number}} MAO incluído',
extra_quota_price: 'Então ${{value, number}} por mês / cada depois',
per_month_each: '${{value, number}} por mês / cada',
extra_mao_price: 'Então ${{value, number}} por MAO',
per_month: '${{value, number}} por mês',
};
export default Object.freeze(quota_table);

View file

@ -81,18 +81,20 @@ const quota_table = {
paid_token_limit_tip:
'Gratuito para {{value}}M de tokens emitidos. Podemos adicionar taxas se exceder {{value}}M de tokens assim que finalizarmos os preços.',
paid_quota_limit_tip:
'Podemos adicionar taxas para funcionalidades que excedam o limite da sua quota como suplementos assim que finalizarmos os preços.',
beta_feature_tip:
'Gratuito durante a fase beta. Começaremos a cobrar assim que finalizarmos os preços dos suplementos.',
usage_based_beta_feature_tip:
'Gratuito durante a fase beta. Começaremos a cobrar assim que finalizarmos os preços baseados no uso da organização.',
beta: 'Beta',
add_on_beta: 'Suplemento (Beta)',
'O Logto adicionará encargos por funcionalidades que ultrapassem o seu limite de quota. Pode utilizá-lo sem custos até começarmos a cobrar, aproximadamente no segundo trimestre de 2024.',
paid_add_on_feature_tip:
'Esta é uma funcionalidade adicional. Pode utilizá-la sem custos até começarmos a cobrar, aproximadamente no segundo trimestre de 2024.',
million: '{{value, number}} milhão',
mau_tip:
'MAU (utilizador ativo mensal) significa o número de utilizadores únicos que trocaram pelo menos um token com o Logto num mês de faturação.',
tokens_tip:
'Todos os tipos de tokens emitidos pelo Logto, incluindo token de acesso, token de atualização, etc.',
included: 'incluído{{value, number}}',
included_mao: '{{value, number}} MAO incluída',
extra_quota_price: 'Depois ${{value, number}} por mês / cada um depois',
per_month_each: '${{value, number}} por mês / cada um',
extra_mao_price: 'Depois ${{value, number}} por MAO',
per_month: '${{value, number}} por mês',
};
export default Object.freeze(quota_table);

View file

@ -81,18 +81,20 @@ const quota_table = {
paid_token_limit_tip:
'Бесплатно для выданных токенов: {{value}} млн. Мы можем начислить плату, если вы превысите {{value}} млн. токенов, когда мы окончательно установим цены.',
paid_quota_limit_tip:
'Мы можем начислить плату за функции, выходящие за пределы вашей квоты, как дополнительные услуги, когда мы окончательно установим цены.',
beta_feature_tip:
'Бесплатно во время бета-тестирования. Мы начнем взимать плату, как только установим цены на дополнительные услуги.',
usage_based_beta_feature_tip:
'Бесплатно во время бета-тестирования. Мы начнем взимать плату, как только установим цены на использование по организации.',
beta: 'Бета-тестирование',
add_on_beta: 'Дополнение (бета-тестирование)',
'Logto добавит плату за функции, выходящие за пределы вашего лимита квоты. Вы можете использовать его бесплатно до начала взимания платы, примерно с 2 квартала 2024 года.',
paid_add_on_feature_tip:
'Это дополнительная функция. Вы можете использовать ее бесплатно до начала взимания платы, примерно с 2 квартала 2024 года.',
million: '{{value, number}} миллионов',
mau_tip:
'MAU (месячно активные пользователи) означает количество уникальных пользователей, которые обменялись хотя бы одним токеном с Logto за месяц расчета.',
tokens_tip:
'Все виды токенов, выпущенных Logto, включая токены доступа, токены обновления и т. д.',
included: 'включено{{value, number}}',
included_mao: '{{value, number}} MAO включено',
extra_quota_price: 'Затем ${{value, number}} в месяц / за каждый после',
per_month_each: '${{value, number}} в месяц / за каждый',
extra_mao_price: 'Затем ${{value, number}} за MAO',
per_month: '${{value, number}} в месяц',
};
export default Object.freeze(quota_table);

View file

@ -81,18 +81,20 @@ const quota_table = {
paid_token_limit_tip:
'Free for {{value}}M token issued. We may add charges if you go beyond {{value}}M tokens once we finalize the prices.',
paid_quota_limit_tip:
'We may add charges for features that go beyond your quota limit as add-ons once we finalize the prices.',
beta_feature_tip:
'Free to use during the beta phase. We will begin charging once we finalize the add-on pricing.',
usage_based_beta_feature_tip:
'Free to use during the beta phase. We will begin charging once we finalize the org usage-based pricing.',
beta: 'Beta',
add_on_beta: 'Ek hizmet (Beta)',
"Logto, kota limitinizi aşan özellikler için ücretlendirme ekleyecektir. Şarjımıza başlamadan önce, yaklaşık olarak 2024 Q2'ye kadar ücretsiz olarak kullanabilirsiniz.",
paid_add_on_feature_tip:
"Bu bir ek özelliktir. Şarjımıza başlamadan önce, yaklaşık olarak 2024 Q2'ye kadar ücretsiz olarak kullanabilirsiniz.",
million: '{{value, number}} milyon',
mau_tip:
'MAU (aylık aktif kullanıcı) Logto ile en az bir token değiştiren benzersiz kullanıcı sayısını ifade eder.',
tokens_tip:
'Logto tarafından ihraç edilen erişim tokeni, yenileme tokeni vb. dahil olmak üzere tüm token türleri.',
included: '{{value, number}} dahil',
included_mao: '{{value, number}} MAO dahil',
extra_quota_price: 'Sonra aylık ${{value, number}} / sonrasında her biri',
per_month_each: 'Aylık ${{value, number}} / her biri',
extra_mao_price: 'Sonra MAO başına ${{value, number}}',
per_month: 'Aylık ${{value, number}}',
};
export default Object.freeze(quota_table);

View file

@ -80,15 +80,19 @@ const quota_table = {
free_token_limit_tip: '免费发行{{value}}M令牌。',
paid_token_limit_tip:
'免费发行{{value}}M令牌。我们可能会在定价确定后如果您的令牌超过{{value}}M则会加收费用。',
paid_quota_limit_tip: '一旦我们确定价格,我们可能会对超出配额限制的功能添加额外费用。',
beta_feature_tip: '在测试阶段免费使用。一旦我们确定附加功能的定价,我们将开始收费。',
usage_based_beta_feature_tip:
'在测试阶段免费使用。一旦我们确定基于组织使用情况的定价,我们将开始收费。',
beta: '测试版',
add_on_beta: '附加功能(测试版)',
paid_quota_limit_tip:
'Logto将为超出配额限制的功能添加费用。在我们从2024年第二季度开始收费之前您可以免费使用它。',
paid_add_on_feature_tip:
'这是一个附加功能。在我们从2024年第二季度开始收费之前您可以免费使用它。',
million: '{{value, number}} 百万',
mau_tip: 'MAU月活跃用户表示在一个计费月内与 Logto 交换过至少一个令牌的独立用户数量。',
tokens_tip: 'Logto 发行的所有类型令牌,包括访问令牌、刷新令牌等。',
included: '已包含{{value, number}}',
included_mao: '已包含 {{value, number}} MAO',
extra_quota_price: '然后每月 ${{value, number}} / 每个之后',
per_month_each: '每月 ${{value, number}} / 每个',
extra_mao_price: '然后每 MAO ${{value, number}}',
per_month: '每月 ${{value, number}}',
};
export default Object.freeze(quota_table);

View file

@ -80,15 +80,19 @@ const quota_table = {
free_token_limit_tip: '免費發行 {{value}}M 個令牌。',
paid_token_limit_tip:
'免費發行 {{value}}M 個令牌。一旦我們確定價格,如果您超出{{value}}M個令牌我們可能會加收費用。',
paid_quota_limit_tip: '一旦我們確定價格,我們可能會以附加功能的形式為超出配額限制的功能收費。',
beta_feature_tip: '在測試版階段免費使用。我們確定附加功能的價格後將開始收費。',
usage_based_beta_feature_tip:
'在測試版階段免費使用。我們確定組織基於使用情況的價格後將開始收費。',
beta: '測試版',
add_on_beta: '附加功能(測試版)',
paid_quota_limit_tip:
'Logto將為超出配額限制的功能添加費用。在我們從2024年第二季度開始收費之前您可以免費使用它。',
paid_add_on_feature_tip:
'這是一個附加功能。在我們從2024年第二季度開始收費之前您可以免費使用它。',
million: '{{value, number}} 百萬',
mau_tip: 'MAU月活躍用戶表示在一個結算月內與 Logto 交換過至少一個令牌的獨立用戶數量。',
tokens_tip: 'Logto 發行的所有類型令牌,包括訪問令牌、刷新令牌等。',
included: '已包含 {{value, number}}',
included_mao: '已包含 {{value, number}} MAO',
extra_quota_price: '然後每月 ${{value, number}} / 每個之後',
per_month_each: '每月 ${{value, number}} / 每個',
extra_mao_price: '然後每 MAO ${{value, number}}',
per_month: '每月 ${{value, number}}',
};
export default Object.freeze(quota_table);

View file

@ -81,15 +81,18 @@ const quota_table = {
paid_token_limit_tip:
'免費發行 {{value}}M 標記。一旦我們確定價格,如果您超過 {{value}}M 標記,我們可能會加收費用。',
paid_quota_limit_tip:
'一旦我們確定價格,如果您超出配額限制的功能,我們可能會將其作為附加功能收費。',
beta_feature_tip: '在測試版階段免費使用。一旦我們確定附加功能的定價,我們將開始收費。',
usage_based_beta_feature_tip:
'在測試版階段免費使用。一旦我們確定組織使用量的定價,我們將開始收費。',
beta: '測試版',
add_on_beta: '附加功能(測試版)',
'Logto將為超出配額限制的功能添加費用。在我們從2024年第二季度開始收費之前您可以免費使用它。',
paid_add_on_feature_tip:
'這是一個附加功能。在我們從2024年第二季度開始收費之前您可以免費使用它。',
million: '{{value, number}} 百萬',
mau_tip: 'MAU月活躍使用者表示在一個計費月內與 Logto 交換過至少一個令牌的獨立使用者數量。',
tokens_tip: 'Logto 發行的所有類型令牌,包括訪問令牌、刷新令牌等。',
included: '已包含{{value, number}}',
included_mao: '已包含 {{value, number}} MAO',
extra_quota_price: '然後每月 ${{value, number}} / 每個之後',
per_month_each: '每月 ${{value, number}} / 每個',
extra_mao_price: '然後每 MAO ${{value, number}}',
per_month: '每月 ${{value, number}}',
};
export default Object.freeze(quota_table);