mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
Merge branch 'master' into gao-add-connector-packages
This commit is contained in:
commit
e8811269c4
232 changed files with 4573 additions and 718 deletions
21
.changeset-staged/shiny-crabs-wink.md
Normal file
21
.changeset-staged/shiny-crabs-wink.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
---
|
||||||
|
"@logto/console": patch
|
||||||
|
"@logto/core": patch
|
||||||
|
"@logto/shared": patch
|
||||||
|
"@logto/ui": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Apply security headers
|
||||||
|
|
||||||
|
Apply security headers to logto http request response using (helmetjs)[https://helmetjs.github.io/].
|
||||||
|
|
||||||
|
[x] crossOriginOpenerPolicy
|
||||||
|
[x] crossOriginEmbedderPolicy
|
||||||
|
[x] crossOriginResourcePolicy
|
||||||
|
[x] hidePoweredBy
|
||||||
|
[x] hsts
|
||||||
|
[x] ieNoOpen
|
||||||
|
[x] noSniff
|
||||||
|
[x] referrerPolicy
|
||||||
|
[x] xssFilter
|
||||||
|
[x] Content-Security-Policy
|
6
.changeset/fluffy-masks-tap.md
Normal file
6
.changeset/fluffy-masks-tap.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
"@logto/phrases": minor
|
||||||
|
"@logto/phrases-ui": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
add it translation
|
6
.changeset/hungry-bears-complain.md
Normal file
6
.changeset/hungry-bears-complain.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
"@logto/phrases": minor
|
||||||
|
"@logto/phrases-ui": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
add pl-PL translation
|
5
.changeset/seven-dolphins-hammer.md
Normal file
5
.changeset/seven-dolphins-hammer.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"@logto/cli": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
`logto translate sync` to create missing files or translate untranslated phrases across all existing languages
|
18
README.md
18
README.md
|
@ -4,21 +4,11 @@
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
Logto has launched Cloud (Preview) and OSS General Availability on <a href="https://www.producthunt.com/posts/logto-cloud-preview" target="_blank">Product Hunt</a>.<br/>
|
|
||||||
</p>
|
|
||||||
<p align="center">
|
|
||||||
😊 Come and support us! We'll see you on the cloud.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
[![discord](https://img.shields.io/discord/965845662535147551?color=5865f2&label=discord)](https://discord.gg/vRvwuwgpVX)
|
[![discord](https://img.shields.io/discord/965845662535147551?color=5865f2&label=discord)](https://discord.gg/vRvwuwgpVX)
|
||||||
[![checks](https://img.shields.io/github/checks-status/logto-io/logto/master)](https://github.com/logto-io/logto/actions?query=branch%3Amaster)
|
[![checks](https://img.shields.io/github/checks-status/logto-io/logto/master)](https://github.com/logto-io/logto/actions?query=branch%3Amaster)
|
||||||
[![release](https://img.shields.io/github/v/release/logto-io/logto?color=7958FF)](https://github.com/logto-io/logto/releases)
|
[![release](https://img.shields.io/github/v/release/logto-io/logto?color=3a3c3f)](https://github.com/logto-io/logto/releases)
|
||||||
[![core coverage](https://img.shields.io/codecov/c/github/logto-io/logto?label=core%20coverage)](https://app.codecov.io/gh/logto-io/logto)
|
[![core coverage](https://img.shields.io/codecov/c/github/logto-io/logto?label=core%20coverage)](https://app.codecov.io/gh/logto-io/logto)
|
||||||
|
[![cloud](https://img.shields.io/badge/cloud-available-7958ff)](https://cloud.logto.io/?sign_up=true)
|
||||||
[![gitpod](https://img.shields.io/badge/gitpod-available-f09439)](https://gitpod.io/#https://github.com/logto-io/demo)
|
[![gitpod](https://img.shields.io/badge/gitpod-available-f09439)](https://gitpod.io/#https://github.com/logto-io/demo)
|
||||||
[![render](https://img.shields.io/badge/render-deploy-5364e9)](https://render.com/deploy?repo=https://github.com/logto-io/logto)
|
[![render](https://img.shields.io/badge/render-deploy-5364e9)](https://render.com/deploy?repo=https://github.com/logto-io/logto)
|
||||||
|
|
||||||
|
@ -53,9 +43,9 @@ Boringly, we call it "[customer identity access management](https://en.wikipedia
|
||||||
|
|
||||||
### Interactive demo
|
### Interactive demo
|
||||||
|
|
||||||
[![GitPod](https://raw.githubusercontent.com/gitpod-io/gitpod/30da76375c996109f243491b23e47feefab7217f/components/dashboard/public/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/logto-io/demo)
|
- Try [Logto Cloud](https://cloud.logto.io/?sign_up=true) to have the same dev experience and zero deployment overhead.
|
||||||
|
|
||||||
If you launch Logto via GitPod, please wait until you see the message like `App is running at https://3002-...gitpod.io` in the terminal, press Cmd (or Ctrl on Windows) and click the URL starts with `https://3002-` to continue your Logto journey.
|
- If you launch Logto [via GitPod](https://gitpod.io/#https://github.com/logto-io/demo), please wait until you see the message like `App is running at https://3002-...gitpod.io` in the terminal, press Cmd (or Ctrl on Windows) and click the URL starts with `https://3002-` to continue your Logto journey.
|
||||||
|
|
||||||
### Launch Logto
|
### Launch Logto
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,9 @@
|
||||||
"lib"
|
"lib"
|
||||||
],
|
],
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
|
||||||
"import": "./lib/index.js"
|
|
||||||
},
|
|
||||||
"./*": {
|
"./*": {
|
||||||
"import": "./lib/*"
|
"import": "./lib/*.js",
|
||||||
|
"types": "./lib/*.d.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
"nanoid": "^4.0.0",
|
"nanoid": "^4.0.0",
|
||||||
"ora": "^6.1.2",
|
"ora": "^6.1.2",
|
||||||
"p-limit": "^4.0.0",
|
"p-limit": "^4.0.0",
|
||||||
|
"p-queue": "^7.3.4",
|
||||||
"p-retry": "^5.1.2",
|
"p-retry": "^5.1.2",
|
||||||
"pg-protocol": "^1.6.0",
|
"pg-protocol": "^1.6.0",
|
||||||
"roarr": "^7.11.0",
|
"roarr": "^7.11.0",
|
||||||
|
|
|
@ -1,88 +1,17 @@
|
||||||
import { existsSync } from 'node:fs';
|
import { isLanguageTag } from '@logto/language-kit';
|
||||||
import fs from 'node:fs/promises';
|
|
||||||
import path from 'node:path';
|
|
||||||
|
|
||||||
import { isLanguageTag, type LanguageTag } from '@logto/language-kit';
|
|
||||||
import { isBuiltInLanguageTag as isPhrasesBuiltInLanguageTag } from '@logto/phrases';
|
import { isBuiltInLanguageTag as isPhrasesBuiltInLanguageTag } from '@logto/phrases';
|
||||||
import { isBuiltInLanguageTag as isPhrasesUiBuiltInLanguageTag } from '@logto/phrases-ui';
|
import { isBuiltInLanguageTag as isPhrasesUiBuiltInLanguageTag } from '@logto/phrases-ui';
|
||||||
import { conditionalString, type Optional } from '@silverhand/essentials';
|
|
||||||
import type { CommandModule } from 'yargs';
|
import type { CommandModule } from 'yargs';
|
||||||
|
|
||||||
import { log } from '../../utils.js';
|
import { log } from '../../utils.js';
|
||||||
import { inquireInstancePath } from '../connector/utils.js';
|
import { inquireInstancePath } from '../connector/utils.js';
|
||||||
|
|
||||||
import { createOpenaiApi, translate } from './openai.js';
|
import { createFullTranslation } from './utils.js';
|
||||||
import { baseLanguage, readBaseLocaleFiles } from './utils.js';
|
|
||||||
|
|
||||||
const createFullTranslation = async (
|
|
||||||
instancePath: Optional<string>,
|
|
||||||
packageName: 'phrases' | 'phrases-ui',
|
|
||||||
languageTag: LanguageTag
|
|
||||||
) => {
|
|
||||||
const directory = path.join(
|
|
||||||
await inquireInstancePath(instancePath),
|
|
||||||
'packages',
|
|
||||||
packageName,
|
|
||||||
'src/locales'
|
|
||||||
);
|
|
||||||
const files = await readBaseLocaleFiles(directory);
|
|
||||||
|
|
||||||
log.info(
|
|
||||||
'Found ' +
|
|
||||||
String(files.length) +
|
|
||||||
' file' +
|
|
||||||
conditionalString(files.length !== 1 && 's') +
|
|
||||||
' in ' +
|
|
||||||
packageName +
|
|
||||||
' to translate'
|
|
||||||
);
|
|
||||||
|
|
||||||
const openai = createOpenaiApi();
|
|
||||||
|
|
||||||
/* eslint-disable no-await-in-loop */
|
|
||||||
for (const file of files) {
|
|
||||||
const relativePath = path.relative(path.join(directory, baseLanguage.toLowerCase()), file);
|
|
||||||
const targetPath = path.join(directory, languageTag.toLowerCase(), relativePath);
|
|
||||||
|
|
||||||
const getTranslationPath = async () => {
|
|
||||||
if (existsSync(targetPath)) {
|
|
||||||
const currentContent = await fs.readFile(targetPath, 'utf8');
|
|
||||||
|
|
||||||
if (currentContent.includes('// UNTRANSLATED')) {
|
|
||||||
return targetPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info(`Target path ${targetPath} exists and has no untranslated mark, skipping`);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return file;
|
|
||||||
};
|
|
||||||
|
|
||||||
const translationPath = await getTranslationPath();
|
|
||||||
|
|
||||||
if (!translationPath) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info(`Translating ${translationPath}`);
|
|
||||||
const result = await translate(openai, languageTag, translationPath);
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
log.error(`Unable to translate ${translationPath}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
await fs.mkdir(path.parse(targetPath).dir, { recursive: true });
|
|
||||||
await fs.writeFile(targetPath, result);
|
|
||||||
log.succeed(`Translated ${targetPath}`);
|
|
||||||
}
|
|
||||||
/* eslint-enable no-await-in-loop */
|
|
||||||
};
|
|
||||||
|
|
||||||
const create: CommandModule<{ path?: string }, { path?: string; 'language-tag': string }> = {
|
const create: CommandModule<{ path?: string }, { path?: string; 'language-tag': string }> = {
|
||||||
command: ['create <language-tag>', 'c'],
|
command: ['create <language-tag>', 'c'],
|
||||||
describe: 'Create a new language translation',
|
describe:
|
||||||
|
'Create a new language translation using ChatGPT. Note the environment variable `OPENAI_API_KEY` is required to work.',
|
||||||
builder: (yargs) =>
|
builder: (yargs) =>
|
||||||
yargs.positional('language-tag', {
|
yargs.positional('language-tag', {
|
||||||
describe: 'The language tag to create, e.g. `af-ZA`.',
|
describe: 'The language tag to create, e.g. `af-ZA`.',
|
||||||
|
@ -94,15 +23,21 @@ const create: CommandModule<{ path?: string }, { path?: string; 'language-tag':
|
||||||
log.error('Invalid language tag. Run `logto translate list-tags` to see available list.');
|
log.error('Invalid language tag. Run `logto translate list-tags` to see available list.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const instancePath = await inquireInstancePath(inputPath);
|
||||||
|
|
||||||
if (isPhrasesBuiltInLanguageTag(languageTag)) {
|
if (isPhrasesBuiltInLanguageTag(languageTag)) {
|
||||||
log.info(languageTag + ' is a built-in tag of phrases, updating untranslated phrases');
|
log.info(languageTag + ' is a built-in tag of phrases, updating untranslated phrases');
|
||||||
}
|
}
|
||||||
await createFullTranslation(inputPath, 'phrases', languageTag);
|
await createFullTranslation({ instancePath, packageName: 'phrases', languageTag });
|
||||||
|
|
||||||
if (isPhrasesUiBuiltInLanguageTag(languageTag)) {
|
if (isPhrasesUiBuiltInLanguageTag(languageTag)) {
|
||||||
log.info(languageTag + ' is a built-in tag of phrases-ui, updating untranslated phrases');
|
log.info(languageTag + ' is a built-in tag of phrases-ui, updating untranslated phrases');
|
||||||
}
|
}
|
||||||
await createFullTranslation(inputPath, 'phrases-ui', languageTag);
|
await createFullTranslation({
|
||||||
|
instancePath,
|
||||||
|
packageName: 'phrases-ui',
|
||||||
|
languageTag,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import type { CommandModule } from 'yargs';
|
||||||
|
|
||||||
import create from './create.js';
|
import create from './create.js';
|
||||||
import listTags from './list-tags.js';
|
import listTags from './list-tags.js';
|
||||||
|
import sync from './sync.js';
|
||||||
|
|
||||||
const translate: CommandModule = {
|
const translate: CommandModule = {
|
||||||
command: ['translate', 't'],
|
command: ['translate', 't'],
|
||||||
|
@ -16,6 +17,7 @@ const translate: CommandModule = {
|
||||||
})
|
})
|
||||||
.command(create)
|
.command(create)
|
||||||
.command(listTags)
|
.command(listTags)
|
||||||
|
.command(sync)
|
||||||
.demandCommand(1),
|
.demandCommand(1),
|
||||||
handler: noop,
|
handler: noop,
|
||||||
};
|
};
|
||||||
|
|
51
packages/cli/src/commands/translate/sync.ts
Normal file
51
packages/cli/src/commands/translate/sync.ts
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import { languages } from '@logto/language-kit';
|
||||||
|
import { isBuiltInLanguageTag as isPhrasesBuiltInLanguageTag } from '@logto/phrases';
|
||||||
|
import { isBuiltInLanguageTag as isPhrasesUiBuiltInLanguageTag } from '@logto/phrases-ui';
|
||||||
|
import PQueue from 'p-queue';
|
||||||
|
import type { CommandModule } from 'yargs';
|
||||||
|
|
||||||
|
import { inquireInstancePath } from '../connector/utils.js';
|
||||||
|
|
||||||
|
import { type CreateFullTranslation, baseLanguage, createFullTranslation } from './utils.js';
|
||||||
|
|
||||||
|
const sync: CommandModule<{ path?: string }, { path?: string }> = {
|
||||||
|
command: ['sync'],
|
||||||
|
describe:
|
||||||
|
'Translate all untranslated phrases using ChatGPT. Note the environment variable `OPENAI_API_KEY` is required to work.',
|
||||||
|
handler: async ({ path: inputPath }) => {
|
||||||
|
const queue = new PQueue({ concurrency: 5 });
|
||||||
|
const instancePath = await inquireInstancePath(inputPath);
|
||||||
|
|
||||||
|
for (const languageTag of Object.keys(languages)) {
|
||||||
|
if (languageTag === baseLanguage) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseOptions = {
|
||||||
|
instancePath,
|
||||||
|
verbose: false,
|
||||||
|
queue,
|
||||||
|
} satisfies Partial<CreateFullTranslation>;
|
||||||
|
|
||||||
|
if (isPhrasesBuiltInLanguageTag(languageTag)) {
|
||||||
|
void createFullTranslation({
|
||||||
|
...baseOptions,
|
||||||
|
packageName: 'phrases',
|
||||||
|
languageTag,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPhrasesUiBuiltInLanguageTag(languageTag)) {
|
||||||
|
void createFullTranslation({
|
||||||
|
...baseOptions,
|
||||||
|
packageName: 'phrases-ui',
|
||||||
|
languageTag,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await queue.onIdle();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default sync;
|
|
@ -1,10 +1,15 @@
|
||||||
|
import { existsSync } from 'node:fs';
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
import { type LanguageTag } from '@logto/language-kit';
|
import { type LanguageTag } from '@logto/language-kit';
|
||||||
|
import { conditionalString } from '@silverhand/essentials';
|
||||||
|
import PQueue from 'p-queue';
|
||||||
|
|
||||||
import { log } from '../../utils.js';
|
import { log } from '../../utils.js';
|
||||||
|
|
||||||
|
import { createOpenaiApi, translate } from './openai.js';
|
||||||
|
|
||||||
export const baseLanguage = 'en' satisfies LanguageTag;
|
export const baseLanguage = 'en' satisfies LanguageTag;
|
||||||
|
|
||||||
export const readLocaleFiles = async (directory: string): Promise<string[]> => {
|
export const readLocaleFiles = async (directory: string): Promise<string[]> => {
|
||||||
|
@ -33,3 +38,82 @@ export const readBaseLocaleFiles = async (directory: string): Promise<string[]>
|
||||||
|
|
||||||
return readLocaleFiles(enDirectory);
|
return readLocaleFiles(enDirectory);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CreateFullTranslation = {
|
||||||
|
instancePath: string;
|
||||||
|
packageName: 'phrases' | 'phrases-ui';
|
||||||
|
languageTag: LanguageTag;
|
||||||
|
verbose?: boolean;
|
||||||
|
queue?: PQueue;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createFullTranslation = async ({
|
||||||
|
instancePath,
|
||||||
|
packageName,
|
||||||
|
languageTag,
|
||||||
|
verbose = true,
|
||||||
|
queue = new PQueue({ concurrency: 5 }),
|
||||||
|
}: CreateFullTranslation) => {
|
||||||
|
const directory = path.join(instancePath, 'packages', packageName, 'src/locales');
|
||||||
|
const files = await readBaseLocaleFiles(directory);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
log.info(
|
||||||
|
'Found ' +
|
||||||
|
String(files.length) +
|
||||||
|
' file' +
|
||||||
|
conditionalString(files.length !== 1 && 's') +
|
||||||
|
' in ' +
|
||||||
|
packageName +
|
||||||
|
' to translate'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const openai = createOpenaiApi();
|
||||||
|
|
||||||
|
/* eslint-disable no-await-in-loop */
|
||||||
|
for (const file of files) {
|
||||||
|
const basePath = path.relative(path.join(directory, baseLanguage.toLowerCase()), file);
|
||||||
|
const targetPath = path.join(directory, languageTag.toLowerCase(), basePath);
|
||||||
|
|
||||||
|
const getTranslationPath = async () => {
|
||||||
|
if (existsSync(targetPath)) {
|
||||||
|
const currentContent = await fs.readFile(targetPath, 'utf8');
|
||||||
|
|
||||||
|
if (currentContent.includes('// UNTRANSLATED')) {
|
||||||
|
return targetPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
log.info(`Target path ${targetPath} exists and has no untranslated mark, skipping`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
|
};
|
||||||
|
|
||||||
|
const translationPath = await getTranslationPath();
|
||||||
|
|
||||||
|
if (!translationPath) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void queue.add(async () => {
|
||||||
|
log.info(`Translating ${translationPath}`);
|
||||||
|
const result = await translate(openai, languageTag, translationPath);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
log.error(`Unable to translate ${translationPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.mkdir(path.parse(targetPath).dir, { recursive: true });
|
||||||
|
await fs.writeFile(targetPath, result);
|
||||||
|
log.succeed(`Translated ${targetPath}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/* eslint-enable no-await-in-loop */
|
||||||
|
|
||||||
|
return queue.onIdle();
|
||||||
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { findUp } from 'find-up';
|
||||||
|
|
||||||
dotenv.config({ path: await findUp('.env', {}) });
|
dotenv.config({ path: await findUp('.env', {}) });
|
||||||
|
|
||||||
const { appInsights } = await import('@logto/app-insights/node.js');
|
const { appInsights } = await import('@logto/app-insights/node');
|
||||||
|
|
||||||
if (appInsights.setup('logto-cloud')) {
|
if (appInsights.setup('logto-cloud')) {
|
||||||
console.debug('Initialized ApplicationInsights');
|
console.debug('Initialized ApplicationInsights');
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { appInsights } from '@logto/app-insights/node.js';
|
import { appInsights } from '@logto/app-insights/node';
|
||||||
import { tryThat } from '@silverhand/essentials';
|
import { tryThat } from '@silverhand/essentials';
|
||||||
import type { BaseContext, NextFunction } from '@withtyped/server';
|
import type { BaseContext, NextFunction } from '@withtyped/server';
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
"@types/mdx": "^2.0.1",
|
"@types/mdx": "^2.0.1",
|
||||||
"@types/mdx-js__react": "^1.5.5",
|
"@types/mdx-js__react": "^1.5.5",
|
||||||
"@types/react": "^18.0.31",
|
"@types/react": "^18.0.31",
|
||||||
|
"@types/react-color": "^3.0.6",
|
||||||
"@types/react-dom": "^18.0.0",
|
"@types/react-dom": "^18.0.0",
|
||||||
"@types/react-helmet": "^6.1.6",
|
"@types/react-helmet": "^6.1.6",
|
||||||
"@types/react-modal": "^3.13.1",
|
"@types/react-modal": "^3.13.1",
|
||||||
|
@ -76,6 +77,7 @@
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-animate-height": "^3.0.4",
|
"react-animate-height": "^3.0.4",
|
||||||
|
"react-color": "^2.19.3",
|
||||||
"react-dnd": "^16.0.0",
|
"react-dnd": "^16.0.0",
|
||||||
"react-dnd-html5-backend": "^16.0.0",
|
"react-dnd-html5-backend": "^16.0.0",
|
||||||
"react-dom": "^18.0.0",
|
"react-dom": "^18.0.0",
|
||||||
|
|
|
@ -29,6 +29,7 @@ import TenantsProvider, { TenantsContext } from './contexts/TenantsProvider';
|
||||||
if (appInsightsReact.setup()) {
|
if (appInsightsReact.setup()) {
|
||||||
console.debug('Initialized ApplicationInsights');
|
console.debug('Initialized ApplicationInsights');
|
||||||
}
|
}
|
||||||
|
|
||||||
void initI18n();
|
void initI18n();
|
||||||
|
|
||||||
function Content() {
|
function Content() {
|
||||||
|
|
|
@ -7,48 +7,24 @@
|
||||||
transition-property: outline, border;
|
transition-property: outline, border;
|
||||||
transition-timing-function: ease-in-out;
|
transition-timing-function: ease-in-out;
|
||||||
transition-duration: 0.2s;
|
transition-duration: 0.2s;
|
||||||
padding: _.unit(2) _.unit(3);
|
padding: _.unit(1.5) _.unit(3);
|
||||||
font: var(--font-body-2);
|
font: var(--font-body-2);
|
||||||
position: relative;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
&:focus-within {
|
&:focus,
|
||||||
|
&.highlight {
|
||||||
border-color: var(--color-primary);
|
border-color: var(--color-primary);
|
||||||
outline-color: var(--color-focused-variant);
|
outline-color: var(--color-focused-variant);
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
.brick {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: inline-block;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
padding: 0;
|
|
||||||
border: 1px solid var(--color-divider);
|
border: 1px solid var(--color-divider);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
margin-right: _.unit(2);
|
||||||
|
|
||||||
&::-moz-color-swatch-wrapper {
|
|
||||||
padding: 0;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-color-swatch-wrapper {
|
|
||||||
padding: 0;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-moz-color-swatch {
|
|
||||||
border-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-color-swatch {
|
|
||||||
border-style: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
flex: 1;
|
|
||||||
margin-left: _.unit(2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,53 @@
|
||||||
import { nanoid } from 'nanoid';
|
import classNames from 'classnames';
|
||||||
import type { ChangeEventHandler } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { useState } from 'react';
|
import { ChromePicker } from 'react-color';
|
||||||
|
|
||||||
|
import { onKeyDownHandler } from '@/utils/a11y';
|
||||||
|
|
||||||
|
import Dropdown from '../Dropdown';
|
||||||
|
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
value?: string;
|
value?: string;
|
||||||
onChange?: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function ColorPicker({ onChange, value = '#000000' }: Props) {
|
function ColorPicker({ onChange, value = '#000000' }: Props) {
|
||||||
const [id, setId] = useState(nanoid());
|
const anchorRef = useRef<HTMLSpanElement>(null);
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const handleChange: ChangeEventHandler<HTMLInputElement> = (event) => {
|
|
||||||
onChange?.(event.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div
|
||||||
<input type="color" id={id} value={value} onChange={handleChange} />
|
tabIndex={0}
|
||||||
<label htmlFor={id}>{value.toUpperCase()}</label>
|
role="button"
|
||||||
|
className={classNames(styles.container, isOpen && styles.highlight)}
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpen(true);
|
||||||
|
}}
|
||||||
|
onKeyDown={onKeyDownHandler(() => {
|
||||||
|
setIsOpen(true);
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<span ref={anchorRef} className={styles.brick} style={{ backgroundColor: value }} />
|
||||||
|
<span>{value.toUpperCase()}</span>
|
||||||
|
<Dropdown
|
||||||
|
anchorRef={anchorRef}
|
||||||
|
isOpen={isOpen}
|
||||||
|
horizontalAlign="start"
|
||||||
|
onClose={() => {
|
||||||
|
setIsOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ChromePicker
|
||||||
|
color={value}
|
||||||
|
onChange={({ hex }) => {
|
||||||
|
onChange(hex);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ColorPicker;
|
export default ColorPicker;
|
||||||
|
|
|
@ -79,7 +79,14 @@ function Dropdown({
|
||||||
}}
|
}}
|
||||||
className={classNames(styles.content, positionState.verticalAlign === 'top' && styles.onTop)}
|
className={classNames(styles.content, positionState.verticalAlign === 'top' && styles.onTop)}
|
||||||
overlayClassName={styles.overlay}
|
overlayClassName={styles.overlay}
|
||||||
onRequestClose={onClose}
|
onRequestClose={(event) => {
|
||||||
|
/**
|
||||||
|
* Note:
|
||||||
|
* we should stop propagation to prevent the event from bubbling up when we click on the overlay to close the dropdown.
|
||||||
|
*/
|
||||||
|
event.stopPropagation();
|
||||||
|
onClose?.();
|
||||||
|
}}
|
||||||
onAfterOpen={mutate}
|
onAfterOpen={mutate}
|
||||||
>
|
>
|
||||||
<div ref={overlayRef} className={styles.dropdownContainer}>
|
<div ref={overlayRef} className={styles.dropdownContainer}>
|
||||||
|
|
|
@ -39,7 +39,14 @@ function ImageWithErrorFallback({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={containerClassName}>
|
<div className={containerClassName}>
|
||||||
<img className={className} src={src} alt={alt} onError={errorHandler} {...props} />
|
<img
|
||||||
|
className={className}
|
||||||
|
src={src}
|
||||||
|
alt={alt}
|
||||||
|
onError={errorHandler}
|
||||||
|
{...props}
|
||||||
|
crossOrigin="anonymous"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ function GithubRawImage({ src, alt }: HTMLProps<HTMLImageElement>) {
|
||||||
src={`${githubRawUrlPrefix}${src}`}
|
src={`${githubRawUrlPrefix}${src}`}
|
||||||
alt={alt}
|
alt={alt}
|
||||||
width={`${width}px`}
|
width={`${width}px`}
|
||||||
|
crossOrigin="anonymous"
|
||||||
onLoad={onLoad}
|
onLoad={onLoad}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,7 +26,7 @@ export type Props = Omit<FileUploaderProps, 'maxSize' | 'allowedMimeTypes'> & {
|
||||||
function ImageUploader({ name, value, onDelete, ...rest }: Props) {
|
function ImageUploader({ name, value, onDelete, ...rest }: Props) {
|
||||||
return value ? (
|
return value ? (
|
||||||
<div className={styles.imageUploader}>
|
<div className={styles.imageUploader}>
|
||||||
<img alt={name} src={value} />
|
<img alt={name} src={value} crossOrigin="anonymous" />
|
||||||
<IconButton
|
<IconButton
|
||||||
className={styles.delete}
|
className={styles.delete}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
"koa-body": "^5.0.0",
|
"koa-body": "^5.0.0",
|
||||||
"koa-compose": "^4.1.0",
|
"koa-compose": "^4.1.0",
|
||||||
"koa-compress": "^5.1.0",
|
"koa-compress": "^5.1.0",
|
||||||
|
"koa-helmet": "^7.0.2",
|
||||||
"koa-logger": "^3.2.1",
|
"koa-logger": "^3.2.1",
|
||||||
"koa-mount": "^4.0.0",
|
"koa-mount": "^4.0.0",
|
||||||
"koa-proxies": "^0.12.1",
|
"koa-proxies": "^0.12.1",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import http2 from 'node:http2';
|
import http2 from 'node:http2';
|
||||||
|
|
||||||
import { appInsights } from '@logto/app-insights/node.js';
|
import { appInsights } from '@logto/app-insights/node';
|
||||||
import { toTitle, trySafe } from '@silverhand/essentials';
|
import { toTitle, trySafe } from '@silverhand/essentials';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import type Koa from 'koa';
|
import type Koa from 'koa';
|
||||||
|
|
|
@ -8,7 +8,7 @@ import SystemContext from './tenants/SystemContext.js';
|
||||||
|
|
||||||
dotenv.config({ path: await findUp('.env', {}) });
|
dotenv.config({ path: await findUp('.env', {}) });
|
||||||
|
|
||||||
const { appInsights } = await import('@logto/app-insights/node.js');
|
const { appInsights } = await import('@logto/app-insights/node');
|
||||||
|
|
||||||
if (appInsights.setup('logto')) {
|
if (appInsights.setup('logto')) {
|
||||||
console.debug('Initialized ApplicationInsights');
|
console.debug('Initialized ApplicationInsights');
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { appInsights } from '@logto/app-insights/node.js';
|
import { appInsights } from '@logto/app-insights/node';
|
||||||
import type { RequestErrorBody } from '@logto/schemas';
|
import type { RequestErrorBody } from '@logto/schemas';
|
||||||
import type { Middleware } from 'koa';
|
import type { Middleware } from 'koa';
|
||||||
import { HttpError } from 'koa';
|
import { HttpError } from 'koa';
|
||||||
|
|
124
packages/core/src/middleware/koa-security-headers.ts
Normal file
124
packages/core/src/middleware/koa-security-headers.ts
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
import { defaultTenantId } from '@logto/schemas';
|
||||||
|
import type { MiddlewareType } from 'koa';
|
||||||
|
import helmet from 'koa-helmet';
|
||||||
|
|
||||||
|
import { EnvSet, AdminApps, getTenantEndpoint } from '#src/env-set/index.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply security headers to the response using koa-helmet
|
||||||
|
* @see https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html for recommended headers
|
||||||
|
* @see https://helmetjs.github.io/ for more details
|
||||||
|
* @returns koa middleware
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function koaSecurityHeaders<StateT, ContextT, ResponseBodyT>(
|
||||||
|
mountedApps: string[],
|
||||||
|
tenantId: string
|
||||||
|
): MiddlewareType<StateT, ContextT, ResponseBodyT> {
|
||||||
|
type Middleware = MiddlewareType<StateT, ContextT, ResponseBodyT>;
|
||||||
|
|
||||||
|
type HelmetOptions = Parameters<typeof helmet>[0];
|
||||||
|
|
||||||
|
const { isProduction, isCloud, isMultiTenancy, adminUrlSet, cloudUrlSet } = EnvSet.values;
|
||||||
|
|
||||||
|
const adminOrigins = adminUrlSet.origins;
|
||||||
|
const cloudOrigins = isCloud ? cloudUrlSet.origins : [];
|
||||||
|
const tenantEndpointOrigin = getTenantEndpoint(
|
||||||
|
isMultiTenancy ? tenantId : defaultTenantId,
|
||||||
|
EnvSet.values
|
||||||
|
).origin;
|
||||||
|
const developmentOrigins = isProduction ? [] : ['ws:'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Applied rules:
|
||||||
|
* - crossOriginOpenerPolicy: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#cross-origin-opener-policy-coop
|
||||||
|
* - crossOriginResourcePolicy: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#cross-origin-resource-policy-corp
|
||||||
|
* - crossOriginEmbedderPolicy: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#cross-origin-embedder-policy-coep
|
||||||
|
* - hidePoweredBy: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-powered-by
|
||||||
|
* - hsts: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#strict-transport-security-hsts
|
||||||
|
* - ieNoOpen: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-download-options
|
||||||
|
* - noSniff: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-content-type-options
|
||||||
|
* - permittedCrossDomainPolicies: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-permitted-cross-domain-policies
|
||||||
|
* - referrerPolicy: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#referrer-policy
|
||||||
|
* - xssFilter: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-xss-protection
|
||||||
|
* - originAgentCluster: https://whatpr.org/html/6214/origin.html#origin-keyed-agent-clusters
|
||||||
|
*/
|
||||||
|
|
||||||
|
const basicSecurityHeaderSettings: HelmetOptions = {
|
||||||
|
contentSecurityPolicy: false, // Exclusively set per app
|
||||||
|
expectCt: false, // Not recommended, will be deprecated by modern browsers
|
||||||
|
dnsPrefetchControl: false,
|
||||||
|
referrerPolicy: {
|
||||||
|
policy: 'strict-origin-when-cross-origin',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mainFlowUiSecurityHeaderSettings: HelmetOptions = {
|
||||||
|
...basicSecurityHeaderSettings,
|
||||||
|
// WARNING: high risk Need to allow self hosted terms of use page loaded in an iframe
|
||||||
|
frameguard: false,
|
||||||
|
// Alow loaded by console preview iframe
|
||||||
|
crossOriginResourcePolicy: {
|
||||||
|
policy: 'cross-origin',
|
||||||
|
},
|
||||||
|
contentSecurityPolicy: {
|
||||||
|
useDefaults: true,
|
||||||
|
// Temporary set to report only to avoid breaking the app
|
||||||
|
reportOnly: true,
|
||||||
|
directives: {
|
||||||
|
imgSrc: ["'self'", 'data:', 'https:'],
|
||||||
|
connectSrc: ["'self'", ...adminOrigins, ...cloudOrigins, ...developmentOrigins],
|
||||||
|
// WARNING: high risk Need to allow self hosted terms of use page loaded in an iframe
|
||||||
|
frameSrc: ["'self'", 'https:'],
|
||||||
|
// Alow loaded by console preview iframe
|
||||||
|
frameAncestors: ["'self'", ...adminOrigins, ...cloudOrigins],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const consoleSecurityHeaderSettings: HelmetOptions = {
|
||||||
|
...basicSecurityHeaderSettings,
|
||||||
|
// Guarded by CSP header bellow
|
||||||
|
frameguard: false,
|
||||||
|
contentSecurityPolicy: {
|
||||||
|
useDefaults: true,
|
||||||
|
// Temporary set to report only to avoid breaking the app
|
||||||
|
reportOnly: true,
|
||||||
|
directives: {
|
||||||
|
imgSrc: ["'self'", 'data:', 'https:'],
|
||||||
|
connectSrc: [
|
||||||
|
"'self'",
|
||||||
|
tenantEndpointOrigin,
|
||||||
|
...adminOrigins,
|
||||||
|
...cloudOrigins,
|
||||||
|
...developmentOrigins,
|
||||||
|
],
|
||||||
|
// Allow Main Flow origin loaded in preview iframe
|
||||||
|
frameSrc: ["'self'", tenantEndpointOrigin],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildHelmetMiddleware: (options: HelmetOptions) => Middleware = (options) =>
|
||||||
|
helmet(options);
|
||||||
|
|
||||||
|
return async (ctx, next) => {
|
||||||
|
const requestPath = ctx.request.path;
|
||||||
|
|
||||||
|
// Admin Console
|
||||||
|
if (
|
||||||
|
requestPath.startsWith(`/${AdminApps.Console}`) ||
|
||||||
|
requestPath.startsWith(`/${AdminApps.Welcome}`)
|
||||||
|
) {
|
||||||
|
return buildHelmetMiddleware(consoleSecurityHeaderSettings)(ctx, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route has been handled by one of mounted apps
|
||||||
|
if (mountedApps.some((app) => app !== '' && requestPath.startsWith(`/${app}`))) {
|
||||||
|
return buildHelmetMiddleware(basicSecurityHeaderSettings)(ctx, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main flow UI
|
||||||
|
return buildHelmetMiddleware(mainFlowUiSecurityHeaderSettings)(ctx, next);
|
||||||
|
};
|
||||||
|
}
|
|
@ -4,7 +4,13 @@ import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
import { userClaims } from '@logto/core-kit';
|
import { userClaims } from '@logto/core-kit';
|
||||||
import type { I18nKey } from '@logto/phrases';
|
import type { I18nKey } from '@logto/phrases';
|
||||||
import { CustomClientMetadataKey, demoAppApplicationId } from '@logto/schemas';
|
import {
|
||||||
|
CustomClientMetadataKey,
|
||||||
|
demoAppApplicationId,
|
||||||
|
logtoCookieKey,
|
||||||
|
type LogtoUiCookie,
|
||||||
|
} from '@logto/schemas';
|
||||||
|
import { conditional } from '@silverhand/essentials';
|
||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
import Provider, { errors, type ResourceServer } from 'oidc-provider';
|
import Provider, { errors, type ResourceServer } from 'oidc-provider';
|
||||||
import snakecaseKeys from 'snakecase-keys';
|
import snakecaseKeys from 'snakecase-keys';
|
||||||
|
@ -145,15 +151,23 @@ export default function initOidc(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
interactions: {
|
interactions: {
|
||||||
url: (ctx, interaction) => {
|
url: (ctx, { params: { client_id: appId }, prompt }) => {
|
||||||
const isDemoApp = interaction.params.client_id === demoAppApplicationId;
|
const isDemoApp = appId === demoAppApplicationId;
|
||||||
|
|
||||||
|
ctx.cookies.set(
|
||||||
|
logtoCookieKey,
|
||||||
|
JSON.stringify({
|
||||||
|
appId: conditional(Boolean(appId) && String(appId)),
|
||||||
|
} satisfies LogtoUiCookie),
|
||||||
|
{ sameSite: 'lax', overwrite: true, httpOnly: false }
|
||||||
|
);
|
||||||
|
|
||||||
const appendParameters = (path: string) => {
|
const appendParameters = (path: string) => {
|
||||||
// `notification` is for showing a text banner on the homepage
|
// `notification` is for showing a text banner on the homepage
|
||||||
return isDemoApp ? path + `?notification=demo_app.notification&no_cache` : path;
|
return isDemoApp ? path + `?notification=demo_app.notification&no_cache` : path;
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (interaction.prompt.name) {
|
switch (prompt.name) {
|
||||||
case 'login': {
|
case 'login': {
|
||||||
const isSignUp =
|
const isSignUp =
|
||||||
ctx.oidc.params?.[OIDCExtraParametersKey.InteractionMode] === InteractionMode.signUp;
|
ctx.oidc.params?.[OIDCExtraParametersKey.InteractionMode] === InteractionMode.signUp;
|
||||||
|
@ -166,7 +180,7 @@ export default function initOidc(
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
throw new Error(`Prompt not supported: ${interaction.prompt.name}`);
|
throw new Error(`Prompt not supported: ${prompt.name}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,6 +14,7 @@ import koaConsoleRedirectProxy from '#src/middleware/koa-console-redirect-proxy.
|
||||||
import koaErrorHandler from '#src/middleware/koa-error-handler.js';
|
import koaErrorHandler from '#src/middleware/koa-error-handler.js';
|
||||||
import koaI18next from '#src/middleware/koa-i18next.js';
|
import koaI18next from '#src/middleware/koa-i18next.js';
|
||||||
import koaOIDCErrorHandler from '#src/middleware/koa-oidc-error-handler.js';
|
import koaOIDCErrorHandler from '#src/middleware/koa-oidc-error-handler.js';
|
||||||
|
import koaSecurityHeaders from '#src/middleware/koa-security-headers.js';
|
||||||
import koaSlonikErrorHandler from '#src/middleware/koa-slonik-error-handler.js';
|
import koaSlonikErrorHandler from '#src/middleware/koa-slonik-error-handler.js';
|
||||||
import koaSpaProxy from '#src/middleware/koa-spa-proxy.js';
|
import koaSpaProxy from '#src/middleware/koa-spa-proxy.js';
|
||||||
import koaSpaSessionGuard from '#src/middleware/koa-spa-session-guard.js';
|
import koaSpaSessionGuard from '#src/middleware/koa-spa-session-guard.js';
|
||||||
|
@ -69,6 +70,7 @@ export default class Tenant implements TenantContext {
|
||||||
app.use(koaConnectorErrorHandler());
|
app.use(koaConnectorErrorHandler());
|
||||||
app.use(koaI18next());
|
app.use(koaI18next());
|
||||||
app.use(koaCompress());
|
app.use(koaCompress());
|
||||||
|
app.use(koaSecurityHeaders(mountedApps, id));
|
||||||
|
|
||||||
// Mount OIDC
|
// Mount OIDC
|
||||||
const provider = initOidc(id, envSet, queries, libraries);
|
const provider = initOidc(id, envSet, queries, libraries);
|
||||||
|
@ -82,6 +84,7 @@ export default class Tenant implements TenantContext {
|
||||||
libraries,
|
libraries,
|
||||||
envSet,
|
envSet,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mount APIs
|
// Mount APIs
|
||||||
app.use(mount('/api', initApis(tenantContext)));
|
app.use(mount('/api', initApis(tenantContext)));
|
||||||
|
|
||||||
|
@ -126,6 +129,7 @@ export default class Tenant implements TenantContext {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
|
|
||||||
const { isPathBasedMultiTenancy, adminUrlSet } = EnvSet.values;
|
const { isPathBasedMultiTenancy, adminUrlSet } = EnvSet.values;
|
||||||
|
|
||||||
this.run =
|
this.run =
|
||||||
isPathBasedMultiTenancy &&
|
isPathBasedMultiTenancy &&
|
||||||
// If admin URL Set is specified, consider that URL first
|
// If admin URL Set is specified, consider that URL first
|
||||||
|
|
|
@ -7,8 +7,10 @@ import de from './locales/de/index.js';
|
||||||
import en from './locales/en/index.js';
|
import en from './locales/en/index.js';
|
||||||
import es from './locales/es/index.js';
|
import es from './locales/es/index.js';
|
||||||
import fr from './locales/fr/index.js';
|
import fr from './locales/fr/index.js';
|
||||||
|
import it from './locales/it/index.js';
|
||||||
import ja from './locales/ja/index.js';
|
import ja from './locales/ja/index.js';
|
||||||
import ko from './locales/ko/index.js';
|
import ko from './locales/ko/index.js';
|
||||||
|
import plPL from './locales/pl-pl/index.js';
|
||||||
import ptBR from './locales/pt-br/index.js';
|
import ptBR from './locales/pt-br/index.js';
|
||||||
import ptPT from './locales/pt-pt/index.js';
|
import ptPT from './locales/pt-pt/index.js';
|
||||||
import ru from './locales/ru/index.js';
|
import ru from './locales/ru/index.js';
|
||||||
|
@ -27,8 +29,10 @@ export const builtInLanguages = [
|
||||||
'en',
|
'en',
|
||||||
'es',
|
'es',
|
||||||
'fr',
|
'fr',
|
||||||
|
'it',
|
||||||
'ja',
|
'ja',
|
||||||
'ko',
|
'ko',
|
||||||
|
'pl-PL',
|
||||||
'pt-PT',
|
'pt-PT',
|
||||||
'pt-BR',
|
'pt-BR',
|
||||||
'ru',
|
'ru',
|
||||||
|
@ -54,8 +58,10 @@ const resource: Resource = {
|
||||||
en,
|
en,
|
||||||
es,
|
es,
|
||||||
fr,
|
fr,
|
||||||
|
it,
|
||||||
ja,
|
ja,
|
||||||
ko,
|
ko,
|
||||||
|
'pl-PL': plPL,
|
||||||
'pt-PT': ptPT,
|
'pt-PT': ptPT,
|
||||||
'pt-BR': ptBR,
|
'pt-BR': ptBR,
|
||||||
ru,
|
ru,
|
||||||
|
|
|
@ -60,6 +60,8 @@ const description = {
|
||||||
create_your_account: 'Erstelle dein Konto',
|
create_your_account: 'Erstelle dein Konto',
|
||||||
sign_in_to_your_account: 'Melde dich in deinem Konto an',
|
sign_in_to_your_account: 'Melde dich in deinem Konto an',
|
||||||
no_region_code_found: 'Kein Regionencode gefunden',
|
no_region_code_found: 'Kein Regionencode gefunden',
|
||||||
|
verify_email: 'Bestätige deine E-Mail-Adresse',
|
||||||
|
verify_phone: 'Bestätige deine Telefonnummer',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default description;
|
export default description;
|
||||||
|
|
|
@ -57,6 +57,8 @@ const description = {
|
||||||
create_your_account: 'Create your account',
|
create_your_account: 'Create your account',
|
||||||
sign_in_to_your_account: 'Sign in to your account',
|
sign_in_to_your_account: 'Sign in to your account',
|
||||||
no_region_code_found: 'No region code found',
|
no_region_code_found: 'No region code found',
|
||||||
|
verify_email: 'Verify your email',
|
||||||
|
verify_phone: 'Verify your phone number',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default description;
|
export default description;
|
||||||
|
|
|
@ -58,6 +58,8 @@ const description = {
|
||||||
create_your_account: 'Cree su cuenta',
|
create_your_account: 'Cree su cuenta',
|
||||||
sign_in_to_your_account: 'Inicie sesión en su cuenta',
|
sign_in_to_your_account: 'Inicie sesión en su cuenta',
|
||||||
no_region_code_found: 'No se encontró código de región',
|
no_region_code_found: 'No se encontró código de región',
|
||||||
|
verify_email: 'Verificar su correo electrónico',
|
||||||
|
verify_phone: 'Verificar su número de teléfono',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default description;
|
export default description;
|
||||||
|
|
|
@ -60,6 +60,8 @@ const description = {
|
||||||
create_your_account: 'Créer votre compte',
|
create_your_account: 'Créer votre compte',
|
||||||
sign_in_to_your_account: 'Connecte-toi à ton compte',
|
sign_in_to_your_account: 'Connecte-toi à ton compte',
|
||||||
no_region_code_found: 'Aucun code de région trouvé',
|
no_region_code_found: 'Aucun code de région trouvé',
|
||||||
|
verify_email: 'Vérifiez votre e-mail',
|
||||||
|
verify_phone: 'Vérifiez votre numéro de téléphone',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default description;
|
export default description;
|
||||||
|
|
29
packages/phrases-ui/src/locales/it/action.ts
Normal file
29
packages/phrases-ui/src/locales/it/action.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
const action = {
|
||||||
|
sign_in: 'Accedi',
|
||||||
|
continue: 'Continua',
|
||||||
|
create_account: 'Crea account',
|
||||||
|
create_account_without_linking: 'Crea account senza collegare',
|
||||||
|
create: 'Crea',
|
||||||
|
enter_passcode: 'Inserisci il codice di verifica',
|
||||||
|
confirm: 'Conferma',
|
||||||
|
cancel: 'Annulla',
|
||||||
|
save_password: 'Salva password',
|
||||||
|
bind: 'Collega con {{address}}',
|
||||||
|
bind_and_continue: 'Collega e continua',
|
||||||
|
back: 'Torna indietro',
|
||||||
|
nav_back: 'Indietro',
|
||||||
|
agree: 'Accetto',
|
||||||
|
got_it: 'Capito',
|
||||||
|
sign_in_with: 'Continua con {{name}}',
|
||||||
|
forgot_password: 'Hai dimenticato la password?',
|
||||||
|
switch_to: 'Passa a {{method}}',
|
||||||
|
sign_in_via_passcode: 'Accedi con il codice di verifica',
|
||||||
|
sign_in_via_password: 'Accedi con la password',
|
||||||
|
change: 'Cambia {{method}}',
|
||||||
|
link_another_email: 'Collega un altro indirizzo email',
|
||||||
|
link_another_phone: 'Collega un altro numero di telefono',
|
||||||
|
link_another_email_or_phone: 'Collega un altro indirizzo email o numero di telefono',
|
||||||
|
show_password: 'Mostra password',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default action;
|
5
packages/phrases-ui/src/locales/it/demo-app.ts
Normal file
5
packages/phrases-ui/src/locales/it/demo-app.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
const demo_app = {
|
||||||
|
notification: "Suggerimento: crea prima un account per testare l'esperienza di accesso.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default demo_app;
|
63
packages/phrases-ui/src/locales/it/description.ts
Normal file
63
packages/phrases-ui/src/locales/it/description.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
const description = {
|
||||||
|
email: 'email',
|
||||||
|
phone_number: 'numero di telefono',
|
||||||
|
username: 'username',
|
||||||
|
reminder: 'Promemoria',
|
||||||
|
not_found: '404 Non trovato',
|
||||||
|
agree_with_terms: 'Ho letto e accetto i ',
|
||||||
|
agree_with_terms_modal: 'Per procedere, si prega di accettare i <link></link>.',
|
||||||
|
terms_of_use: 'Termini di utilizzo',
|
||||||
|
sign_in: 'Accedi',
|
||||||
|
privacy_policy: 'Informativa sulla privacy',
|
||||||
|
create_account: 'Crea account',
|
||||||
|
or: 'o',
|
||||||
|
and: 'e',
|
||||||
|
enter_passcode: 'Il codice di verifica è stato inviato alla tua {{address}} {{target}}',
|
||||||
|
passcode_sent: 'Il codice di verifica è stato inviato di nuovo',
|
||||||
|
resend_after_seconds: 'Inviare di nuovo dopo <span>{{seconds}}</span> secondi',
|
||||||
|
resend_passcode: 'Inviare nuovamente il codice di verifica',
|
||||||
|
create_account_id_exists: "L'account con {{type}} {{value}} già esiste, vuoi accedere?",
|
||||||
|
link_account_id_exists: "L'account con {{type}} {{value}} è già esistente. Vuoi collegarlo?",
|
||||||
|
sign_in_id_does_not_exist:
|
||||||
|
"L'account con {{type}} {{value}} non esiste, vuoi creare un nuovo account?",
|
||||||
|
sign_in_id_does_not_exist_alert: "L'account con {{type}} {{value}} non esiste.",
|
||||||
|
create_account_id_exists_alert:
|
||||||
|
"L'account {{type}} {{value}} è collegato ad un altro account. Prova con un altro {{type}}.",
|
||||||
|
social_identity_exist:
|
||||||
|
"L'{{type}} {{value}} è collegato ad un altro account. Prova con un altro {{type}}.",
|
||||||
|
bind_account_title: 'Collega o crea un account',
|
||||||
|
social_create_account: 'Puoi creare un nuovo account.',
|
||||||
|
social_link_email: "Puoi collegare un'altra email",
|
||||||
|
social_link_phone: 'Puoi collegare un altro telefono',
|
||||||
|
social_link_email_or_phone: "Puoi collegare un'altra email o telefono",
|
||||||
|
social_bind_with_existing: 'Abbiamo trovato un account correlato, puoi collegarlo direttamente.',
|
||||||
|
reset_password: 'Resetta la password',
|
||||||
|
reset_password_description:
|
||||||
|
'Inserisci il {{types, list(type: disjunction;)}} associato al tuo account, e ti invieremo il codice di verifica per resettare la password.',
|
||||||
|
new_password: 'Nuova password',
|
||||||
|
set_password: 'Imposta una password',
|
||||||
|
password_changed: 'Password cambiata',
|
||||||
|
no_account: 'Ancora nessun account? ',
|
||||||
|
have_account: 'Hai già un account?',
|
||||||
|
enter_password: 'Inserisci la password',
|
||||||
|
enter_password_for: 'Accedi con la password per {{method}} {{value}}',
|
||||||
|
enter_username: 'Imposta username',
|
||||||
|
enter_username_description:
|
||||||
|
"L'username è un'alternativa per l'accesso. L'username deve contenere solo lettere, numeri e trattini bassi.",
|
||||||
|
link_email: 'Collega emails',
|
||||||
|
link_phone: 'Collega telefono',
|
||||||
|
link_email_or_phone: 'Collega email o telefono',
|
||||||
|
link_email_description: "Per maggiore sicurezza, collega la tua email all'account.",
|
||||||
|
link_phone_description: "Per maggiore sicurezza, collega il tuo telefono all'account.",
|
||||||
|
link_email_or_phone_description:
|
||||||
|
"Per maggiore sicurezza, collega la tua email o il tuo telefono all'account.",
|
||||||
|
continue_with_more_information:
|
||||||
|
"Per maggiore sicurezza, completa i dettagli dell'account qui sotto.",
|
||||||
|
create_your_account: 'Crea il tuo account',
|
||||||
|
sign_in_to_your_account: 'Accedi al tuo account',
|
||||||
|
no_region_code_found: 'Nessun codice di regione trovato',
|
||||||
|
verify_email: 'Verifica la tua email',
|
||||||
|
verify_phone: 'Verifica il tuo numero di telefono',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default description;
|
23
packages/phrases-ui/src/locales/it/error.ts
Normal file
23
packages/phrases-ui/src/locales/it/error.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
const error = {
|
||||||
|
general_required: `{{types, list(type: disjunction;)}} è richiesto`,
|
||||||
|
general_invalid: `Il {{types, list(type: disjunction;)}} non è valido`,
|
||||||
|
username_required: 'Username è richiesto',
|
||||||
|
password_required: 'La password è richiesta',
|
||||||
|
username_exists: 'Username esiste già',
|
||||||
|
username_should_not_start_with_number: "L'username non dovrebbe iniziare con un numero",
|
||||||
|
username_invalid_charset: "L'username dovrebbe contenere solo lettere, numeri o underscore.",
|
||||||
|
invalid_email: "L'email non è valida",
|
||||||
|
invalid_phone: 'Il numero di telefono non è valido',
|
||||||
|
password_min_length: 'La password richiede un minimo di {{min}} caratteri',
|
||||||
|
invalid_password:
|
||||||
|
'La password richiede un minimo di {{min}} caratteri e contiene una combinazione di lettere, numeri e simboli.',
|
||||||
|
passwords_do_not_match: 'Le password non corrispondono. Per favore prova di nuovo.',
|
||||||
|
invalid_passcode: 'Il codice di verifica non è valido',
|
||||||
|
invalid_connector_auth: "L'autorizzazione è invalida",
|
||||||
|
invalid_connector_request: 'I dati del connettore non sono validi',
|
||||||
|
unknown: 'Errore sconosciuto. Si prega di riprovare più tardi.',
|
||||||
|
invalid_session: 'Sessione non trovata. Si prega di tornare indietro e accedere di nuovo.',
|
||||||
|
timeout: 'Timeout della richiesta. Si prega di riprovare più tardi.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default error;
|
21
packages/phrases-ui/src/locales/it/index.ts
Normal file
21
packages/phrases-ui/src/locales/it/index.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { type LocalePhrase } from '../../types.js';
|
||||||
|
|
||||||
|
import action from './action.js';
|
||||||
|
import demo_app from './demo-app.js';
|
||||||
|
import description from './description.js';
|
||||||
|
import error from './error.js';
|
||||||
|
import input from './input.js';
|
||||||
|
import secondary from './secondary.js';
|
||||||
|
|
||||||
|
const it: LocalePhrase = Object.freeze({
|
||||||
|
translation: {
|
||||||
|
input,
|
||||||
|
secondary,
|
||||||
|
action,
|
||||||
|
description,
|
||||||
|
error,
|
||||||
|
demo_app,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default it;
|
10
packages/phrases-ui/src/locales/it/input.ts
Normal file
10
packages/phrases-ui/src/locales/it/input.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
const input = {
|
||||||
|
username: 'Nome utente',
|
||||||
|
password: 'Password',
|
||||||
|
email: 'Email',
|
||||||
|
phone_number: 'Numero di telefono',
|
||||||
|
confirm_password: 'Conferma password',
|
||||||
|
search_region_code: 'Codice regione di ricerca',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default input;
|
6
packages/phrases-ui/src/locales/it/secondary.ts
Normal file
6
packages/phrases-ui/src/locales/it/secondary.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const secondary = {
|
||||||
|
social_bind_with:
|
||||||
|
'Già un account? Accedi per collegare {{methods, list(type: disjunction;)}} con la tua identità sociale.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default secondary;
|
|
@ -58,6 +58,8 @@ const description = {
|
||||||
create_your_account: 'アカウントを作成する',
|
create_your_account: 'アカウントを作成する',
|
||||||
sign_in_to_your_account: 'アカウントにサインインする',
|
sign_in_to_your_account: 'アカウントにサインインする',
|
||||||
no_region_code_found: '地域コードが見つかりません',
|
no_region_code_found: '地域コードが見つかりません',
|
||||||
|
verify_email: 'Eメールを確認する',
|
||||||
|
verify_phone: '電話番号を確認する',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default description;
|
export default description;
|
||||||
|
|
|
@ -53,6 +53,8 @@ const description = {
|
||||||
create_your_account: '계정 생성하기',
|
create_your_account: '계정 생성하기',
|
||||||
sign_in_to_your_account: '계정에 로그인하세요',
|
sign_in_to_your_account: '계정에 로그인하세요',
|
||||||
no_region_code_found: '지역 코드를 찾을 수 없습니다.',
|
no_region_code_found: '지역 코드를 찾을 수 없습니다.',
|
||||||
|
verify_email: '이메일 인증',
|
||||||
|
verify_phone: '휴대전화번호 인증',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default description;
|
export default description;
|
||||||
|
|
29
packages/phrases-ui/src/locales/pl-pl/action.ts
Normal file
29
packages/phrases-ui/src/locales/pl-pl/action.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
const action = {
|
||||||
|
sign_in: 'Zaloguj się',
|
||||||
|
continue: 'Kontynuuj',
|
||||||
|
create_account: 'Utwórz konto',
|
||||||
|
create_account_without_linking: 'Utwórz konto bez łączenia',
|
||||||
|
create: 'Utwórz',
|
||||||
|
enter_passcode: 'Wprowadź kod weryfikacyjny',
|
||||||
|
confirm: 'Potwierdź',
|
||||||
|
cancel: 'Anuluj',
|
||||||
|
save_password: 'Zapisz hasło',
|
||||||
|
bind: 'Połącz z {{address}}',
|
||||||
|
bind_and_continue: 'Połącz i kontynuuj',
|
||||||
|
back: 'Wróć',
|
||||||
|
nav_back: 'Wstecz',
|
||||||
|
agree: 'Zgadzam się',
|
||||||
|
got_it: 'Zrozumiałem',
|
||||||
|
sign_in_with: 'Kontynuuj z {{name}}',
|
||||||
|
forgot_password: 'Zapomniałeś hasła?',
|
||||||
|
switch_to: 'Przełącz na {{method}}',
|
||||||
|
sign_in_via_passcode: 'Zaloguj się za pomocą kodu weryfikacyjnego',
|
||||||
|
sign_in_via_password: 'Zaloguj się za pomocą hasła',
|
||||||
|
change: 'Zmień {{method}}',
|
||||||
|
link_another_email: 'Połącz inne konto email',
|
||||||
|
link_another_phone: 'Połącz inne konto telefoniczne',
|
||||||
|
link_another_email_or_phone: 'Połącz inne konto email lub telefoniczne',
|
||||||
|
show_password: 'Pokaż hasło',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default action;
|
5
packages/phrases-ui/src/locales/pl-pl/demo-app.ts
Normal file
5
packages/phrases-ui/src/locales/pl-pl/demo-app.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
const demo_app = {
|
||||||
|
notification: 'Wskazówka: Najpierw stwórz konto, aby przetestować proces logowania.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default demo_app;
|
63
packages/phrases-ui/src/locales/pl-pl/description.ts
Normal file
63
packages/phrases-ui/src/locales/pl-pl/description.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
const description = {
|
||||||
|
email: 'adres email',
|
||||||
|
phone_number: 'numer telefonu',
|
||||||
|
username: 'nazwa użytkownika',
|
||||||
|
reminder: 'Przypomnienie',
|
||||||
|
not_found: '404 Nie znaleziono',
|
||||||
|
agree_with_terms: 'Przeczytałem/am i zgadzam się z ',
|
||||||
|
agree_with_terms_modal: 'Do kontynuacji należy zaakceptować <link></link>.',
|
||||||
|
terms_of_use: 'Warunki korzystania',
|
||||||
|
sign_in: 'Zaloguj się',
|
||||||
|
privacy_policy: 'Polityka prywatności',
|
||||||
|
create_account: 'Utwórz konto',
|
||||||
|
or: 'lub',
|
||||||
|
and: 'i',
|
||||||
|
enter_passcode: 'Kod weryfikacyjny został wysłany na twoje {{address}} {{target}}',
|
||||||
|
passcode_sent: 'Kod weryfikacyjny został wysłany ponownie',
|
||||||
|
resend_after_seconds: 'Wyślij ponownie po <span>{{seconds}}</span> sekundach',
|
||||||
|
resend_passcode: 'Wyślij kod weryfikacyjny ponownie',
|
||||||
|
create_account_id_exists: 'Konto z {{type}} {{value}} już istnieje. Czy chcesz się zalogować?',
|
||||||
|
link_account_id_exists: 'Konto z {{type}} {{value}} już istnieje. Czy chcesz je połączyć?',
|
||||||
|
sign_in_id_does_not_exist:
|
||||||
|
'Konto z {{type}} {{value}} nie istnieje. Czy chcesz utworzyć nowe konto?',
|
||||||
|
sign_in_id_does_not_exist_alert: 'Konto z {{type}} {{value}} nie istnieje.',
|
||||||
|
create_account_id_exists_alert:
|
||||||
|
'Konto z {{type}} {{value}} jest połączone z innym kontem. Spróbuj inny {{type}}.',
|
||||||
|
social_identity_exist: '{{type}} {{value}} jest połączony z innym kontem. Spróbuj inny {{type}}.',
|
||||||
|
bind_account_title: 'Połącz lub utwórz konto',
|
||||||
|
social_create_account: 'Możesz utworzyć nowe konto.',
|
||||||
|
social_link_email: 'Możesz połączyć kolejny adres email',
|
||||||
|
social_link_phone: 'Możesz połączyć kolejny numer telefonu',
|
||||||
|
social_link_email_or_phone: 'Możesz połączyć kolejny adres email lub numer telefonu',
|
||||||
|
social_bind_with_existing: 'Znaleźliśmy powiązane konto, możesz je połączyć bezpośrednio.',
|
||||||
|
reset_password: 'Zresetuj hasło',
|
||||||
|
reset_password_description:
|
||||||
|
'Wpisz {{types, lista(type: złączonych;)}} związanego z twoim kontem, a wyślemy ci kod weryfikacyjny do zresetowania hasła.',
|
||||||
|
new_password: 'Nowe hasło',
|
||||||
|
set_password: 'Ustaw hasło',
|
||||||
|
password_changed: 'Hasło zmienione',
|
||||||
|
no_account: 'Nie masz jeszcze konta? ',
|
||||||
|
have_account: 'Masz już konto?',
|
||||||
|
enter_password: 'Wpisz hasło',
|
||||||
|
enter_password_for: 'Zaloguj się przy użyciu hasła do {{method}} {{value}}',
|
||||||
|
enter_username: 'Ustaw nazwę użytkownika',
|
||||||
|
enter_username_description:
|
||||||
|
'Nazwa użytkownika jest alternatywną formą logowania. Nazwa użytkownika powinna zawierać tylko litery, cyfry i podkreślenia.',
|
||||||
|
link_email: 'Połącz adres email',
|
||||||
|
link_phone: 'Połącz numer telefonu',
|
||||||
|
link_email_or_phone: 'Połącz adres email lub numer telefonu',
|
||||||
|
link_email_description: 'Dla zwiększenia bezpieczeństwa, proszę połączyć konto z adresem e-mail.',
|
||||||
|
link_phone_description:
|
||||||
|
'Dla zwiększenia bezpieczeństwa, proszę połączyć konto z numerem telefonu.',
|
||||||
|
link_email_or_phone_description:
|
||||||
|
'Dla zwiększenia bezpieczeństwa, proszę połączyć konto z adresem e-mail lub numerem telefonu.',
|
||||||
|
continue_with_more_information:
|
||||||
|
'Dla zwiększenia bezpieczeństwa, proszę uzupełnić poniższe informacje o koncie.',
|
||||||
|
create_your_account: 'Utwórz konto',
|
||||||
|
sign_in_to_your_account: 'Zaloguj się do swojego konta',
|
||||||
|
no_region_code_found: 'Nie znaleziono kodu regionu',
|
||||||
|
verify_email: 'Potwierdź swój email',
|
||||||
|
verify_phone: 'Potwierdź swój numer telefonu',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default description;
|
24
packages/phrases-ui/src/locales/pl-pl/error.ts
Normal file
24
packages/phrases-ui/src/locales/pl-pl/error.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
const error = {
|
||||||
|
general_required: `{{types, list(type: disjunction;)}} jest wymagany`,
|
||||||
|
general_invalid: `{{types, list(type: disjunction;)}} jest nieprawidłowe`,
|
||||||
|
username_required: 'Nazwa użytkownika jest wymagana',
|
||||||
|
password_required: 'Hasło jest wymagane',
|
||||||
|
username_exists: 'Nazwa użytkownika już istnieje',
|
||||||
|
username_should_not_start_with_number: 'Nazwa użytkownika nie powinna zaczynać się od liczby',
|
||||||
|
username_invalid_charset:
|
||||||
|
'Nazwa użytkownika powinna zawierać tylko litery, liczby lub podkreślenia.',
|
||||||
|
invalid_email: 'Nieprawidłowy adres e-mail',
|
||||||
|
invalid_phone: 'Nieprawidłowy numer telefonu',
|
||||||
|
password_min_length: 'Hasło wymaga minimum {{min}} znaków',
|
||||||
|
invalid_password:
|
||||||
|
'Hasło wymaga minimum {{min}} znaków i zawiera kombinację liter, cyfr oraz symboli.',
|
||||||
|
passwords_do_not_match: 'Hasła nie pasują do siebie. Proszę spróbuj ponownie.',
|
||||||
|
invalid_passcode: 'Nieprawidłowy kod weryfikacyjny',
|
||||||
|
invalid_connector_auth: 'Nieprawidłowa autoryzacja',
|
||||||
|
invalid_connector_request: 'Nieprawidłowe dane konektora',
|
||||||
|
unknown: 'Nieznany błąd. Proszę spróbuj ponownie później.',
|
||||||
|
invalid_session: 'Sesja nie znaleziona. Proszę wróć i zaloguj się ponownie.',
|
||||||
|
timeout: 'Czas żądania upłynął. Proszę spróbuj ponownie później.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default error;
|
21
packages/phrases-ui/src/locales/pl-pl/index.ts
Normal file
21
packages/phrases-ui/src/locales/pl-pl/index.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { type LocalePhrase } from '../../types.js';
|
||||||
|
|
||||||
|
import action from './action.js';
|
||||||
|
import demo_app from './demo-app.js';
|
||||||
|
import description from './description.js';
|
||||||
|
import error from './error.js';
|
||||||
|
import input from './input.js';
|
||||||
|
import secondary from './secondary.js';
|
||||||
|
|
||||||
|
const plPL: LocalePhrase = Object.freeze({
|
||||||
|
translation: {
|
||||||
|
input,
|
||||||
|
secondary,
|
||||||
|
action,
|
||||||
|
description,
|
||||||
|
error,
|
||||||
|
demo_app,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default plPL;
|
10
packages/phrases-ui/src/locales/pl-pl/input.ts
Normal file
10
packages/phrases-ui/src/locales/pl-pl/input.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
const input = {
|
||||||
|
username: 'Nazwa użytkownika',
|
||||||
|
password: 'Hasło',
|
||||||
|
email: 'E-mail',
|
||||||
|
phone_number: 'Numer telefonu',
|
||||||
|
confirm_password: 'Potwierdź hasło',
|
||||||
|
search_region_code: 'Kod regionu wyszukiwania',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default input;
|
6
packages/phrases-ui/src/locales/pl-pl/secondary.ts
Normal file
6
packages/phrases-ui/src/locales/pl-pl/secondary.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const secondary = {
|
||||||
|
social_bind_with:
|
||||||
|
'Masz już konto? Zaloguj się, aby połączyć {{methods, list(type: disjunction;)}} z twoją tożsamością społeczną.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default secondary;
|
|
@ -55,6 +55,8 @@ const description = {
|
||||||
create_your_account: 'Crie sua conta',
|
create_your_account: 'Crie sua conta',
|
||||||
sign_in_to_your_account: 'Faça login na sua conta',
|
sign_in_to_your_account: 'Faça login na sua conta',
|
||||||
no_region_code_found: 'Não foi possível encontrar o código de região do seu telefone.',
|
no_region_code_found: 'Não foi possível encontrar o código de região do seu telefone.',
|
||||||
|
verify_email: 'Verificar e-mail',
|
||||||
|
verify_phone: 'Verificar número de telefone',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default description;
|
export default description;
|
||||||
|
|
|
@ -55,6 +55,8 @@ const description = {
|
||||||
create_your_account: 'Crie a sua conta',
|
create_your_account: 'Crie a sua conta',
|
||||||
sign_in_to_your_account: 'Inicie sessão na sua conta',
|
sign_in_to_your_account: 'Inicie sessão na sua conta',
|
||||||
no_region_code_found: 'Não foi possível encontrar o código de região do seu telefone.',
|
no_region_code_found: 'Não foi possível encontrar o código de região do seu telefone.',
|
||||||
|
verify_email: 'Verifique o seu email',
|
||||||
|
verify_phone: 'Verifique o seu número de telefone',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default description;
|
export default description;
|
||||||
|
|
|
@ -59,6 +59,8 @@ const description = {
|
||||||
create_your_account: 'Создайте свой аккаунт',
|
create_your_account: 'Создайте свой аккаунт',
|
||||||
sign_in_to_your_account: 'Войди в свой аккаунт',
|
sign_in_to_your_account: 'Войди в свой аккаунт',
|
||||||
no_region_code_found: 'Не удалось определить код региона',
|
no_region_code_found: 'Не удалось определить код региона',
|
||||||
|
verify_email: 'Подтвердите Ваш электронный адрес',
|
||||||
|
verify_phone: 'Подтвердите свой номер телефона',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default description;
|
export default description;
|
||||||
|
|
|
@ -56,6 +56,8 @@ const description = {
|
||||||
create_your_account: 'Hesabını oluştur',
|
create_your_account: 'Hesabını oluştur',
|
||||||
sign_in_to_your_account: 'Hesabına giriş yap',
|
sign_in_to_your_account: 'Hesabına giriş yap',
|
||||||
no_region_code_found: 'Bölge kodu bulunamadı',
|
no_region_code_found: 'Bölge kodu bulunamadı',
|
||||||
|
verify_email: 'E-postanızın doğrulanması',
|
||||||
|
verify_phone: 'Telefon numaranızın doğrulanması',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default description;
|
export default description;
|
||||||
|
|
|
@ -49,6 +49,8 @@ const description = {
|
||||||
create_your_account: '注册你的账号',
|
create_your_account: '注册你的账号',
|
||||||
sign_in_to_your_account: '登录你的账号',
|
sign_in_to_your_account: '登录你的账号',
|
||||||
no_region_code_found: '没有找到区域码',
|
no_region_code_found: '没有找到区域码',
|
||||||
|
verify_email: '验证你的邮箱',
|
||||||
|
verify_phone: '验证你的手机号',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default description;
|
export default description;
|
||||||
|
|
|
@ -49,6 +49,8 @@ const description = {
|
||||||
create_your_account: '註冊你的帳號',
|
create_your_account: '註冊你的帳號',
|
||||||
sign_in_to_your_account: '登錄你的帳號',
|
sign_in_to_your_account: '登錄你的帳號',
|
||||||
no_region_code_found: '沒有找到區域碼',
|
no_region_code_found: '沒有找到區域碼',
|
||||||
|
verify_email: '驗證你的郵箱',
|
||||||
|
verify_phone: '驗證你的手機號',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default description;
|
export default description;
|
||||||
|
|
|
@ -49,6 +49,8 @@ const description = {
|
||||||
create_your_account: '註冊你的帳號',
|
create_your_account: '註冊你的帳號',
|
||||||
sign_in_to_your_account: '登錄你的帳號',
|
sign_in_to_your_account: '登錄你的帳號',
|
||||||
no_region_code_found: '沒有找到區域碼',
|
no_region_code_found: '沒有找到區域碼',
|
||||||
|
verify_email: '驗證你的郵箱',
|
||||||
|
verify_phone: '驗證你的手機號碼',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default description;
|
export default description;
|
||||||
|
|
|
@ -7,8 +7,10 @@ import de from './locales/de/index.js';
|
||||||
import en from './locales/en/index.js';
|
import en from './locales/en/index.js';
|
||||||
import es from './locales/es/index.js';
|
import es from './locales/es/index.js';
|
||||||
import fr from './locales/fr/index.js';
|
import fr from './locales/fr/index.js';
|
||||||
|
import it from './locales/it/index.js';
|
||||||
import ja from './locales/ja/index.js';
|
import ja from './locales/ja/index.js';
|
||||||
import ko from './locales/ko/index.js';
|
import ko from './locales/ko/index.js';
|
||||||
|
import plPL from './locales/pl-pl/index.js';
|
||||||
import ptBR from './locales/pt-br/index.js';
|
import ptBR from './locales/pt-br/index.js';
|
||||||
import ptPT from './locales/pt-pt/index.js';
|
import ptPT from './locales/pt-pt/index.js';
|
||||||
import ru from './locales/ru/index.js';
|
import ru from './locales/ru/index.js';
|
||||||
|
@ -27,8 +29,10 @@ export const builtInLanguages = [
|
||||||
'en',
|
'en',
|
||||||
'es',
|
'es',
|
||||||
'fr',
|
'fr',
|
||||||
|
'it',
|
||||||
'ja',
|
'ja',
|
||||||
'ko',
|
'ko',
|
||||||
|
'pl-PL',
|
||||||
'pt-BR',
|
'pt-BR',
|
||||||
'pt-PT',
|
'pt-PT',
|
||||||
'ru',
|
'ru',
|
||||||
|
@ -66,8 +70,10 @@ const resource: Resource = {
|
||||||
en,
|
en,
|
||||||
es,
|
es,
|
||||||
fr,
|
fr,
|
||||||
|
it,
|
||||||
ja,
|
ja,
|
||||||
ko,
|
ko,
|
||||||
|
'pl-PL': plPL,
|
||||||
'pt-BR': ptBR,
|
'pt-BR': ptBR,
|
||||||
'pt-PT': ptPT,
|
'pt-PT': ptPT,
|
||||||
ru,
|
ru,
|
||||||
|
|
13
packages/phrases/src/locales/it/errors/auth.ts
Normal file
13
packages/phrases/src/locales/it/errors/auth.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
const auth = {
|
||||||
|
authorization_header_missing: 'Header Autorizzazione mancante.',
|
||||||
|
authorization_token_type_not_supported: 'Tipo di autorizzazione non supportato.',
|
||||||
|
unauthorized: 'Non autorizzato. Controlla le credenziali e il loro ambito.',
|
||||||
|
forbidden: "Vietato. Controlla i ruoli e le autorizzazioni dell'utente.",
|
||||||
|
expected_role_not_found:
|
||||||
|
"Ruolo atteso non trovato. Controlla i ruoli e le autorizzazioni dell'utente.",
|
||||||
|
jwt_sub_missing: 'Manca il valore `sub` in JWT.',
|
||||||
|
require_re_authentication:
|
||||||
|
"È necessaria una nuova autenticazione per eseguire un'azione protetta.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default auth;
|
40
packages/phrases/src/locales/it/errors/connector.ts
Normal file
40
packages/phrases/src/locales/it/errors/connector.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
const connector = {
|
||||||
|
general: 'Si è verificato un errore nel connettore: {{errorDescription}}',
|
||||||
|
not_found: 'Impossibile trovare un connettore disponibile per il tipo: {{type}}.',
|
||||||
|
not_enabled: 'Il connettore non è abilitato.',
|
||||||
|
invalid_metadata: 'I metadati del connettore non sono validi.',
|
||||||
|
invalid_config_guard: 'La guardia di configurazione del connettore non è valida.',
|
||||||
|
unexpected_type: 'Il tipo di connettore è inaspettato.',
|
||||||
|
invalid_request_parameters: 'La richiesta contiene parametri di input errati.',
|
||||||
|
insufficient_request_parameters: 'La richiesta potrebbe mancare di alcuni parametri di input.',
|
||||||
|
invalid_config: 'La configurazione del connettore non è valida.',
|
||||||
|
invalid_response: 'La risposta del connettore non è valida.',
|
||||||
|
template_not_found:
|
||||||
|
'Impossibile trovare il modello corretto nella configurazione del connettore.',
|
||||||
|
not_implemented: '{{method}}: non è stato ancora implementato.',
|
||||||
|
social_invalid_access_token: 'Il token di accesso del connettore non è valido.',
|
||||||
|
invalid_auth_code: 'Il codice di autenticazione del connettore non è valido.',
|
||||||
|
social_invalid_id_token: 'Il token ID del connettore non è valido.',
|
||||||
|
authorization_failed: "Il processo di autorizzazione dell'utente non è riuscito.",
|
||||||
|
social_auth_code_invalid:
|
||||||
|
'Impossibile ottenere il token di accesso, controllare il codice di autorizzazione.',
|
||||||
|
more_than_one_sms: 'Il numero di connettori SMS è maggiore di 1.',
|
||||||
|
more_than_one_email: 'Il numero di connettori email è maggiore di 1.',
|
||||||
|
more_than_one_connector_factory:
|
||||||
|
'Trovate più fabbriche di connettori (con id {{connectorIds}}), è possibile disinstallare quelle non necessarie.',
|
||||||
|
db_connector_type_mismatch: "C'è un connettore nel DB che non corrisponde al tipo.",
|
||||||
|
not_found_with_connector_id:
|
||||||
|
"Impossibile trovare il connettore con l'id connettore standard fornito.",
|
||||||
|
multiple_instances_not_supported:
|
||||||
|
"Non è possibile creare più di un'istanza con il connettore standard selezionato.",
|
||||||
|
invalid_type_for_syncing_profile:
|
||||||
|
'È possibile sincronizzare solo il profilo utente con i connettori social.',
|
||||||
|
can_not_modify_target: "Non è possibile modificare il 'target' del connettore.",
|
||||||
|
should_specify_target: "Si dovrebbe specificare il 'target'.",
|
||||||
|
multiple_target_with_same_platform:
|
||||||
|
'Non è possibile avere più connettori social con lo stesso target e piattaforma.',
|
||||||
|
cannot_overwrite_metadata_for_non_standard_connector:
|
||||||
|
"I 'metadati' di questo connettore non possono essere sovrascritti.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connector;
|
8
packages/phrases/src/locales/it/errors/entity.ts
Normal file
8
packages/phrases/src/locales/it/errors/entity.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
const entity = {
|
||||||
|
create_failed: 'Impossibile creare {{name}}.',
|
||||||
|
not_exists: '{{name}} non esiste.',
|
||||||
|
not_exists_with_id: '{{name}} con ID `{{id}}` non esiste.',
|
||||||
|
not_found: 'La risorsa non esiste.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default entity;
|
9
packages/phrases/src/locales/it/errors/guard.ts
Normal file
9
packages/phrases/src/locales/it/errors/guard.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
const guard = {
|
||||||
|
invalid_input: 'La richiesta {{type}} non è valida.',
|
||||||
|
invalid_pagination: 'Il valore di paginazione della richiesta non è valido.',
|
||||||
|
can_not_get_tenant_id: "Impossibile ottenere l'ID dell'inquilino dalla richiesta.",
|
||||||
|
file_size_exceeded: 'Dimensione del file superata.',
|
||||||
|
mime_type_not_allowed: 'Il tipo MIME non è consentito.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default guard;
|
39
packages/phrases/src/locales/it/errors/index.ts
Normal file
39
packages/phrases/src/locales/it/errors/index.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import auth from './auth.js';
|
||||||
|
import connector from './connector.js';
|
||||||
|
import entity from './entity.js';
|
||||||
|
import guard from './guard.js';
|
||||||
|
import localization from './localization.js';
|
||||||
|
import log from './log.js';
|
||||||
|
import oidc from './oidc.js';
|
||||||
|
import password from './password.js';
|
||||||
|
import request from './request.js';
|
||||||
|
import role from './role.js';
|
||||||
|
import scope from './scope.js';
|
||||||
|
import session from './session.js';
|
||||||
|
import sign_in_experiences from './sign-in-experiences.js';
|
||||||
|
import storage from './storage.js';
|
||||||
|
import swagger from './swagger.js';
|
||||||
|
import user from './user.js';
|
||||||
|
import verification_code from './verification-code.js';
|
||||||
|
|
||||||
|
const errors = {
|
||||||
|
request,
|
||||||
|
auth,
|
||||||
|
guard,
|
||||||
|
oidc,
|
||||||
|
user,
|
||||||
|
password,
|
||||||
|
session,
|
||||||
|
connector,
|
||||||
|
verification_code,
|
||||||
|
sign_in_experiences,
|
||||||
|
localization,
|
||||||
|
swagger,
|
||||||
|
entity,
|
||||||
|
log,
|
||||||
|
role,
|
||||||
|
scope,
|
||||||
|
storage,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default errors;
|
7
packages/phrases/src/locales/it/errors/localization.ts
Normal file
7
packages/phrases/src/locales/it/errors/localization.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
const localization = {
|
||||||
|
cannot_delete_default_language:
|
||||||
|
'{{languageTag}} è impostato come la tua lingua predefinita e non può essere eliminato.',
|
||||||
|
invalid_translation_structure: "Schemi di dati non validi. Controlla l'input e riprova.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default localization;
|
5
packages/phrases/src/locales/it/errors/log.ts
Normal file
5
packages/phrases/src/locales/it/errors/log.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
const log = {
|
||||||
|
invalid_type: 'Il tipo di registro non è valido.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default log;
|
20
packages/phrases/src/locales/it/errors/oidc.ts
Normal file
20
packages/phrases/src/locales/it/errors/oidc.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
const oidc = {
|
||||||
|
aborted: "L'utente finale ha annullato l'interazione.",
|
||||||
|
invalid_scope: 'La scope {{scope}} non è supportata.',
|
||||||
|
invalid_scope_plural: 'Le scope {{scopes}} non sono supportate.',
|
||||||
|
invalid_token: 'Token non valido fornito.',
|
||||||
|
invalid_client_metadata: 'Metadata client non valide fornite.',
|
||||||
|
insufficient_scope: 'Token di accesso senza la scope richiesta {{scopes}}.',
|
||||||
|
invalid_request: 'La richiesta non è valida.',
|
||||||
|
invalid_grant: 'La richiesta di grant non è valida.',
|
||||||
|
invalid_redirect_uri:
|
||||||
|
'`redirect_uri` non corrisponde a nessuno degli `redirect_uris` registrati dal client.',
|
||||||
|
access_denied: 'Accesso negato.',
|
||||||
|
invalid_target: 'Indicatore di risorsa non valido.',
|
||||||
|
unsupported_grant_type: 'Tipo di `grant_type` richiesto non supportato.',
|
||||||
|
unsupported_response_mode: 'Modalità di risposta `response_mode` richiesta non supportata.',
|
||||||
|
unsupported_response_type: 'Tipo di risposta `response_type` richiesto non supportato.',
|
||||||
|
provider_error: 'Errore interno OIDC: {{message}}.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default oidc;
|
6
packages/phrases/src/locales/it/errors/password.ts
Normal file
6
packages/phrases/src/locales/it/errors/password.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const password = {
|
||||||
|
unsupported_encryption_method: 'Il metodo di crittografia {{name}} non è supportato.',
|
||||||
|
pepper_not_found: 'Pepper password non trovato. Per favore controlla le tue env di core.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default password;
|
6
packages/phrases/src/locales/it/errors/request.ts
Normal file
6
packages/phrases/src/locales/it/errors/request.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const request = {
|
||||||
|
invalid_input: "L'input non è valido. {{details}}",
|
||||||
|
general: 'Si è verificato un errore nella richiesta.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default request;
|
11
packages/phrases/src/locales/it/errors/role.ts
Normal file
11
packages/phrases/src/locales/it/errors/role.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
const role = {
|
||||||
|
name_in_use: 'Il nome di ruolo {{name}} è già in uso',
|
||||||
|
scope_exists: "L'identificatore di ambito {{scopeId}} è già stato aggiunto a questo ruolo",
|
||||||
|
user_exists: "L'identificatore di utente {{userId}} è già stato aggiunto a questo ruolo",
|
||||||
|
default_role_missing:
|
||||||
|
'Alcuni dei nomi di ruolo predefiniti non esistono nel database, assicurati di creare prima i ruoli',
|
||||||
|
internal_role_violation:
|
||||||
|
'Potresti cercare di aggiornare o eliminare un ruolo interno che è vietato da Logto. Se stai creando un nuovo ruolo, prova un altro nome che non inizi con "#internal:". ',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default role;
|
6
packages/phrases/src/locales/it/errors/scope.ts
Normal file
6
packages/phrases/src/locales/it/errors/scope.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const scope = {
|
||||||
|
name_exists: 'Il nome dello scope {{name}} è già in uso',
|
||||||
|
name_with_space: 'Il nome dello scope non può contenere spazi.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default scope;
|
26
packages/phrases/src/locales/it/errors/session.ts
Normal file
26
packages/phrases/src/locales/it/errors/session.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
const session = {
|
||||||
|
not_found: 'Sessione non trovata. Torna indietro e accedi nuovamente.',
|
||||||
|
invalid_credentials: 'Account o password non corretti. Controlla le tue credenziali.',
|
||||||
|
invalid_sign_in_method: 'Metodo di accesso corrente non disponibile.',
|
||||||
|
invalid_connector_id: 'Impossibile trovare un connettore disponibile con ID {{connectorId}}.',
|
||||||
|
insufficient_info: 'Informazioni di accesso insufficienti.',
|
||||||
|
connector_id_mismatch: "L'ID del connettore non corrisponde con il record della sessione.",
|
||||||
|
connector_session_not_found:
|
||||||
|
'Sessione del connettore non trovata. Torna indietro e accedi nuovamente.',
|
||||||
|
verification_session_not_found:
|
||||||
|
'La verifica non è stata completata con successo. Riavvia il processo di verifica e riprova.',
|
||||||
|
verification_expired:
|
||||||
|
'La connessione è scaduta. Verifica di nuovo per garantire la sicurezza del tuo account.',
|
||||||
|
unauthorized: 'Accedi prima di procedere.',
|
||||||
|
unsupported_prompt_name: 'Nome del prompt non supportato.',
|
||||||
|
forgot_password_not_enabled: 'Recupero password non abilitato.',
|
||||||
|
verification_failed:
|
||||||
|
'La verifica non è stata completata con successo. Riavvia il processo di verifica e riprova.',
|
||||||
|
connector_validation_session_not_found:
|
||||||
|
'Sessione del connettore per la convalida del token non trovata.',
|
||||||
|
identifier_not_found: 'Identificativo utente non trovato. Torna indietro e accedi nuovamente.',
|
||||||
|
interaction_not_found:
|
||||||
|
'Sessione di interazione non trovata. Torna indietro e avvia la sessione nuovamente.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default session;
|
|
@ -0,0 +1,23 @@
|
||||||
|
const sign_in_experiences = {
|
||||||
|
empty_content_url_of_terms_of_use:
|
||||||
|
'URL di contenuto "Termini di utilizzo" vuoto. Si prega di aggiungere l\'URL del contenuto se "Termini di utilizzo" è abilitato.',
|
||||||
|
empty_social_connectors:
|
||||||
|
'Connettori social vuoti. Si prega di aggiungere connettori social abilitati quando il metodo di accesso social è abilitato.',
|
||||||
|
enabled_connector_not_found: '{{type}} conettore abilitato non trovato.',
|
||||||
|
not_one_and_only_one_primary_sign_in_method:
|
||||||
|
"Deve esserci un solo metodo di accesso principale. Si prega di verificare l'input.",
|
||||||
|
username_requires_password:
|
||||||
|
"Deve abilitare impostazione di una password per l'identificatore di registrazione dell'username.",
|
||||||
|
passwordless_requires_verify:
|
||||||
|
"Deve abilitare la verifica per l'identificatore di registrazione tramite email/telefono.",
|
||||||
|
miss_sign_up_identifier_in_sign_in:
|
||||||
|
"I metodi di accesso devono contenere l'identificatore di registrazione.",
|
||||||
|
password_sign_in_must_be_enabled:
|
||||||
|
'Il metodo di accesso con password deve essere abilitato quando è richiesta la creazione di una password nella registrazione.',
|
||||||
|
code_sign_in_must_be_enabled:
|
||||||
|
'Il metodo di accesso con codice di verifica deve essere abilitato quando non è richiesta una password nella registrazione.',
|
||||||
|
unsupported_default_language: 'Questa lingua - {{language}} non è supportata al momento.',
|
||||||
|
at_least_one_authentication_factor: 'Devi selezionare almeno un fattore di autenticazione.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default sign_in_experiences;
|
7
packages/phrases/src/locales/it/errors/storage.ts
Normal file
7
packages/phrases/src/locales/it/errors/storage.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
const storage = {
|
||||||
|
not_configured: 'Provider di archiviazione non configurato.',
|
||||||
|
missing_parameter: 'Parametro mancante {{parameter}} per il provider di archiviazione.',
|
||||||
|
upload_error: 'Impossibile caricare il file sul provider di archiviazione.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default storage;
|
7
packages/phrases/src/locales/it/errors/swagger.ts
Normal file
7
packages/phrases/src/locales/it/errors/swagger.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
const swagger = {
|
||||||
|
invalid_zod_type: 'Tipo Zod non valido. Controllare la configurazione di guardia del percorso.',
|
||||||
|
not_supported_zod_type_for_params:
|
||||||
|
'Tipo Zod non supportato per i parametri. Controllare la configurazione di guardia del percorso.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default swagger;
|
33
packages/phrases/src/locales/it/errors/user.ts
Normal file
33
packages/phrases/src/locales/it/errors/user.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
const user = {
|
||||||
|
username_already_in_use: 'Questo nome utente è già in uso.',
|
||||||
|
email_already_in_use: 'Questa email è associata ad un account esistente.',
|
||||||
|
phone_already_in_use: 'Questo numero di telefono è associato ad un account esistente.',
|
||||||
|
invalid_email: 'Indirizzo email non valido.',
|
||||||
|
invalid_phone: 'Numero di telefono non valido.',
|
||||||
|
email_not_exist: "L'indirizzo email non è stato ancora registrato.",
|
||||||
|
phone_not_exist: 'Il numero di telefono non è stato ancora registrato.',
|
||||||
|
identity_not_exist: "L'account social non è stato ancora registrato.",
|
||||||
|
identity_already_in_use: "L'account social è stato associato ad un account esistente.",
|
||||||
|
social_account_exists_in_profile: 'Hai già associato questo account social.',
|
||||||
|
cannot_delete_self: 'Non puoi eliminarti da solo.',
|
||||||
|
sign_up_method_not_enabled: 'Questo metodo di registrazione non è abilitato.',
|
||||||
|
sign_in_method_not_enabled: 'Questo metodo di accesso non è abilitato.',
|
||||||
|
same_password: 'La nuova password non può essere uguale alla vecchia password.',
|
||||||
|
password_required_in_profile: 'È necessario impostare una password prima di accedere.',
|
||||||
|
new_password_required_in_profile: 'È necessario impostare una nuova password.',
|
||||||
|
password_exists_in_profile: 'La password esiste già nel tuo profilo.',
|
||||||
|
username_required_in_profile: 'È necessario impostare un nome utente prima di accedere.',
|
||||||
|
username_exists_in_profile: 'Il nome utente esiste già nel tuo profilo.',
|
||||||
|
email_required_in_profile: "È necessario aggiungere un'indirizzo email prima di accedere.",
|
||||||
|
email_exists_in_profile: 'Il tuo profilo è già associato ad un indirizzo email.',
|
||||||
|
phone_required_in_profile: 'È necessario aggiungere un numero di telefono prima di accedere.',
|
||||||
|
phone_exists_in_profile: 'Il tuo profilo è già associato ad un numero di telefono.',
|
||||||
|
email_or_phone_required_in_profile:
|
||||||
|
'È necessario aggiungere un indirizzo email o un numero di telefono prima di accedere.',
|
||||||
|
suspended: 'Questo account è stato sospeso.',
|
||||||
|
user_not_exist: "L'utente con {{ identifier }} non esiste.",
|
||||||
|
missing_profile: 'È necessario fornire informazioni aggiuntive prima di accedere.',
|
||||||
|
role_exists: "L'ID ruolo {{roleId}} è già stato aggiunto a questo utente",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default user;
|
13
packages/phrases/src/locales/it/errors/verification-code.ts
Normal file
13
packages/phrases/src/locales/it/errors/verification-code.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
const verification_code = {
|
||||||
|
phone_email_empty: "Entrambi il telefono e l'e-mail sono vuoti.",
|
||||||
|
not_found: 'Codice di verifica non trovato. Si prega di inviare il codice di verifica per primo.',
|
||||||
|
phone_mismatch:
|
||||||
|
'Telefono non corrispondente. Si prega di richiedere un nuovo codice di verifica.',
|
||||||
|
email_mismatch: 'Email non corrispondente. Si prega di richiedere un nuovo codice di verifica.',
|
||||||
|
code_mismatch: 'Codice di verifica non valido.',
|
||||||
|
expired: 'Il codice di verifica è scaduto. Si prega di richiedere un nuovo codice di verifica.',
|
||||||
|
exceed_max_try:
|
||||||
|
'Superata la limitazione dei tentativi di codice di verifica. Si prega di richiedere un nuovo codice di verifica.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default verification_code;
|
11
packages/phrases/src/locales/it/index.ts
Normal file
11
packages/phrases/src/locales/it/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import type { LocalePhrase } from '../../types.js';
|
||||||
|
|
||||||
|
import errors from './errors/index.js';
|
||||||
|
import translation from './translation/index.js';
|
||||||
|
|
||||||
|
const it: LocalePhrase = Object.freeze({
|
||||||
|
translation,
|
||||||
|
errors,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default it;
|
|
@ -0,0 +1,32 @@
|
||||||
|
const api_resource_details = {
|
||||||
|
page_title: 'Dettagli delle risorse API',
|
||||||
|
back_to_api_resources: 'Torna alle risorse API',
|
||||||
|
settings_tab: 'Impostazioni',
|
||||||
|
permissions_tab: 'Autorizzazioni',
|
||||||
|
settings: 'Impostazioni',
|
||||||
|
settings_description:
|
||||||
|
"Le risorse API, anche note come Indicatori di Risorse, indicano i servizi o le risorse di destinazione da richiedere, di solito una variabile di formato URI che rappresenta l'identità della risorsa.",
|
||||||
|
token_expiration_time_in_seconds: 'Tempo di scadenza del token (in secondi)',
|
||||||
|
token_expiration_time_in_seconds_placeholder: 'Inserisci il tempo di scadenza del tuo token',
|
||||||
|
delete_description:
|
||||||
|
'Questa azione non può essere annullata. Eliminerà definitivamente la risorsa API. Per favore, inserisci il nome della risorsa api <span>{{name}}</span> per confermare.',
|
||||||
|
enter_your_api_resource_name: 'Inserisci il nome della tua risorsa API',
|
||||||
|
api_resource_deleted: 'La risorsa API {{name}} è stata eliminata con successo',
|
||||||
|
permission: {
|
||||||
|
create_button: 'Crea autorizzazione',
|
||||||
|
create_title: 'crea autorizzazione',
|
||||||
|
create_subtitle: 'Definire le autorizzazioni (ambiti) necessarie per questa API.',
|
||||||
|
confirm_create: 'Crea autorizzazione',
|
||||||
|
name: 'Nome autorizzazione',
|
||||||
|
name_placeholder: 'lettura:risorsa',
|
||||||
|
forbidden_space_in_name: "Il nome dell'autorizzazione non deve contenere spazi.",
|
||||||
|
description: 'Descrizione',
|
||||||
|
description_placeholder: 'In grado di leggere le risorse',
|
||||||
|
permission_created: "L'autorizzazione {{name}} è stata creata con successo",
|
||||||
|
delete_description:
|
||||||
|
"Se questa autorizzazione viene eliminata, l'utente che aveva questa autorizzazione perderà l'accesso concessogli tramite di essa.",
|
||||||
|
deleted: 'L\'autorizzazione "{{name}}" è stata eliminata con successo!',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default api_resource_details;
|
|
@ -0,0 +1,15 @@
|
||||||
|
const api_resources = {
|
||||||
|
page_title: 'Risorse API',
|
||||||
|
title: 'Risorse API',
|
||||||
|
subtitle: 'Definisci API che le tue applicazioni autorizzate possono utilizzare',
|
||||||
|
create: 'Crea risorsa API',
|
||||||
|
api_name: 'Nome API',
|
||||||
|
api_name_placeholder: "Inserisci il nome dell'API",
|
||||||
|
api_identifier: 'Identificatore API',
|
||||||
|
api_identifier_tip:
|
||||||
|
"L'identificatore univoco della risorsa API. Deve essere un URI assoluto e non ha componenti di frammento (#). Corrisponde al parametro <a>risorsa</a> in OAuth 2.0.",
|
||||||
|
api_resource_created: 'La risorsa API {{name}} è stata creata con successo',
|
||||||
|
api_identifier_placeholder: 'https://tuo-identificatore-api/',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default api_resources;
|
|
@ -0,0 +1,51 @@
|
||||||
|
const application_details = {
|
||||||
|
page_title: "Dettagli dell'applicazione",
|
||||||
|
back_to_applications: 'Torna alle applicazioni',
|
||||||
|
check_guide: 'Verifica guida',
|
||||||
|
settings: 'Impostazioni',
|
||||||
|
settings_description:
|
||||||
|
'Le applicazioni vengono utilizzate per identificare le tue applicazioni in Logto per OIDC, esperienza di accesso, registri di controllo, ecc.',
|
||||||
|
advanced_settings: 'Impostazioni avanzate',
|
||||||
|
advanced_settings_description:
|
||||||
|
"Le impostazioni avanzate includono termini correlati all'OIDC. Puoi consultare il Endpoint Token per ulteriori informazioni.",
|
||||||
|
application_name: "Nome dell'applicazione",
|
||||||
|
application_name_placeholder: 'La mia app',
|
||||||
|
description: 'Descrizione',
|
||||||
|
description_placeholder: 'Inserisci la descrizione della tua applicazione',
|
||||||
|
authorization_endpoint: 'Authorization Endpoint',
|
||||||
|
authorization_endpoint_tip:
|
||||||
|
"L'endpoint per effettuare l'autenticazione e l'autorizzazione. Viene utilizzato per la connessione OpenID <a>autenticazione</a>.",
|
||||||
|
application_id: 'ID Applicazione',
|
||||||
|
application_id_tip:
|
||||||
|
'L\'identificatore univoco dell\'applicazione generato normalmente da Logto. Sta anche per "<a>client_id</a>" in OpenID Connect.',
|
||||||
|
application_secret: 'App Segreta',
|
||||||
|
redirect_uri: 'URI di reindirizzamento',
|
||||||
|
redirect_uris: 'URI di reindirizzamento',
|
||||||
|
redirect_uri_placeholder: 'https://il-tuo-sito-web.com/la-tua-app',
|
||||||
|
redirect_uri_placeholder_native: 'io.logto://callback',
|
||||||
|
redirect_uri_tip:
|
||||||
|
'Il URI di reindirizzamento dopo il login di un utente (sia riuscito che non). Vedi OpenID Connect autenticazione richiesta per ulteriori informazioni.',
|
||||||
|
post_sign_out_redirect_uri: 'URI di reindirizzamento post disconnessione',
|
||||||
|
post_sign_out_redirect_uris: 'URI di reindirizzamento post disconnessione',
|
||||||
|
post_sign_out_redirect_uri_placeholder: 'https://your.website.com/home',
|
||||||
|
post_sign_out_redirect_uri_tip:
|
||||||
|
"Il URI di reindirizzamento dopo la disconnessione dell'utente (facoltativo). Potrebbe non avere alcun effetto pratico in alcuni tipi di app.",
|
||||||
|
cors_allowed_origins: 'Origini consentite CORS',
|
||||||
|
cors_allowed_origins_placeholder: 'https://il-tuo-sito-web.com',
|
||||||
|
cors_allowed_origins_tip:
|
||||||
|
'Per impostazione predefinita, saranno consentite tutte le origini degli URI di reindirizzamento. Di solito non è richiesta alcuna azione per questo campo. Vedi la documentazione MDN per informazioni dettagliate.',
|
||||||
|
id_token_expiration: 'Scadenza token ID',
|
||||||
|
refresh_token_expiration: 'Scadenza token di aggiornamento',
|
||||||
|
token_endpoint: 'Endpoint del token',
|
||||||
|
user_info_endpoint: 'Endpoint delle informazioni utente',
|
||||||
|
enable_admin_access: "Abilita l'accesso amministratore",
|
||||||
|
enable_admin_access_label:
|
||||||
|
"Abilita o disabilita l'accesso all'API di gestione. Una volta abilitato, puoi utilizzare i token di accesso per chiamare l'API di gestione a nome di questa applicazione.",
|
||||||
|
delete_description:
|
||||||
|
"Questa azione non può essere annullata. Eliminerà definitivamente l'applicazione. Inserisci il nome dell'applicazione <span>{{name}}</span> per confermare.",
|
||||||
|
enter_your_application_name: 'Inserisci il nome della tua applicazione',
|
||||||
|
application_deleted: "L'applicazione {{name}} è stata eliminata con successo",
|
||||||
|
redirect_uri_required: 'Devi inserire almeno un URI di reindirizzamento',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default application_details;
|
|
@ -0,0 +1,53 @@
|
||||||
|
const applications = {
|
||||||
|
page_title: 'Applicazioni',
|
||||||
|
title: 'Applicazioni',
|
||||||
|
subtitle:
|
||||||
|
"Configura l'autenticazione Logto per la tua applicazione nativa, a singola pagina, macchina-to-macchina o tradizionale",
|
||||||
|
create: 'Crea Applicazione',
|
||||||
|
application_name: 'Nome applicazione',
|
||||||
|
application_name_placeholder: 'La mia App',
|
||||||
|
application_description: 'Descrizione applicazione',
|
||||||
|
application_description_placeholder: 'Inserisci la descrizione della tua applicazione',
|
||||||
|
select_application_type: 'Seleziona un tipo di applicazione',
|
||||||
|
no_application_type_selected: 'Non hai ancora selezionato alcun tipo di applicazione',
|
||||||
|
application_created:
|
||||||
|
"L'applicazione {{name}} è stata creata con successo! \nOra completa le impostazioni della tua applicazione.",
|
||||||
|
app_id: 'ID App',
|
||||||
|
type: {
|
||||||
|
native: {
|
||||||
|
title: 'App Nativa',
|
||||||
|
subtitle: "Un'applicazione che viene eseguita in un ambiente nativo",
|
||||||
|
description: 'E.g., applicazione iOS, applicazione Android',
|
||||||
|
},
|
||||||
|
spa: {
|
||||||
|
title: 'Applicazione a Singola Pagina',
|
||||||
|
subtitle:
|
||||||
|
"Un'applicazione che viene eseguita in un browser web e aggiorna dinamicamente i dati in maniera localizzata",
|
||||||
|
description: 'E.g., applicazione React DOM, applicazione Vue',
|
||||||
|
},
|
||||||
|
traditional: {
|
||||||
|
title: 'Web Tradizionale',
|
||||||
|
subtitle: "Un'applicazione che renderizza e aggiorna le pagine solo attraverso il server web",
|
||||||
|
description: 'E.g., Next.js, PHP',
|
||||||
|
},
|
||||||
|
machine_to_machine: {
|
||||||
|
title: 'Macchina-to-Macchina',
|
||||||
|
subtitle: "Un'app (solitamente un servizio) che comunica direttamente con le risorse",
|
||||||
|
description: 'E.g., servizio backend',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
guide: {
|
||||||
|
get_sample_file: 'Scarica Esempio',
|
||||||
|
header_description:
|
||||||
|
'Segui la guida passo passo per integrare la tua applicazione o clicca il pulsante corretto per scaricare il nostro progetto di esempio',
|
||||||
|
title: "L'applicazione è stata creata con successo",
|
||||||
|
subtitle:
|
||||||
|
'Ora segui i passi di seguito per completare le impostazioni della tua app. Seleziona il tipo di SDK per continuare.',
|
||||||
|
description_by_sdk: "Questa guida rapida illustra come integrare Logto in un'app {{sdk}}",
|
||||||
|
},
|
||||||
|
placeholder_title: 'Seleziona un tipo di applicazione per continuare',
|
||||||
|
placeholder_description:
|
||||||
|
"Logto utilizza un'entità applicazione per OIDC per aiutarti in compiti come l'identificazione delle tue app, la gestione dell'accesso e la creazione di registri di audit.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default applications;
|
|
@ -0,0 +1,121 @@
|
||||||
|
const cloud = {
|
||||||
|
welcome: {
|
||||||
|
page_title: 'Benvenuto',
|
||||||
|
title: 'Benvenuto e creiamo insieme la tua anteprima di Logto Cloud',
|
||||||
|
description:
|
||||||
|
'Che tu sia un utente open-source o cloud, fai un tour della vetrina e scopri il valore completo di Logto. Cloud preview serve anche come versione preliminare di Logto Cloud.',
|
||||||
|
project_field: 'Sto usando Logto per',
|
||||||
|
project_options: {
|
||||||
|
personal: 'Progetto personale',
|
||||||
|
company: 'Progetto aziendale',
|
||||||
|
},
|
||||||
|
deployment_type_field: 'Preferisci open-source o cloud?',
|
||||||
|
deployment_type_options: {
|
||||||
|
open_source: 'Open-Source',
|
||||||
|
cloud: 'Cloud',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
about: {
|
||||||
|
page_title: "Un po' di te",
|
||||||
|
title: "Un po' di te per rendere unica la tua esperienza Logto",
|
||||||
|
description:
|
||||||
|
'Facciamo diventare la tua esperienza Logto unica conoscendoti meglio. Le tue informazioni sono al sicuro con noi.',
|
||||||
|
title_field: 'La tua posizione',
|
||||||
|
title_options: {
|
||||||
|
developer: 'Sviluppatore',
|
||||||
|
team_lead: 'Team Lead',
|
||||||
|
ceo: 'CEO',
|
||||||
|
cto: 'CTO',
|
||||||
|
product: 'Prodotto',
|
||||||
|
others: 'Altro',
|
||||||
|
},
|
||||||
|
company_name_field: "Nome dell'azienda",
|
||||||
|
company_name_placeholder: 'Acme.co',
|
||||||
|
company_size_field: "Dimensione dell'azienda",
|
||||||
|
company_options: {
|
||||||
|
size_1: '1',
|
||||||
|
size_2_49: '2-49',
|
||||||
|
size_50_199: '50-199',
|
||||||
|
size_200_999: '200-999',
|
||||||
|
size_1000_plus: '1000+',
|
||||||
|
},
|
||||||
|
reason_field: 'Mi sto iscrivendo perché',
|
||||||
|
reason_options: {
|
||||||
|
passwordless: 'Ricerca di autenticazione senza password e UI kit',
|
||||||
|
efficiency: 'Scoperta di infrastrutture di identità preconfezionate',
|
||||||
|
access_control: "Controllo dell'accesso degli utenti in base ai ruoli e alle responsabilità",
|
||||||
|
multi_tenancy: 'Ricerca di strategie per un prodotto multi-tenancy',
|
||||||
|
enterprise: 'Ricerca di soluzioni SSO per la preparazione aziendale',
|
||||||
|
others: 'Altro',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
congrats: {
|
||||||
|
page_title: 'Guadagna crediti in anticipo',
|
||||||
|
title: 'Fantastiche notizie! Sei qualificato per guadagnare i primi crediti di Logto Cloud!',
|
||||||
|
description:
|
||||||
|
"Non perdere l'occasione di usufruire di una sottoscrizione gratuita di Logto Cloud per <strong>60 giorni</strong> dopo il lancio ufficiale! Contatta subito il team di Logto per saperne di più.",
|
||||||
|
check_out_button: 'Guarda la anteprima dal vivo',
|
||||||
|
email_us_title: 'Scrivici una email per offerta speciale e dettagli di prezzo',
|
||||||
|
email_us_description: 'Ottieni prezzi esclusivi per risparmiare denaro',
|
||||||
|
email_us_button: 'Invia email',
|
||||||
|
join_description:
|
||||||
|
'Entra nel nostro <a>{{link}}</a> pubblico per connetterti e chattare con altri sviluppatori.',
|
||||||
|
discord_link: 'canale discord',
|
||||||
|
enter_admin_console: 'Entra in Logto Cloud Preview',
|
||||||
|
},
|
||||||
|
gift: {
|
||||||
|
title: 'Utilizza Logto Cloud gratuitamente per 60 giorni. Unisciti ai primi.',
|
||||||
|
description:
|
||||||
|
'Prenota una sessione individuale con il nostro team per ottenere crediti anticipati.',
|
||||||
|
reserve_title: 'Prenota il tuo tempo con il team Logto',
|
||||||
|
reserve_description: 'Il credito è disponibile solo previa valutazione.',
|
||||||
|
book_button: 'Prenota',
|
||||||
|
email_us_title: 'Scrivici una email',
|
||||||
|
email_us_description: 'Contattaci per una offerta speciale e dettagli di prezzo.',
|
||||||
|
email_us_button: 'Invia',
|
||||||
|
},
|
||||||
|
sie: {
|
||||||
|
page_title: "Personalizza l'esperienza di accesso",
|
||||||
|
title: 'Personalizziamo insieme la tua esperienza di accesso',
|
||||||
|
inspire: {
|
||||||
|
title: 'Crea esempi coinvolgenti',
|
||||||
|
description:
|
||||||
|
'Ti senti incerto riguardo l\'esperienza di accesso? Fai clic su "Ispirami" e lascia che la magia accada!',
|
||||||
|
inspire_me: 'Ispirami',
|
||||||
|
},
|
||||||
|
logo_field: "Logo dell'app",
|
||||||
|
color_field: 'Colore del brand',
|
||||||
|
identifier_field: 'Identificativo',
|
||||||
|
identifier_options: {
|
||||||
|
email: 'Email',
|
||||||
|
phone: 'Telefono',
|
||||||
|
user_name: 'Nome utente',
|
||||||
|
},
|
||||||
|
authn_field: 'Autenticazione',
|
||||||
|
authn_options: {
|
||||||
|
password: 'Password',
|
||||||
|
verification_code: 'Codice di verifica',
|
||||||
|
},
|
||||||
|
social_field: 'Accesso tramite social',
|
||||||
|
finish_and_done: 'Termina e completato',
|
||||||
|
preview: {
|
||||||
|
mobile_tab: 'Mobile',
|
||||||
|
web_tab: 'Web',
|
||||||
|
},
|
||||||
|
connectors: {
|
||||||
|
unlocked_later: 'Sbloccato in seguito',
|
||||||
|
unlocked_later_tip:
|
||||||
|
'Una volta completato il processo di onboarding e inserito il prodotto, avrai accesso a ancora più metodi di accesso tramite social.',
|
||||||
|
notice:
|
||||||
|
'Si prega di evitare di utilizzare il connettore demo per scopi di produzione. Una volta completati i test, cancellare gentilmente il connettore demo e configurare il proprio connettore con le proprie credenziali.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
broadcast: '📣 Sei in Logto Cloud (Preview)',
|
||||||
|
socialCallback: {
|
||||||
|
title: 'Accesso effettuato con successo',
|
||||||
|
description:
|
||||||
|
"Hai effettuato l'accesso con successo utilizzando il tuo account social. Per garantire integrazione senza problemi e accesso a tutte le funzionalità di Logto, ti consigliamo di procedere alla configurazione del tuo connettore social.",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default cloud;
|
|
@ -0,0 +1,14 @@
|
||||||
|
const components = {
|
||||||
|
uploader: {
|
||||||
|
action_description: 'Trascina e rilascia o cerca',
|
||||||
|
uploading: 'Caricamento in corso...',
|
||||||
|
image_limit:
|
||||||
|
'Carica immagini sotto i {{size, number}}KB, solo {{extensions, list(style: narrow; type: conjunction;)}}.',
|
||||||
|
error_upload: 'Qualcosa è andato storto. Caricamento fallito.',
|
||||||
|
error_file_size: 'Il file è troppo grande. Carica un file sotto i {{size, number}}KB.',
|
||||||
|
error_file_type:
|
||||||
|
'Formato file non supportato. Sono accettati solo formati {{extensions, list(style: narrow; type: conjunction;)}}.',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default components;
|
|
@ -0,0 +1,35 @@
|
||||||
|
const connector_details = {
|
||||||
|
page_title: 'Dettagli del connettore',
|
||||||
|
back_to_connectors: 'Torna ai connettori',
|
||||||
|
check_readme: 'Verifica README',
|
||||||
|
settings: 'Impostazioni generali',
|
||||||
|
settings_description:
|
||||||
|
"I connettori svolgono un ruolo critico in Logto. Grazie al loro aiuto, Logto consente agli utenti finali di utilizzare la registrazione o l'accesso senza password e le capacità di accedere tramite account social.",
|
||||||
|
parameter_configuration: 'Configurazione dei parametri',
|
||||||
|
test_connection: 'Test di connessione',
|
||||||
|
save_error_empty_config: 'Inserisci la configurazione',
|
||||||
|
send: 'Invia',
|
||||||
|
send_error_invalid_format: 'Input non valido',
|
||||||
|
edit_config_label: 'Inserisci il tuo JSON qui',
|
||||||
|
test_email_sender: 'Prova il tuo connettore per email',
|
||||||
|
test_sms_sender: 'Prova il tuo connettore per SMS',
|
||||||
|
test_email_placeholder: 'john.doe@example.com',
|
||||||
|
test_sms_placeholder: '+1 555-123-4567',
|
||||||
|
test_message_sent: 'Messaggio di prova inviato',
|
||||||
|
test_sender_description:
|
||||||
|
'Logto utilizza il modello "Generico" per i test. Riceverai un messaggio se il tuo connettore è correttamente configurato.',
|
||||||
|
options_change_email: 'Cambia connettore per email',
|
||||||
|
options_change_sms: 'Cambia connettore per SMS',
|
||||||
|
connector_deleted: 'Il connettore è stato eliminato con successo',
|
||||||
|
type_email: 'Connettore per email',
|
||||||
|
type_sms: 'Connettore per SMS',
|
||||||
|
type_social: 'Connettore social',
|
||||||
|
in_used_social_deletion_description:
|
||||||
|
"Questo connettore è in uso nella tua esperienza di accesso. Eliminandolo, l'esperienza di accesso di <name/> verrà eliminata nelle impostazioni dell'esperienza di accesso. Dovrai riconfigurarlo se decidi di aggiungerlo di nuovo.",
|
||||||
|
in_used_passwordless_deletion_description:
|
||||||
|
'Questo {{name}} è in uso nella tua esperienza di accesso. Eliminandolo, la tua esperienza di accesso non funzionerà correttamente fino a quando non risolverai il conflitto. Dovrai riconfigurarlo se decidi di aggiungerlo di nuovo.',
|
||||||
|
deletion_description:
|
||||||
|
'Stai rimuovendo questo connettore. Non può essere annullato e dovrai riconfigurarlo se decidi di aggiungerlo di nuovo.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connector_details;
|
|
@ -0,0 +1,91 @@
|
||||||
|
const connectors = {
|
||||||
|
page_title: 'Connettori',
|
||||||
|
title: 'Connettori',
|
||||||
|
subtitle:
|
||||||
|
'Imposta i connettori per abilitare una esperienza di accesso senza password e tramite social media',
|
||||||
|
create: 'Aggiungi connettore sociale',
|
||||||
|
config_sie_notice: 'Hai impostato i connettori. Assicurati di configurarli in <a>{{link}}</a>.',
|
||||||
|
config_sie_link_text: 'esperienza di accesso',
|
||||||
|
tab_email_sms: 'Connettori email e SMS',
|
||||||
|
tab_social: 'Connettori social',
|
||||||
|
connector_name: 'Nome del connettore',
|
||||||
|
demo_tip:
|
||||||
|
'Il numero massimo di messaggi consentiti per questo connettore demo è limitato a 100 e non è consigliato per il deployment in un ambiente di produzione.',
|
||||||
|
social_demo_tip:
|
||||||
|
'Il connettore demo è progettato esclusivamente per scopi di dimostrazione e non è consigliato per il deployment in un ambiente di produzione.',
|
||||||
|
connector_type: 'Tipo',
|
||||||
|
connector_status: 'Esperienza di accesso',
|
||||||
|
connector_status_in_use: 'In uso',
|
||||||
|
connector_status_not_in_use: 'Non in uso',
|
||||||
|
not_in_use_tip: {
|
||||||
|
content:
|
||||||
|
'Non in uso significa che la tua esperienza di accesso non ha utilizzato questo metodo di accesso. <a>{{link}}</a> per aggiungere questo metodo di accesso. ',
|
||||||
|
go_to_sie: 'Vai all’esperienza di accesso',
|
||||||
|
},
|
||||||
|
placeholder_title: 'Connettore sociale',
|
||||||
|
placeholder_description:
|
||||||
|
'Logto ha fornito molti connettori di accesso condivisi tramite i social media, frattanto puoi creare il tuo usando i protocolli standard.',
|
||||||
|
save_and_done: 'Salva e Completa',
|
||||||
|
type: {
|
||||||
|
email: 'Connettore email',
|
||||||
|
sms: 'Connettore SMS',
|
||||||
|
social: 'Connettore sociale',
|
||||||
|
},
|
||||||
|
setup_title: {
|
||||||
|
email: 'Imposta connettore email',
|
||||||
|
sms: 'Imposta connettore SMS',
|
||||||
|
social: 'Aggiungi connettore sociale',
|
||||||
|
},
|
||||||
|
guide: {
|
||||||
|
subtitle: 'Una guida passo passo per configurare il tuo connettore',
|
||||||
|
general_setting: 'Impostazioni generali',
|
||||||
|
parameter_configuration: 'Configurazione dei parametri',
|
||||||
|
test_connection: 'Prova la connessione',
|
||||||
|
name: 'Nome per il pulsante di accesso tramite social media',
|
||||||
|
name_placeholder: 'Inserisci il nome per il pulsante di accesso tramite social media',
|
||||||
|
name_tip:
|
||||||
|
'Il nome del pulsante del connettore verrà visualizzato come "Continua con {{name}}." Presta attenzione alla lunghezza del nome in caso risulti troppo lungo.',
|
||||||
|
logo: 'URL del logo per il pulsante di accesso tramite social media',
|
||||||
|
logo_placeholder: 'https://your.cdn.domain/logo.png',
|
||||||
|
logo_tip:
|
||||||
|
"L'immagine del logo verrà mostrata sul connettore. Otteni un link di immagine pubblicamente accessibile e inserisci qui il link.",
|
||||||
|
logo_dark: 'URL del logo per il pulsante di accesso tramite social media (modalità scura)',
|
||||||
|
logo_dark_placeholder: 'https://your.cdn.domain/logo.png',
|
||||||
|
logo_dark_tip:
|
||||||
|
'Imposta il logo del tuo connettore per la modalità scura dopo averla abilitata nell’esperienza di accesso nel Console dell’Amministratore.',
|
||||||
|
logo_dark_collapse: 'Comprimi',
|
||||||
|
logo_dark_show: 'Mostra le impostazioni del logo per la modalità scura',
|
||||||
|
target: 'Nome del provider di identità',
|
||||||
|
target_placeholder: 'Inserisci il nome del provider di identità del connettore',
|
||||||
|
target_tip:
|
||||||
|
'Il valore del "Nome IdP" può essere una stringa di identificatore univoco per distinguere le identità social. Questa impostazione non può essere cambiata dopo la costruzione del connettore.',
|
||||||
|
target_tooltip:
|
||||||
|
"'Nome IdP' nei connettori social di Logto si riferisce alla 'fonte' delle tue identità social media. Nel design di Logto, non accettiamo lo stesso 'Nome IdP' di una piattaforma specifica per evitare conflitti. Devi fare molta attenzione prima di aggiungere un connettore, poiché NON PUOI cambiarne il valore una volta creato. <a>Scopri di più.</a>",
|
||||||
|
target_conflict:
|
||||||
|
'Il nome IdP inserito corrisponde al connettore <span>nome</span> esistente. L’utilizzo dello stesso nome IdP potrebbe causare un comportamento di accesso imprevisto in cui gli utenti possono accedere allo stesso account tramite due connettori diversi.',
|
||||||
|
target_conflict_line2:
|
||||||
|
'Se desideri sostituire il connettore corrente con lo stesso provider di identità e consentire agli utenti precedenti di accedere senza registrarsi nuovamente, elimina il connettore <span>nome</span> e crea un nuovo connettore con lo stesso "Nome IdP".',
|
||||||
|
target_conflict_line3:
|
||||||
|
'Se desideri connetterti a un provider di identità diverso, modifica il "Nome IdP" e procedi.',
|
||||||
|
config: 'Inserisci il tuo JSON di configurazione',
|
||||||
|
sync_profile: 'Sincronizza le informazioni del profilo',
|
||||||
|
sync_profile_only_at_sign_up: 'Sincronizza solo al momento della registrazione',
|
||||||
|
sync_profile_each_sign_in: 'Effettua sempre una sincronizzazione ad ogni accesso',
|
||||||
|
sync_profile_tip:
|
||||||
|
'Sincronizza il profilo di base dal provider social, ad esempio i nomi degli utenti e le loro immagini del profilo.',
|
||||||
|
callback_uri: 'URI di callback',
|
||||||
|
callback_uri_description:
|
||||||
|
"Anche chiamato URI di reindirizzamento, è l'URI in Logto dove gli utenti verranno rimandati dopo l'autorizzazione tramite social media, copia e incollalo nella pagina di configurazione del provider social media.",
|
||||||
|
},
|
||||||
|
platform: {
|
||||||
|
universal: 'Universale',
|
||||||
|
web: 'Web',
|
||||||
|
native: 'Native',
|
||||||
|
},
|
||||||
|
add_multi_platform: 'supporta più piattaforme, seleziona una piattaforma per continuare',
|
||||||
|
drawer_title: 'Guida per il connettore',
|
||||||
|
drawer_subtitle: 'Segui le istruzioni per integrare il tuo connettore',
|
||||||
|
unknown: 'Connettore sconosciuto',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connectors;
|
|
@ -0,0 +1,22 @@
|
||||||
|
const contatto = {
|
||||||
|
title: 'Contattaci',
|
||||||
|
description:
|
||||||
|
'Unisciti alla nostra comunità per fornire feedback, chiedere aiuto e condividere le tue idee con altri sviluppatori',
|
||||||
|
discord: {
|
||||||
|
title: 'Canale Discord',
|
||||||
|
description: 'Unisciti al nostro canale pubblico per chattare con altri sviluppatori',
|
||||||
|
button: 'Unisciti',
|
||||||
|
},
|
||||||
|
github: {
|
||||||
|
title: 'GitHub',
|
||||||
|
description: 'Crea un problema e invialo su GitHub',
|
||||||
|
button: 'Apri',
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
title: 'Invia email',
|
||||||
|
description: 'Invia una email per ulteriori informazioni e supporto',
|
||||||
|
button: 'Invia',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default contatto;
|
|
@ -0,0 +1,21 @@
|
||||||
|
const dashboard = {
|
||||||
|
page_title: 'Dashboard',
|
||||||
|
title: 'Dashboard',
|
||||||
|
description: 'Ottieni una panoramica sulle prestazioni della tua app',
|
||||||
|
total_users: 'Totale utenti',
|
||||||
|
total_users_tip: 'Totale utenti',
|
||||||
|
new_users_today: 'Nuovi utenti oggi',
|
||||||
|
new_users_today_tip: 'Il numero di nuovi utenti registrati sulle tue app oggi',
|
||||||
|
new_users_7_days: 'Nuovi utenti negli ultimi 7 giorni',
|
||||||
|
new_users_7_days_tip: 'Il numero di nuovi utenti registrati sulle tue app negli ultimi 7 giorni',
|
||||||
|
daily_active_users: 'Utenti attivi giornalieri',
|
||||||
|
daily_active_users_tip: 'Il numero di utenti unici che hanno scambiato token sulle tue app oggi',
|
||||||
|
weekly_active_users: 'Utenti attivi settimanali',
|
||||||
|
weekly_active_users_tip:
|
||||||
|
'Il numero di utenti unici che hanno scambiato token sulle tue app negli ultimi 7 giorni',
|
||||||
|
monthly_active_users: 'Utenti attivi mensili',
|
||||||
|
monthly_active_users_tip:
|
||||||
|
'Il numero di utenti unici che hanno scambiato token sulle tue app negli ultimi 30 giorni',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default dashboard;
|
|
@ -0,0 +1,25 @@
|
||||||
|
const errors = {
|
||||||
|
something_went_wrong: 'Oops! Si è verificato un errore.',
|
||||||
|
page_not_found: 'Pagina non trovata',
|
||||||
|
unknown_server_error: 'Si è verificato un errore del server sconosciuto',
|
||||||
|
empty: 'Nessun dato',
|
||||||
|
missing_total_number: 'Impossibile trovare Total-Number negli header di risposta',
|
||||||
|
invalid_uri_format: 'Formato URI non valido',
|
||||||
|
invalid_origin_format: 'Formato origine URI non valido',
|
||||||
|
invalid_json_format: 'Formato JSON non valido',
|
||||||
|
invalid_error_message_format: 'Il formato del messaggio di errore non è valido.',
|
||||||
|
required_field_missing: 'Inserisci {{field}}',
|
||||||
|
required_field_missing_plural: 'Devi inserire almeno un {{field}}',
|
||||||
|
more_details: 'Ulteriori dettagli',
|
||||||
|
username_pattern_error:
|
||||||
|
'Il nome utente dovrebbe contenere solo lettere, numeri, o trattini bassi e non dovrebbe iniziare con un numero.',
|
||||||
|
password_pattern_error:
|
||||||
|
'La password richiede un minimo di {{min}} caratteri e contiene una combinazione di lettere, numeri e simboli.',
|
||||||
|
insecure_contexts: 'I contesti non sicuri (non HTTPS) non sono supportati.',
|
||||||
|
unexpected_error: 'Si è verificato un errore inaspettato.',
|
||||||
|
not_found: '404 non trovato',
|
||||||
|
create_internal_role_violation:
|
||||||
|
"Stai creando un nuovo ruolo interno che è proibito da Logto. Prova un altro nome che non inizi con '#internal:'.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default errors;
|
|
@ -0,0 +1,56 @@
|
||||||
|
const general = {
|
||||||
|
placeholder: 'Segnaposto',
|
||||||
|
skip: 'Salta',
|
||||||
|
next: 'Avanti',
|
||||||
|
back: 'Indietro',
|
||||||
|
retry: 'Riprova',
|
||||||
|
done: 'Fatto',
|
||||||
|
search: 'Cerca',
|
||||||
|
search_placeholder: 'Cerca',
|
||||||
|
clear_result: 'Cancella risultati',
|
||||||
|
save: 'Salva',
|
||||||
|
save_changes: 'Salva modifiche',
|
||||||
|
saved: 'Salvato!',
|
||||||
|
discard: 'Elimina',
|
||||||
|
loading: 'Caricamento...',
|
||||||
|
redirecting: 'Redirezione...',
|
||||||
|
add: 'Aggiungi',
|
||||||
|
added: 'Aggiunto',
|
||||||
|
cancel: 'Annulla',
|
||||||
|
confirm: 'Conferma',
|
||||||
|
check_out: 'Checkout',
|
||||||
|
create: 'Crea',
|
||||||
|
set_up: 'Imposta',
|
||||||
|
customize: 'Personalizza',
|
||||||
|
enable: 'Abilita',
|
||||||
|
reminder: 'Promemoria',
|
||||||
|
delete: 'Elimina',
|
||||||
|
more_options: 'PIÙ OPZIONI',
|
||||||
|
close: 'Chiudi',
|
||||||
|
copy: 'Copia',
|
||||||
|
copying: 'Copiare',
|
||||||
|
copied: 'Copiato',
|
||||||
|
required: 'Obbligatorio',
|
||||||
|
add_another: 'Aggiungi un altro',
|
||||||
|
deletion_confirmation: 'Sei sicuro di voler eliminare questo {{title}}?',
|
||||||
|
settings_nav: 'Impostazioni',
|
||||||
|
unsaved_changes_warning:
|
||||||
|
'Hai apportato alcune modifiche. Sei sicuro di voler lasciare questa pagina?',
|
||||||
|
leave_page: 'Lascia pagina',
|
||||||
|
stay_on_page: 'Rimani sulla pagina',
|
||||||
|
type_to_search: 'Digita per cercare',
|
||||||
|
got_it: 'Capito',
|
||||||
|
continue: 'Continua',
|
||||||
|
page_info: '{{min, number}}-{{max, number}} di {{total, number}}',
|
||||||
|
learn_more: 'Scopri di più',
|
||||||
|
tab_errors: '{{count, number}} errori',
|
||||||
|
skip_for_now: 'Salta per ora',
|
||||||
|
remove: 'Rimuovi',
|
||||||
|
visit: 'Visita',
|
||||||
|
join: 'Unisciti',
|
||||||
|
try_now: 'Prova ora',
|
||||||
|
multiple_form_field: '(Multiplo)',
|
||||||
|
demo: 'Demo',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default general;
|
|
@ -0,0 +1,35 @@
|
||||||
|
const get_started = {
|
||||||
|
page_title: 'Inizia',
|
||||||
|
progress: 'Guida per iniziare: {{completed}}/{{total}}',
|
||||||
|
progress_dropdown_title: 'Alcune cose che puoi fare...',
|
||||||
|
title: 'Esplora per avere successo',
|
||||||
|
subtitle_part1: 'Alcune cose che puoi fare per ottenere rapidamente il valore di Logto',
|
||||||
|
subtitle_part2: 'Sono un professionista e ho completato tutti i passaggi.',
|
||||||
|
hide_this: 'Nascondi questo',
|
||||||
|
confirm_message:
|
||||||
|
'Sei sicuro di voler nascondere questa pagina? Questa azione non può essere annullata.',
|
||||||
|
check_preview_title: "Controlla l'anteprima dal vivo",
|
||||||
|
check_preview_subtitle: 'Prova subito Logto sign-in per vedere come funziona',
|
||||||
|
integration_title: 'Crea ed integra la tua applicazione',
|
||||||
|
integration_subtitle:
|
||||||
|
"Configura l'autenticazione di Logto per la tua applicazione nativa, single page, machine to machine o tradizionale",
|
||||||
|
custom_sie_title: "Personalizza l'esperienza di accesso",
|
||||||
|
custom_sie_subtitle: 'Sblocca una vasta gamma di scenari con le impostazioni avanzate',
|
||||||
|
passwordless_title: "Scala l'accesso senza password aggiungendo i tuoi connettori",
|
||||||
|
passwordless_subtitle:
|
||||||
|
"Prova l'accesso senza password e abilita un'esperienza sicura e priva di attriti per i tuoi clienti",
|
||||||
|
community_title: 'Unisciti alla nostra community su Discord',
|
||||||
|
community_subtitle: 'Entra nel nostro canale pubblico per parlare con altri sviluppatori',
|
||||||
|
management_api_title: 'Interagisci con la Management API',
|
||||||
|
management_api_subtitle:
|
||||||
|
'Collega direttamente il tuo sistema di autenticazione alla nostra Management API',
|
||||||
|
further_readings_title: 'Ulteriori letture',
|
||||||
|
further_readings_subtitle:
|
||||||
|
'Consulta la nostra documentazione passo-passo basata su scenari senza concetti noiosi',
|
||||||
|
add_rbac_title:
|
||||||
|
'Aggiungi il controllo degli accessi basati sui ruoli per proteggere le tue risorse',
|
||||||
|
add_rbac_subtitle:
|
||||||
|
"Controlla le tue risorse attraverso l'autorizzazione basata su ruoli scalabile per utilizzi diversi.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default get_started;
|
|
@ -0,0 +1,62 @@
|
||||||
|
import api_resource_details from './api-resource-details.js';
|
||||||
|
import api_resources from './api-resources.js';
|
||||||
|
import application_details from './application-details.js';
|
||||||
|
import applications from './applications.js';
|
||||||
|
import cloud from './cloud.js';
|
||||||
|
import components from './components.js';
|
||||||
|
import connector_details from './connector-details.js';
|
||||||
|
import connectors from './connectors.js';
|
||||||
|
import contact from './contact.js';
|
||||||
|
import dashboard from './dashboard.js';
|
||||||
|
import errors from './errors.js';
|
||||||
|
import general from './general.js';
|
||||||
|
import get_started from './get-started.js';
|
||||||
|
import log_details from './log-details.js';
|
||||||
|
import logs from './logs.js';
|
||||||
|
import menu from './menu.js';
|
||||||
|
import permissions from './permissions.js';
|
||||||
|
import profile from './profile.js';
|
||||||
|
import role_details from './role-details.js';
|
||||||
|
import roles from './roles.js';
|
||||||
|
import session_expired from './session-expired.js';
|
||||||
|
import sign_in_exp from './sign-in-exp/index.js';
|
||||||
|
import tab_sections from './tab-sections.js';
|
||||||
|
import tabs from './tabs.js';
|
||||||
|
import user_details from './user-details.js';
|
||||||
|
import users from './users.js';
|
||||||
|
import welcome from './welcome.js';
|
||||||
|
|
||||||
|
const admin_console = {
|
||||||
|
title: 'Admin Console',
|
||||||
|
admin_user: 'Admin',
|
||||||
|
system_app: 'System',
|
||||||
|
menu,
|
||||||
|
general,
|
||||||
|
errors,
|
||||||
|
tab_sections,
|
||||||
|
tabs,
|
||||||
|
applications,
|
||||||
|
application_details,
|
||||||
|
api_resources,
|
||||||
|
api_resource_details,
|
||||||
|
connectors,
|
||||||
|
connector_details,
|
||||||
|
get_started,
|
||||||
|
users,
|
||||||
|
user_details,
|
||||||
|
contact,
|
||||||
|
sign_in_exp,
|
||||||
|
dashboard,
|
||||||
|
logs,
|
||||||
|
log_details,
|
||||||
|
session_expired,
|
||||||
|
welcome,
|
||||||
|
roles,
|
||||||
|
role_details,
|
||||||
|
permissions,
|
||||||
|
cloud,
|
||||||
|
profile,
|
||||||
|
components,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default admin_console;
|
|
@ -0,0 +1,18 @@
|
||||||
|
const log_details = {
|
||||||
|
page_title: 'Dettagli registro di verifica',
|
||||||
|
back_to_logs: 'Torna ai log di verifica',
|
||||||
|
back_to_user: 'Torna a {{name}}',
|
||||||
|
success: 'Successo',
|
||||||
|
failed: 'Fallito',
|
||||||
|
event_key: 'Chiave evento',
|
||||||
|
application: 'Applicazione',
|
||||||
|
ip_address: 'Indirizzo IP',
|
||||||
|
user: 'Utente',
|
||||||
|
log_id: 'ID registro',
|
||||||
|
time: 'Tempo',
|
||||||
|
user_agent: 'User agent',
|
||||||
|
tab_details: 'Dettagli',
|
||||||
|
raw_data: 'Dati grezzi',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default log_details;
|
|
@ -0,0 +1,13 @@
|
||||||
|
const logs = {
|
||||||
|
page_title: 'Log di Audit',
|
||||||
|
title: 'Log di Audit',
|
||||||
|
subtitle:
|
||||||
|
'Visualizza i dati di registro degli eventi di autenticazione effettuati dai tuoi utenti',
|
||||||
|
event: 'Evento',
|
||||||
|
user: 'Utente',
|
||||||
|
application: 'Applicazione',
|
||||||
|
time: 'Tempo',
|
||||||
|
filter_by: 'Filtra per',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default logs;
|
|
@ -0,0 +1,13 @@
|
||||||
|
const menu = {
|
||||||
|
profile: 'Profilo',
|
||||||
|
language: 'Lingua',
|
||||||
|
appearance: {
|
||||||
|
label: 'Aspetto',
|
||||||
|
light: 'Modalità chiaro',
|
||||||
|
dark: 'Modalità scuro',
|
||||||
|
system: 'Sincronizza con sistema',
|
||||||
|
},
|
||||||
|
sign_out: 'Esci',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default menu;
|
|
@ -0,0 +1,12 @@
|
||||||
|
const permissions = {
|
||||||
|
search_placeholder: 'Cerca per nome API o permesso',
|
||||||
|
search_placeholder_without_api: 'Cerca per nome permesso',
|
||||||
|
name_column: 'Permesso',
|
||||||
|
description_column: 'Descrizione',
|
||||||
|
api_column: 'API',
|
||||||
|
placeholder_title: 'Permesso',
|
||||||
|
placeholder_description:
|
||||||
|
"Il permesso si riferisce all'autorizzazione per accedere ad una risorsa (la chiamiamo risorsa API).",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default permissions;
|
|
@ -0,0 +1,82 @@
|
||||||
|
const profile = {
|
||||||
|
page_title: 'Impostazioni Account',
|
||||||
|
title: 'Impostazioni Account',
|
||||||
|
description:
|
||||||
|
'Cambia le tue impostazioni account e gestisci le tue informazioni personali qui per garantire la sicurezza del tuo account.',
|
||||||
|
settings: {
|
||||||
|
title: 'IMPOSTAZIONI PROFILO',
|
||||||
|
profile_information: 'Informazioni profilo',
|
||||||
|
avatar: 'Avatar',
|
||||||
|
name: 'Nome',
|
||||||
|
username: 'Nome utente',
|
||||||
|
},
|
||||||
|
link_account: {
|
||||||
|
title: 'COLLEGA ACCOUNT',
|
||||||
|
email_sign_in: 'Accesso con email',
|
||||||
|
email: 'Email',
|
||||||
|
social_sign_in: 'Accesso con social media',
|
||||||
|
link_email: 'Collega email',
|
||||||
|
link_email_subtitle: "Collega la tua email per accedere o per recuperare l'account.",
|
||||||
|
email_required: "L'email è obbligatoria",
|
||||||
|
invalid_email: 'Indirizzo email non valido',
|
||||||
|
identical_email_address: "L'indirizzo email inserito è identico a quello corrente",
|
||||||
|
anonymous: 'Anonimo',
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
title: 'PASSWORD E SICUREZZA',
|
||||||
|
password: 'Password',
|
||||||
|
password_setting: 'Impostazione password',
|
||||||
|
new_password: 'Nuova password',
|
||||||
|
confirm_password: 'Conferma password',
|
||||||
|
enter_password: 'Inserisci la tua password',
|
||||||
|
enter_password_subtitle: 'Verifica che sia tu per proteggere la sicurezza del tuo account.',
|
||||||
|
set_password: 'Imposta password',
|
||||||
|
verify_via_password: 'Verifica tramite password',
|
||||||
|
show_password: 'Mostra password',
|
||||||
|
required: 'La password è obbligatoria',
|
||||||
|
min_length: 'La password deve contenere almeno {{min}} caratteri',
|
||||||
|
do_not_match: 'Le password non corrispondono. Riprova.',
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
enter_verification_code: 'Inserisci il codice di verifica',
|
||||||
|
enter_verification_code_subtitle:
|
||||||
|
'Il codice di verifica è stato inviato a <strong>{{target}}</strong>',
|
||||||
|
verify_via_code: 'Verifica tramite codice di verifica',
|
||||||
|
resend: 'Invia nuovamente il codice di verifica',
|
||||||
|
resend_countdown: 'Invia nuovamente in {{countdown}} secondi',
|
||||||
|
},
|
||||||
|
delete_account: {
|
||||||
|
title: 'ELIMINA ACCOUNT',
|
||||||
|
label: 'Elimina account',
|
||||||
|
description:
|
||||||
|
'Eliminando il tuo account, verranno rimossi tutti i tuoi dati personali, le informazioni utente, la configurazione. Questa operazione non può essere annullata.',
|
||||||
|
button: 'Elimina account',
|
||||||
|
dialog_paragraph_1:
|
||||||
|
"Ci dispiace sapere che desideri eliminare il tuo account. L'eliminazione dell'account rimuoverà permanentemente tutti i dati, inclusi le informazioni utente, i log e le impostazioni, e questa azione non può essere annullata. Quindi assicurati di eseguire il backup di eventuali dati importanti prima di procedere.",
|
||||||
|
dialog_paragraph_2:
|
||||||
|
"Per procedere con il processo di eliminazione dell'account, invia un'email al nostro team di supporto all'indirizzo <a>{{mail}}</a> con l'oggetto \"Richiesta di eliminazione account\". Ti assisteremo e ci assicureremo che tutti i tuoi dati siano correttamente eliminati dal nostro sistema.",
|
||||||
|
dialog_paragraph_3:
|
||||||
|
'Grazie per aver scelto Logto Cloud. Se hai ulteriori domande o dubbi, non esitare a contattarci.',
|
||||||
|
},
|
||||||
|
set: 'Imposta',
|
||||||
|
change: 'Cambia',
|
||||||
|
link: 'Collega',
|
||||||
|
unlink: 'Scollega',
|
||||||
|
not_set: 'Non impostato',
|
||||||
|
change_avatar: 'Cambia avatar',
|
||||||
|
change_name: 'Cambia nome',
|
||||||
|
change_username: 'Cambia nome utente',
|
||||||
|
set_name: 'Imposta nome',
|
||||||
|
email_changed: 'Email modificata!',
|
||||||
|
password_changed: 'Password modificata!',
|
||||||
|
updated: '{{target}} aggiornato!',
|
||||||
|
linked: '{{target}} collegato!',
|
||||||
|
unlinked: '{{target}} scollegato!',
|
||||||
|
email_exists_reminder:
|
||||||
|
"Questa email {{email}} è associata a un account esistente. Collega un'altra email qui.",
|
||||||
|
unlink_confirm_text: 'Sì, scollega',
|
||||||
|
unlink_reminder:
|
||||||
|
"Gli utenti non potranno più accedere tramite l'account <span></span> se lo scolleghi. Sicuro di procedere?",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default profile;
|
|
@ -0,0 +1,51 @@
|
||||||
|
const role_details = {
|
||||||
|
back_to_roles: 'Torna ai Ruoli',
|
||||||
|
identifier: 'Identificatore',
|
||||||
|
delete_description:
|
||||||
|
'Ciò cancellerà anche i permessi associati a questo ruolo dagli utenti coinvolti e cancellerà la corrispondenza tra ruoli, utenti e permessi.',
|
||||||
|
role_deleted: '{{name}} è stato cancellato con successo!',
|
||||||
|
settings_tab: 'Impostazioni',
|
||||||
|
users_tab: 'Utenti',
|
||||||
|
permissions_tab: 'Permessi',
|
||||||
|
settings: 'Impostazioni',
|
||||||
|
settings_description:
|
||||||
|
"I ruoli sono un raggruppamento di autorizzazioni che possono essere assegnate agli utenti. Forniscono anche un modo per aggregare le autorizzazioni definite per diverse API, rendendo più efficiente l'aggiunta, la rimozione o la regolazione delle autorizzazioni rispetto all'assegnazione individuale agli utenti.",
|
||||||
|
field_name: 'Nome',
|
||||||
|
field_description: 'Descrizione',
|
||||||
|
permission: {
|
||||||
|
assign_button: 'Assegna permessi',
|
||||||
|
assign_title: 'Assegna permessi',
|
||||||
|
assign_subtitle:
|
||||||
|
'Assegna autorizzazioni a questo ruolo. Il ruolo acquisirà le autorizzazioni aggiunte e gli utenti con questo ruolo erediteranno queste autorizzazioni.',
|
||||||
|
assign_form_field: 'Assegna permessi',
|
||||||
|
added_text_one: '{{count, number}} permesso aggiunto',
|
||||||
|
added_text_other: '{{count, number}} autorizzazioni aggiunte',
|
||||||
|
api_permission_count_one: '{{count, number}} permesso',
|
||||||
|
api_permission_count_other: '{{count, number}} autorizzazioni',
|
||||||
|
confirm_assign: 'Assegna permessi',
|
||||||
|
permission_assigned:
|
||||||
|
'Le autorizzazioni selezionate sono state assegnate con successo a questo ruolo',
|
||||||
|
deletion_description:
|
||||||
|
"Se questa autorizzazione viene rimossa, l'utente interessato con questo ruolo perderà l'accesso garantito da questa autorizzazione.",
|
||||||
|
permission_deleted: 'La permissione "{{name}}" è stata rimossa con successo da questo ruolo',
|
||||||
|
empty: 'Nessuna autorizzazione disponibile',
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
assign_button: 'Assegna utenti',
|
||||||
|
name_column: 'Utente',
|
||||||
|
app_column: 'App',
|
||||||
|
latest_sign_in_column: 'Ultimo accesso',
|
||||||
|
delete_description:
|
||||||
|
"Resterà nella tua raccolta di utenti ma perderà l'autorizzazione per questo ruolo.",
|
||||||
|
deleted: '{{name}} è stato rimosso con successo da questo ruolo',
|
||||||
|
assign_title: 'Assegna utenti',
|
||||||
|
assign_subtitle:
|
||||||
|
'Assegna utenti a questo ruolo. Trova utenti appropriati cercando nome, email, telefono o ID utente.',
|
||||||
|
assign_users_field: 'Assegna utenti',
|
||||||
|
confirm_assign: 'Assegna utenti',
|
||||||
|
users_assigned: 'Gli utenti selezionati sono stati assegnati con successo a questo ruolo',
|
||||||
|
empty: 'Nessun utente disponibile',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default role_details;
|
|
@ -0,0 +1,24 @@
|
||||||
|
const roles = {
|
||||||
|
page_title: 'Ruoli',
|
||||||
|
title: 'Ruoli',
|
||||||
|
subtitle:
|
||||||
|
"I ruoli includono le autorizzazioni che determinano ciò che un utente può fare. RBAC utilizza i ruoli per dare agli utenti l'accesso alle risorse necessarie per specifiche azioni.",
|
||||||
|
create: 'Crea Ruolo',
|
||||||
|
role_name: 'Ruolo',
|
||||||
|
role_description: 'Descrizione',
|
||||||
|
role_name_placeholder: 'Inserisci il nome del tuo ruolo',
|
||||||
|
role_description_placeholder: 'Inserisci la descrizione del tuo ruolo',
|
||||||
|
assigned_users: 'Utenti assegnati',
|
||||||
|
assign_permissions: 'Assegna autorizzazioni',
|
||||||
|
create_role_title: 'Crea Ruolo',
|
||||||
|
create_role_description:
|
||||||
|
'Crea e gestisci ruoli per le tue applicazioni. I ruoli contengono collezioni di autorizzazioni e possono essere assegnati agli utenti.',
|
||||||
|
create_role_button: 'Crea Ruolo',
|
||||||
|
role_created: 'Il ruolo {{name}} è stato creato con successo!',
|
||||||
|
search: 'Cerca per nome, descrizione o ID del ruolo',
|
||||||
|
placeholder_title: 'Ruoli',
|
||||||
|
placeholder_description:
|
||||||
|
'I ruoli sono un raggruppamento di autorizzazioni che possono essere assegnati agli utenti. Assicurati di aggiungere le autorizzazioni prima di creare i ruoli.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default roles;
|
|
@ -0,0 +1,8 @@
|
||||||
|
const session_expired = {
|
||||||
|
title: 'La sessione è scaduta',
|
||||||
|
subtitle:
|
||||||
|
'La tua sessione potrebbe essere scaduta e sei stato disconnesso. Clicca il pulsante qui sotto per accedere di nuovo alla console di amministrazione.',
|
||||||
|
button: 'Accedi di nuovo',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default session_expired;
|
|
@ -0,0 +1,88 @@
|
||||||
|
import others from './others.js';
|
||||||
|
import sign_up_and_sign_in from './sign-up-and-sign-in.js';
|
||||||
|
|
||||||
|
const sign_in_exp = {
|
||||||
|
page_title: 'Esperienza di accesso',
|
||||||
|
title: 'Esperienza di accesso',
|
||||||
|
description:
|
||||||
|
"Personalizza l'interfaccia di accesso per abbinarla al tuo marchio e visualizzala in tempo reale",
|
||||||
|
tabs: {
|
||||||
|
branding: 'Marchio',
|
||||||
|
sign_up_and_sign_in: 'Registrazione e accesso',
|
||||||
|
others: 'Altri',
|
||||||
|
},
|
||||||
|
welcome: {
|
||||||
|
title: "Personalizza l'esperienza di accesso",
|
||||||
|
description:
|
||||||
|
'Inizia subito con la configurazione del tuo primo accesso. Questa guida ti guiderà attraverso tutte le impostazioni necessarie.',
|
||||||
|
get_started: 'Inizia',
|
||||||
|
apply_remind:
|
||||||
|
"Si prega di notare che l'esperienza di accesso verrà applicata a tutte le applicazioni in questo account.",
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
title: 'COLORE',
|
||||||
|
primary_color: 'Colore del marchio',
|
||||||
|
dark_primary_color: 'Colore del marchio (scuro)',
|
||||||
|
dark_mode: 'Abilita modalità scura',
|
||||||
|
dark_mode_description:
|
||||||
|
"La tua app avrà un tema modalità scura generato automaticamente in base al tuo colore del marchio e all'algoritmo Logto. Sei libero di personalizzare.",
|
||||||
|
dark_mode_reset_tip: 'Ricalcola il colore della modalità scura in base al colore del marchio.',
|
||||||
|
reset: 'Ricalcola',
|
||||||
|
},
|
||||||
|
branding: {
|
||||||
|
title: 'AREA DI BRANDIZZO',
|
||||||
|
ui_style: 'Stile',
|
||||||
|
favicon: 'Favicon',
|
||||||
|
logo_image_url: "URL dell'immagine del logo dell'app",
|
||||||
|
logo_image_url_placeholder: 'https://your.cdn.domain/logo.png',
|
||||||
|
dark_logo_image_url: "URL dell'immagine del logo dell'app (scuro)",
|
||||||
|
dark_logo_image_url_placeholder: 'https://your.cdn.domain/logo-dark.png',
|
||||||
|
logo_image: "Logo dell'app",
|
||||||
|
dark_logo_image: "Logo dell'app (scuro)",
|
||||||
|
logo_image_error: "Logo dell'app: {{error}}",
|
||||||
|
favicon_error: 'Favicon: {{error}}',
|
||||||
|
},
|
||||||
|
custom_css: {
|
||||||
|
title: 'CSS personalizzato',
|
||||||
|
css_code_editor_title: 'Personalizza la tua interfaccia utente con CSS personalizzato',
|
||||||
|
css_code_editor_description1: "Guarda l'esempio di CSS personalizzato.",
|
||||||
|
css_code_editor_description2: '<a>{{link}}</a>',
|
||||||
|
css_code_editor_description_link_content: 'Ulteriori informazioni',
|
||||||
|
css_code_editor_content_placeholder:
|
||||||
|
'Inserisci il tuo CSS personalizzato per adattare lo stile di qualsiasi cosa alle tue specifiche. Esprimi la tua creatività e fai risaltare la tua interfaccia utente.',
|
||||||
|
},
|
||||||
|
sign_up_and_sign_in,
|
||||||
|
others,
|
||||||
|
setup_warning: {
|
||||||
|
no_connector_sms:
|
||||||
|
'Nessun connettore SMS ancora configurato. Prima di completare la configurazione, gli utenti non saranno in grado di accedere con questo metodo. <a>{{link}}</a> in "Connettori"',
|
||||||
|
no_connector_email:
|
||||||
|
'Nessun connettore email ancora configurato. Prima di completare la configurazione, gli utenti non saranno in grado di accedere con questo metodo. <a>{{link}}</a> in "Connettori"',
|
||||||
|
no_connector_social:
|
||||||
|
'Nessun connettore sociale ancora configurato. Prima di completare la configurazione, gli utenti non saranno in grado di accedere con questo metodo. <a>{{link}}</a> in "Connettori"',
|
||||||
|
no_added_social_connector:
|
||||||
|
'Hai configurato alcuni connettori sociali adesso. Assicurati di aggiungerne alcuni alla tua esperienza di accesso.',
|
||||||
|
setup_link: 'Configura',
|
||||||
|
},
|
||||||
|
save_alert: {
|
||||||
|
description:
|
||||||
|
'Stai implementando nuove procedure di accesso e registrazione. Tutti i tuoi utenti potrebbero essere influenzati dalla nuova configurazione. Sei sicuro di voler procedere con il cambiamento?',
|
||||||
|
before: 'Prima',
|
||||||
|
after: 'Dopo',
|
||||||
|
sign_up: 'Registrazione',
|
||||||
|
sign_in: 'Accesso',
|
||||||
|
social: 'Sociale',
|
||||||
|
},
|
||||||
|
preview: {
|
||||||
|
title: 'Anteprima di accesso',
|
||||||
|
live_preview: 'Anteprima in diretta',
|
||||||
|
live_preview_tip: 'Salva per visualizzare le modifiche',
|
||||||
|
native: 'Nat',
|
||||||
|
desktop_web: 'Web desktop',
|
||||||
|
mobile_web: 'Web mobile',
|
||||||
|
desktop: 'Desktop',
|
||||||
|
mobile: 'Mobile',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default sign_in_exp;
|
|
@ -0,0 +1,48 @@
|
||||||
|
const others = {
|
||||||
|
terms_of_use: {
|
||||||
|
title: 'TERMINI',
|
||||||
|
terms_of_use: "URL dei termini d'uso",
|
||||||
|
terms_of_use_placeholder: 'https://tuoi.termini.di.uso/',
|
||||||
|
privacy_policy: 'URL della politica sulla privacy',
|
||||||
|
privacy_policy_placeholder: 'https://tua.politica.sulla.privacy/',
|
||||||
|
},
|
||||||
|
languages: {
|
||||||
|
title: 'LINGUE',
|
||||||
|
enable_auto_detect: 'Abilita rilevamento automatico',
|
||||||
|
description:
|
||||||
|
"Il software rileva l'impostazione di lingua dell'utente e passa alla lingua locale. Puoi aggiungere nuove lingue traducendo l'interfaccia utente dall'inglese a un'altra lingua.",
|
||||||
|
manage_language: 'Gestisci lingua',
|
||||||
|
default_language: 'Lingua predefinita',
|
||||||
|
default_language_description_auto:
|
||||||
|
"La lingua predefinita verrà utilizzata quando la lingua dell'utente rilevata non è coperta dalla libreria delle lingue attuali.",
|
||||||
|
default_language_description_fixed:
|
||||||
|
"Quando il rilevamento automatico è disattivato, l'unica lingua che il tuo software mostrerà è quella predefinita. Attivare il rilevamento automatico per l'estensione delle lingue.",
|
||||||
|
},
|
||||||
|
manage_language: {
|
||||||
|
title: 'Gestisci lingua',
|
||||||
|
subtitle:
|
||||||
|
"Localizza l'esperienza del prodotto aggiungendo lingue e traduzioni. Il tuo contributo può essere impostato come lingua predefinita.",
|
||||||
|
add_language: 'Aggiungi lingua',
|
||||||
|
logto_provided: 'Logtogli forniti',
|
||||||
|
key: 'Chiave',
|
||||||
|
logto_source_values: 'Valori di origine Logtogli',
|
||||||
|
custom_values: 'Valori personalizzati',
|
||||||
|
clear_all_tip: 'Cancella tutti i valori',
|
||||||
|
unsaved_description: 'Le modifiche non saranno salvate se lasci la pagina senza salvare.',
|
||||||
|
deletion_tip: 'Elimina la lingua',
|
||||||
|
deletion_title: 'Vuoi eliminare la lingua aggiunta?',
|
||||||
|
deletion_description:
|
||||||
|
"Dopo l'eliminazione, i tuoi utenti non saranno più in grado di navigare in quella lingua.",
|
||||||
|
default_language_deletion_title: 'La lingua predefinita non può essere eliminata.',
|
||||||
|
default_language_deletion_description:
|
||||||
|
'{{language}} è impostata come tua lingua predefinita e non può essere eliminata.',
|
||||||
|
},
|
||||||
|
advanced_options: {
|
||||||
|
title: 'OPZIONI AVANZATE',
|
||||||
|
enable_user_registration: 'Abilita registrazione utente',
|
||||||
|
enable_user_registration_description:
|
||||||
|
"Abilitare o impedire la registrazione degli utenti. Una volta disattivata, gli utenti possono ancora essere aggiunti nella console di amministrazione ma gli utenti non possono più creare account attraverso l'interfaccia di accesso.",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default others;
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue