web: replace rollup with esbuild (#8699)
* Holding for a moment... * web: replace rollup with esbuild This commit replaces rollup with esbuild. The biggest fix was to alter the way CSS is imported into our system; esbuild delivers it to the browser as text, rather than as a bundle with metadata that, frankly, we never use. ESBuild will bundle the CSS for us just fine, and interpreting those strings *as* CSS turned out to be a small hurdle. Code has been added to AKElement and Interface to ensure that all CSS referenced by an element has been converted to a Browser CSSStyleSheet before being presented to the browser. A similar fix has been provided for the markdown imports. The biggest headache there was that the re-arrangement of our documentation broke Jen's existing parser for fixing relative links. I've provided a corresponding hack that provides the necessary detail, but since the Markdown is being presented to the browser as text, we have to provide a hint in the markdown component for where any relative links should go, and we're importing and processing the markdown at runtime. This doesn't seem to be a big performance hit. The entire build process is driven by the new build script, `build.mjs`, which starts the esbuild process as a service connected to the build script and then runs the commands sent to it as fast as possible. The biggest "hack" in it is actually the replacement for rollup's `rollup-copy-plugin`, which is clever enough I'm surprised it doesn't exist as a standalone file-copy package in its own right. I've also used a filesystem watch library to encode a "watcher" mechanism into the build script. `node build.mjs --watch` will work on MacOS; I haven't tested it elsewhere, at least not yet. `node build.mjs --proxy` does what the old rollup.proxy.js script did. The savings are substantial. It takes less than two seconds to build the whole UI, a huge savings off the older ~45-50 seconds I routinely saw on my old Mac. It's also about 9% smaller. The trade-offs appear to be small: processing the CSS as StyleSheets, and the Markdown as HTML, at run-time is a small performance hit, but I didn't notice it in amongst everything else the UI does as it starts up. Manual chunking is gone; esbuild's support for that is quite difficult to get right compared to Rollup's, although there's been a bit of yelling at ESbuild over it. Codemirror is built into its own chunk; it's just not _named_ distinctly anymore. The one thing I haven't been able to test yet is whether or not the polyfills and runtim shims work as expected on older browsers. * web: continue with performance and build fixes This commit introduces a couple of fixes enabled by esbuild and other features. 1. build-locales `build-locales` is a new NodeJS script in the `./scripts` folder that does pretty much what it says in the name: it translates Xliff files into `.ts` files. It has two DevExp advantages over the old build system. First, it will check the build times of the xlf files and their ts equivalents, and will only run the actual build-locales command if the XLF files are newer than their TS equivalents. Second, it captures the stderr output from the build-locales command and summarizes it. Instead of the thousands of lines of "this string has no translation equivalent," now it just reports the number of missed translations per locale. 2. check-spelling This is a simple wrapper around the `codespell` command, mostly just to reduce the visual clutter of `package.json`, but also to permit it to run just about anywhere without needed hard-coded paths to the dictionaries, using a fairly classic trick with git. 3. pseudolocalize and import-maps These scripts were in TypeScript, but for our purposes I've saved their constructed equivalents instead. This saves on visual clutter in the `package.json` script, and reduced the time they have to run during full builds. They're small enough I feel confident they won't need too much looking over. Also, two lint bugs in Markdown.ts have been fixed. * Removed a few lines that weren't in use. * build-locales was sufficiently complex it needed some comments. * web: formalize that horrible unixy git status checker into a proper function. * Added types for , the Markdown processor for in-line documentation. * re-add dependencies required for storybook Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix optional deps Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix relative links for docs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * only build once on startup Signed-off-by: Jens Langhammer <jens@goauthentik.io> * prevent crash when build fails in watch mode, improve console output Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Co-authored-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
2
web/.gitignore
vendored
2
web/.gitignore
vendored
@ -109,5 +109,3 @@ temp/
|
|||||||
# End of https://www.gitignore.io/api/node
|
# End of https://www.gitignore.io/api/node
|
||||||
api/**
|
api/**
|
||||||
storybook-static/
|
storybook-static/
|
||||||
scripts/*.mjs
|
|
||||||
scripts/*.js
|
|
||||||
|
147
web/build.mjs
Normal file
147
web/build.mjs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
import * as chokidar from "chokidar";
|
||||||
|
import esbuild from "esbuild";
|
||||||
|
import fs from "fs";
|
||||||
|
import { globSync } from "glob";
|
||||||
|
import path from "path";
|
||||||
|
import { cwd } from "process";
|
||||||
|
import process from "process";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
const isProdBuild = process.env.NODE_ENV === "production";
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
const apiBasePath = process.env.AK_API_BASE_PATH || "";
|
||||||
|
|
||||||
|
const definitions = {
|
||||||
|
"process.env.NODE_ENV": JSON.stringify(isProdBuild ? "production" : "development"),
|
||||||
|
"process.env.CWD": JSON.stringify(cwd()),
|
||||||
|
"process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath),
|
||||||
|
};
|
||||||
|
|
||||||
|
// All is magic is just to make sure the assets are copied into the right places. This is a very stripped down version
|
||||||
|
// of what the rollup-copy-plugin does, without any of the features we don't use, and using globSync instead of globby
|
||||||
|
// since we already had globSync lying around thanks to Typescript. If there's a third argument in an array entry, it's
|
||||||
|
// used to replace the internal path before concatenating it all together as the destination target.
|
||||||
|
|
||||||
|
const otherFiles = [
|
||||||
|
["node_modules/@patternfly/patternfly/patternfly.min.css", "."],
|
||||||
|
["node_modules/@patternfly/patternfly/assets/**", ".", "node_modules/@patternfly/patternfly/"],
|
||||||
|
["src/custom.css", "."],
|
||||||
|
["src/common/styles/**", "."],
|
||||||
|
["src/assets/images/**", "./assets/images"],
|
||||||
|
["./icons/*", "./assets/icons"],
|
||||||
|
];
|
||||||
|
|
||||||
|
const isFile = (filePath) => fs.statSync(filePath).isFile();
|
||||||
|
function nameCopyTarget(src, dest, strip) {
|
||||||
|
const target = path.join(dest, strip ? src.replace(strip, "") : path.parse(src).base);
|
||||||
|
return [src, target];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [source, rawdest, strip] of otherFiles) {
|
||||||
|
const matchedPaths = globSync(source);
|
||||||
|
const dest = path.join("dist", rawdest);
|
||||||
|
const copyTargets = matchedPaths.map((path) => nameCopyTarget(path, dest, strip));
|
||||||
|
for (const [src, dest] of copyTargets) {
|
||||||
|
if (isFile(src)) {
|
||||||
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
||||||
|
fs.copyFileSync(src, dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This starts the definitions used for esbuild: Our targets, our arguments, the function for running a build, and three
|
||||||
|
// options for building: watching, building, and building the proxy.
|
||||||
|
|
||||||
|
const interfaces = [
|
||||||
|
["polyfill/poly.ts", "."],
|
||||||
|
["standalone/loading/index.ts", "standalone/loading"],
|
||||||
|
["flow/FlowInterface.ts", "flow"],
|
||||||
|
["user/UserInterface.ts", "user"],
|
||||||
|
["enterprise/rac/index.ts", "enterprise/rac"],
|
||||||
|
["standalone/api-browser/index.ts", "standalone/api-browser"],
|
||||||
|
["admin/AdminInterface/AdminInterface.ts", "admin"],
|
||||||
|
];
|
||||||
|
|
||||||
|
const baseArgs = {
|
||||||
|
bundle: true,
|
||||||
|
write: true,
|
||||||
|
sourcemap: !isProdBuild,
|
||||||
|
minify: isProdBuild,
|
||||||
|
splitting: true,
|
||||||
|
treeShaking: true,
|
||||||
|
external: ["*.woff", "*.woff2"],
|
||||||
|
tsconfig: "./tsconfig.json",
|
||||||
|
loader: { ".css": "text", ".md": "text" },
|
||||||
|
define: definitions,
|
||||||
|
format: "esm",
|
||||||
|
};
|
||||||
|
|
||||||
|
function buildAuthentik(interfaces) {
|
||||||
|
const start = Date.now();
|
||||||
|
console.clear();
|
||||||
|
for (const [source, dest] of interfaces) {
|
||||||
|
const DIST = path.join(__dirname, "./dist", dest);
|
||||||
|
console.log(`[${new Date(start).toISOString()}] Starting build for target ${source}`);
|
||||||
|
try {
|
||||||
|
esbuild.buildSync({
|
||||||
|
...baseArgs,
|
||||||
|
entryPoints: [`./src/${source}`],
|
||||||
|
outdir: DIST,
|
||||||
|
});
|
||||||
|
} catch (exc) {
|
||||||
|
console.error(
|
||||||
|
`[${new Date(Date.now()).toISOString()}] Failed to build ${source}: ${exc}`,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const end = Date.now();
|
||||||
|
console.log(
|
||||||
|
`[${new Date(end).toISOString()}] Finished build for target ${source} in ${Date.now() - start}ms`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeoutId = null;
|
||||||
|
function debouncedBuild() {
|
||||||
|
if (timeoutId !== null) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
buildAuthentik(interfaces);
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.argv.length > 2 && (process.argv[2] === "-h" || process.argv[2] === "--help")) {
|
||||||
|
console.log(`Build the authentikUI
|
||||||
|
|
||||||
|
options:
|
||||||
|
-w, --watch: Build all ${interfaces.length} interfaces
|
||||||
|
-p, --proxy: Build only the polyfills and the loading application
|
||||||
|
-h, --help: This help message
|
||||||
|
`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.argv.length > 2 && (process.argv[2] === "-w" || process.argv[2] === "--watch")) {
|
||||||
|
console.log("Watching ./src for changes");
|
||||||
|
chokidar.watch("./src").on("all", (event, path) => {
|
||||||
|
if (!["add", "change", "unlink"].includes(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!/(\.css|\.ts|\.js)$/.test(path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
debouncedBuild();
|
||||||
|
});
|
||||||
|
} else if (process.argv.length > 2 && (process.argv[2] === "-p" || process.argv[2] === "--proxy")) {
|
||||||
|
// There's no watch-for-proxy, sorry.
|
||||||
|
buildAuthentik(interfaces.slice(0, 2));
|
||||||
|
process.exit(0);
|
||||||
|
} else {
|
||||||
|
// And the fallback: just build it.
|
||||||
|
buildAuthentik(interfaces);
|
||||||
|
}
|
6296
web/package-lock.json
generated
6296
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -5,33 +5,29 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"extract-locales": "lit-localize extract",
|
"extract-locales": "lit-localize extract",
|
||||||
"build-locales": "run-s build-locales:build",
|
"build-locales": "node scripts/build-locales.mjs",
|
||||||
"build-locales:build": "lit-localize build",
|
"build-locales:build": "lit-localize build",
|
||||||
"build-locales:repair": "prettier --write ./src/locale-codes.ts",
|
"build-locales:repair": "prettier --write ./src/locale-codes.ts",
|
||||||
"rollup:build": "cross-env NODE_OPTIONS='--max_old_space_size=8192' rollup -c ./rollup.config.mjs",
|
"esbuild:build": "node build.mjs",
|
||||||
"rollup:build-proxy": "cross-env NODE_OPTIONS='--max_old_space_size=8192' rollup -c ./rollup.proxy.mjs",
|
"esbuild:build-proxy": "node build.mjs --proxy",
|
||||||
"rollup:watch": "cross-env NODE_OPTIONS='--max_old_space_size=8192' rollup -c -w",
|
"esbuild:watch": "node build.mjs --watch",
|
||||||
"build": "run-s build-locales rollup:build",
|
"build": "run-s build-locales esbuild:build",
|
||||||
"build-proxy": "run-s build-locales rollup:build-proxy",
|
"build-proxy": "run-s build-locales esbuild:build-proxy",
|
||||||
"watch": "run-s build-locales rollup:watch",
|
"watch": "run-s build-locales esbuild:watch",
|
||||||
"lint": "eslint . --max-warnings 0 --fix",
|
"lint": "eslint . --max-warnings 0 --fix",
|
||||||
"lint:precommit": "eslint --max-warnings 0 --config ./.eslintrc.precommit.json $(git status --porcelain . | grep '^[M?][M?]' | cut -c8- | grep -E '\\.(ts|js|tsx|jsx)$') ",
|
"lint:precommit": "node scripts/eslint-precommit.mjs",
|
||||||
"lint:spelling": "codespell -D - -D ../.github/codespell-dictionary.txt -I ../.github/codespell-words.txt -S './src/locales/**' ./src -s",
|
"lint:spelling": "node scripts/check-spelling.mjs",
|
||||||
"lit-analyse": "lit-analyzer src",
|
"lit-analyse": "lit-analyzer src",
|
||||||
"precommit": "run-s tsc lit-analyse lint:precommit lint:spelling prettier",
|
"precommit": "npm-run-all --parallel tsc lit-analyse lint:spelling --sequential lint:precommit prettier",
|
||||||
"prequick": "run-s tsc:execute lit-analyse lint:precommit lint:spelling",
|
"prequick": "run-s tsc:execute lit-analyse lint:precommit lint:spelling",
|
||||||
"prettier-check": "prettier --check .",
|
"prettier-check": "prettier --check .",
|
||||||
"prettier": "prettier --write .",
|
"prettier": "prettier --write .",
|
||||||
"pseudolocalize:build-extract-script": "cd scripts && tsc --esModuleInterop --module es2020 --moduleResolution 'node' pseudolocalize.ts && mv pseudolocalize.js pseudolocalize.mjs",
|
"pseudolocalize": "node scripts/pseudolocalize.mjs",
|
||||||
"pseudolocalize:extract": "node scripts/pseudolocalize.mjs",
|
|
||||||
"pseudolocalize": "run-s pseudolocalize:build-extract-script pseudolocalize:extract",
|
|
||||||
"tsc:execute": "tsc --noEmit -p .",
|
"tsc:execute": "tsc --noEmit -p .",
|
||||||
"tsc": "run-s build-locales tsc:execute",
|
"tsc": "run-s build-locales tsc:execute",
|
||||||
"storybook": "storybook dev -p 6006",
|
"storybook": "storybook dev -p 6006",
|
||||||
"storybook:build": "cross-env NODE_OPTIONS='--max_old_space_size=8192' storybook build",
|
"storybook:build": "cross-env NODE_OPTIONS='--max_old_space_size=8192' storybook build",
|
||||||
"storybook:build-import-map": "run-s storybook:build-import-map-script storybook:run-import-map-script",
|
"storybook:build-import-map": "node scripts/build-storybook-import-maps.mjs"
|
||||||
"storybook:build-import-map-script": "cd scripts && tsc --esModuleInterop --module es2020 --target es2020 --moduleResolution 'node' build-storybook-import-maps.ts && mv build-storybook-import-maps.js build-storybook-import-maps.mjs",
|
|
||||||
"storybook:run-import-map-script": "node scripts/build-storybook-import-maps.mjs"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/lang-html": "^6.4.8",
|
"@codemirror/lang-html": "^6.4.8",
|
||||||
@ -61,8 +57,10 @@
|
|||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"guacamole-common-js": "^1.5.0",
|
"guacamole-common-js": "^1.5.0",
|
||||||
"lit": "^2.8.0",
|
"lit": "^2.8.0",
|
||||||
|
"md-front-matter": "^1.0.4",
|
||||||
"mermaid": "^10.9.0",
|
"mermaid": "^10.9.0",
|
||||||
"rapidoc": "^9.3.4",
|
"rapidoc": "^9.3.4",
|
||||||
|
"showdown": "^2.1.0",
|
||||||
"style-mod": "^4.1.2",
|
"style-mod": "^4.1.2",
|
||||||
"ts-pattern": "^5.0.6",
|
"ts-pattern": "^5.0.6",
|
||||||
"webcomponent-qr-code": "^1.2.0",
|
"webcomponent-qr-code": "^1.2.0",
|
||||||
@ -78,16 +76,9 @@
|
|||||||
"@babel/preset-env": "^7.24.0",
|
"@babel/preset-env": "^7.24.0",
|
||||||
"@babel/preset-typescript": "^7.23.3",
|
"@babel/preset-typescript": "^7.23.3",
|
||||||
"@hcaptcha/types": "^1.0.3",
|
"@hcaptcha/types": "^1.0.3",
|
||||||
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
|
|
||||||
"@jeysal/storybook-addon-css-user-preferences": "^0.2.0",
|
"@jeysal/storybook-addon-css-user-preferences": "^0.2.0",
|
||||||
"@lit/localize-tools": "^0.7.2",
|
"@lit/localize-tools": "^0.7.2",
|
||||||
"@rollup/plugin-babel": "^6.0.4",
|
"@spotlightjs/spotlight": "^1.2.12",
|
||||||
"@rollup/plugin-commonjs": "^25.0.7",
|
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
||||||
"@rollup/plugin-replace": "^5.0.5",
|
|
||||||
"@rollup/plugin-terser": "^0.4.4",
|
|
||||||
"@rollup/plugin-typescript": "^11.1.6",
|
|
||||||
"@spotlightjs/spotlight": "^1.2.13",
|
|
||||||
"@storybook/addon-essentials": "^7.6.17",
|
"@storybook/addon-essentials": "^7.6.17",
|
||||||
"@storybook/addon-links": "^7.6.17",
|
"@storybook/addon-links": "^7.6.17",
|
||||||
"@storybook/api": "^7.6.17",
|
"@storybook/api": "^7.6.17",
|
||||||
@ -100,11 +91,17 @@
|
|||||||
"@types/codemirror": "5.60.15",
|
"@types/codemirror": "5.60.15",
|
||||||
"@types/grecaptcha": "^3.0.8",
|
"@types/grecaptcha": "^3.0.8",
|
||||||
"@types/guacamole-common-js": "1.5.2",
|
"@types/guacamole-common-js": "1.5.2",
|
||||||
|
"@types/showdown": "^2.0.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
||||||
"@typescript-eslint/parser": "^7.1.1",
|
"@typescript-eslint/parser": "^7.1.1",
|
||||||
|
"@rollup/plugin-replace": "^5.0.5",
|
||||||
|
"rollup-plugin-modify": "^3.0.0",
|
||||||
|
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||||
"babel-plugin-macros": "^3.1.0",
|
"babel-plugin-macros": "^3.1.0",
|
||||||
"babel-plugin-tsconfig-paths": "^1.0.3",
|
"babel-plugin-tsconfig-paths": "^1.0.3",
|
||||||
|
"chokidar": "^3.6.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
|
"esbuild": "^0.20.1",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-google": "^0.14.0",
|
"eslint-config-google": "^0.14.0",
|
||||||
"eslint-plugin-custom-elements": "0.0.8",
|
"eslint-plugin-custom-elements": "0.0.8",
|
||||||
@ -112,17 +109,13 @@
|
|||||||
"eslint-plugin-sonarjs": "^0.24.0",
|
"eslint-plugin-sonarjs": "^0.24.0",
|
||||||
"eslint-plugin-storybook": "^0.8.0",
|
"eslint-plugin-storybook": "^0.8.0",
|
||||||
"github-slugger": "^2.0.0",
|
"github-slugger": "^2.0.0",
|
||||||
|
"glob": "^10.3.10",
|
||||||
"lit-analyzer": "^2.0.3",
|
"lit-analyzer": "^2.0.3",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"pseudolocale": "^2.0.0",
|
"pseudolocale": "^2.0.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"rollup": "^4.12.0",
|
|
||||||
"rollup-plugin-copy": "^3.5.0",
|
|
||||||
"rollup-plugin-cssimport": "^1.0.3",
|
|
||||||
"rollup-plugin-modify": "^3.0.0",
|
|
||||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
|
||||||
"storybook": "^7.6.17",
|
"storybook": "^7.6.17",
|
||||||
"storybook-addon-mock": "^4.3.0",
|
"storybook-addon-mock": "^4.3.0",
|
||||||
"ts-lit-plugin": "^2.0.2",
|
"ts-lit-plugin": "^2.0.2",
|
||||||
@ -134,7 +127,10 @@
|
|||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/darwin-arm64": "^0.20.1",
|
"@esbuild/darwin-arm64": "^0.20.1",
|
||||||
"@esbuild/linux-amd64": "^0.18.11",
|
"@esbuild/linux-amd64": "^0.18.11",
|
||||||
"@esbuild/linux-arm64": "^0.20.1"
|
"@esbuild/linux-arm64": "^0.20.1",
|
||||||
|
"@rollup/rollup-darwin-arm64": "4.12.0",
|
||||||
|
"@rollup/rollup-linux-arm64-gnu": "4.12.0",
|
||||||
|
"@rollup/rollup-linux-x64-gnu": "4.12.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
|
@ -1,192 +0,0 @@
|
|||||||
import markdown from "@jackfranklin/rollup-plugin-markdown";
|
|
||||||
import babel from "@rollup/plugin-babel";
|
|
||||||
import commonjs from "@rollup/plugin-commonjs";
|
|
||||||
import { nodeResolve } from "@rollup/plugin-node-resolve";
|
|
||||||
import replace from "@rollup/plugin-replace";
|
|
||||||
import terser from "@rollup/plugin-terser";
|
|
||||||
import { cwd } from "process";
|
|
||||||
import copy from "rollup-plugin-copy";
|
|
||||||
import cssimport from "rollup-plugin-cssimport";
|
|
||||||
|
|
||||||
// https://github.com/d3/d3-interpolate/issues/58
|
|
||||||
const IGNORED_WARNINGS = /Circular dependency(.*d3-[interpolate|selection])|(.*@lit\/localize.*)/;
|
|
||||||
|
|
||||||
const extensions = [".js", ".jsx", ".ts", ".tsx"];
|
|
||||||
|
|
||||||
export const resources = [
|
|
||||||
{
|
|
||||||
src: "node_modules/@patternfly/patternfly/patternfly.min.css",
|
|
||||||
dest: "dist/",
|
|
||||||
},
|
|
||||||
{ src: "src/common/styles/*", dest: "dist/" },
|
|
||||||
{ src: "src/custom.css", dest: "dist/" },
|
|
||||||
|
|
||||||
{
|
|
||||||
src: "node_modules/@patternfly/patternfly/assets/*",
|
|
||||||
dest: "dist/assets/",
|
|
||||||
},
|
|
||||||
{ src: "src/assets/*", dest: "dist/assets" },
|
|
||||||
{ src: "./icons/*", dest: "dist/assets/icons" },
|
|
||||||
];
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
export const isProdBuild = process.env.NODE_ENV === "production";
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
export const apiBasePath = process.env.AK_API_BASE_PATH || "";
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
||||||
export function manualChunks(id) {
|
|
||||||
if (id.endsWith(".md")) {
|
|
||||||
return "docs";
|
|
||||||
}
|
|
||||||
if (id.includes("@goauthentik/api")) {
|
|
||||||
return "api";
|
|
||||||
}
|
|
||||||
if (id.includes("locales")) {
|
|
||||||
const parts = id.split("/");
|
|
||||||
const file = parts[parts.length - 1];
|
|
||||||
return "locale-" + file.replace(".ts", "");
|
|
||||||
}
|
|
||||||
if (id.includes("node_modules")) {
|
|
||||||
if (id.includes("codemirror")) {
|
|
||||||
return "vendor-cm";
|
|
||||||
}
|
|
||||||
return "vendor";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const defaultOptions = {
|
|
||||||
plugins: [
|
|
||||||
cssimport(),
|
|
||||||
markdown(),
|
|
||||||
nodeResolve({ extensions, browser: true }),
|
|
||||||
commonjs(),
|
|
||||||
babel({
|
|
||||||
extensions,
|
|
||||||
babelHelpers: "runtime",
|
|
||||||
include: ["src/**/*"],
|
|
||||||
}),
|
|
||||||
replace({
|
|
||||||
"process.env.NODE_ENV": JSON.stringify(isProdBuild ? "production" : "development"),
|
|
||||||
"process.env.CWD": JSON.stringify(cwd()),
|
|
||||||
"process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath),
|
|
||||||
"preventAssignment": true,
|
|
||||||
}),
|
|
||||||
isProdBuild && terser(),
|
|
||||||
].filter((p) => p),
|
|
||||||
watch: {
|
|
||||||
clearScreen: false,
|
|
||||||
},
|
|
||||||
preserveEntrySignatures: "strict",
|
|
||||||
cache: true,
|
|
||||||
context: "window",
|
|
||||||
onwarn: function (warning, warn) {
|
|
||||||
if (IGNORED_WARNINGS.test(warning)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (warning.code === "UNRESOLVED_IMPORT") {
|
|
||||||
throw Object.assign(new Error(), warning);
|
|
||||||
}
|
|
||||||
warn(warning);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Polyfills (imported first)
|
|
||||||
export const POLY = {
|
|
||||||
input: "./src/polyfill/poly.ts",
|
|
||||||
output: [
|
|
||||||
{
|
|
||||||
format: "iife",
|
|
||||||
file: "dist/poly.js",
|
|
||||||
sourcemap: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
cache: true,
|
|
||||||
plugins: [
|
|
||||||
cssimport(),
|
|
||||||
nodeResolve({ browser: true }),
|
|
||||||
commonjs(),
|
|
||||||
isProdBuild && terser(),
|
|
||||||
copy({
|
|
||||||
targets: [...resources],
|
|
||||||
copyOnce: false,
|
|
||||||
}),
|
|
||||||
].filter((p) => p),
|
|
||||||
};
|
|
||||||
|
|
||||||
export const standalone = ["api-browser", "loading"].map((input) => {
|
|
||||||
return {
|
|
||||||
input: `./src/standalone/${input}`,
|
|
||||||
output: [
|
|
||||||
{
|
|
||||||
format: "es",
|
|
||||||
dir: `dist/standalone/${input}`,
|
|
||||||
sourcemap: true,
|
|
||||||
manualChunks: manualChunks,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
...defaultOptions,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const enterprise = ["rac"].map((input) => {
|
|
||||||
return {
|
|
||||||
input: `./src/enterprise/${input}`,
|
|
||||||
output: [
|
|
||||||
{
|
|
||||||
format: "es",
|
|
||||||
dir: `dist/enterprise/${input}`,
|
|
||||||
sourcemap: true,
|
|
||||||
manualChunks: manualChunks,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
...defaultOptions,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export default [
|
|
||||||
POLY,
|
|
||||||
// Standalone
|
|
||||||
...standalone,
|
|
||||||
// Flow interface
|
|
||||||
{
|
|
||||||
input: "./src/flow/FlowInterface.ts",
|
|
||||||
output: [
|
|
||||||
{
|
|
||||||
format: "es",
|
|
||||||
dir: "dist/flow",
|
|
||||||
sourcemap: true,
|
|
||||||
manualChunks: manualChunks,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
...defaultOptions,
|
|
||||||
},
|
|
||||||
// Admin interface
|
|
||||||
{
|
|
||||||
input: "./src/admin/AdminInterface/AdminInterface.ts",
|
|
||||||
output: [
|
|
||||||
{
|
|
||||||
format: "es",
|
|
||||||
dir: "dist/admin",
|
|
||||||
sourcemap: true,
|
|
||||||
manualChunks: manualChunks,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
...defaultOptions,
|
|
||||||
},
|
|
||||||
// User interface
|
|
||||||
{
|
|
||||||
input: "./src/user/UserInterface.ts",
|
|
||||||
output: [
|
|
||||||
{
|
|
||||||
format: "es",
|
|
||||||
dir: "dist/user",
|
|
||||||
sourcemap: true,
|
|
||||||
manualChunks: manualChunks,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
...defaultOptions,
|
|
||||||
},
|
|
||||||
// Enterprise
|
|
||||||
...enterprise,
|
|
||||||
];
|
|
@ -1,3 +0,0 @@
|
|||||||
import { POLY, standalone } from "./rollup.config.mjs";
|
|
||||||
|
|
||||||
export default [POLY, ...standalone];
|
|
67
web/scripts/build-locales.mjs
Normal file
67
web/scripts/build-locales.mjs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { spawnSync } from "child_process";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import process from "process";
|
||||||
|
|
||||||
|
const localizeRules = JSON.parse(fs.readFileSync("./lit-localize.json", "utf-8"));
|
||||||
|
|
||||||
|
function compareXlfAndSrc(loc) {
|
||||||
|
const xlf = path.join("./xliff", `${loc}.xlf`);
|
||||||
|
const src = path.join("./src/locales", `${loc}.ts`);
|
||||||
|
|
||||||
|
// Returns false if: the expected XLF file doesn't exist, The expected
|
||||||
|
// generated file doesn't exist, or the XLF file is newer (has a higher date)
|
||||||
|
// than the generated file. The missing XLF file is important enough it
|
||||||
|
// generates a unique error message and halts the build.
|
||||||
|
|
||||||
|
try {
|
||||||
|
var xlfStat = fs.statSync(xlf);
|
||||||
|
} catch (_error) {
|
||||||
|
console.error(`lit-localize expected '${loc}.xlf', but XLF file is not present`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var srcStat = fs.statSync(src);
|
||||||
|
} catch (_error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the xlf is newer (greater) than src, it's out of date.
|
||||||
|
if (xlfStat.mtimeMs > srcStat.mtimeMs) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For all the expected files, find out if any aren't up-to-date.
|
||||||
|
|
||||||
|
const upToDate = localizeRules.targetLocales.reduce(
|
||||||
|
(acc, loc) => acc && compareXlfAndSrc(loc),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!upToDate) {
|
||||||
|
const status = spawnSync("npm", ["run", "build-locales:build"], { encoding: "utf8" });
|
||||||
|
|
||||||
|
// Count all the missing message warnings
|
||||||
|
const counts = status.stderr.split("\n").reduce((acc, line) => {
|
||||||
|
const match = /^([\w-]+) message/.exec(line);
|
||||||
|
if (!match) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
acc.set(match[1], (acc.get(match[1]) || 0) + 1);
|
||||||
|
return acc;
|
||||||
|
}, new Map());
|
||||||
|
|
||||||
|
const locales = Array.from(counts.keys());
|
||||||
|
locales.sort();
|
||||||
|
|
||||||
|
const report = locales
|
||||||
|
.map((locale) => `Locale '${locale}' has ${counts.get(locale)} missing translations`)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
console.log(`Translation tables rebuilt.\n${report}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Locale ./src is up-to-date");
|
@ -5,13 +5,12 @@ import { fileURLToPath } from "url";
|
|||||||
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
function* walkFilesystem(dir: string): Generator<string, undefined, any> {
|
function* walkFilesystem(dir) {
|
||||||
const openeddir = fs.opendirSync(dir);
|
const openeddir = fs.opendirSync(dir);
|
||||||
if (!openeddir) {
|
if (!openeddir) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let d;
|
||||||
let d: fs.Dirent | null;
|
|
||||||
while ((d = openeddir?.readSync())) {
|
while ((d = openeddir?.readSync())) {
|
||||||
if (!d) {
|
if (!d) {
|
||||||
break;
|
break;
|
||||||
@ -24,13 +23,14 @@ function* walkFilesystem(dir: string): Generator<string, undefined, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const import_re = /^(import \w+ from .*\.css)";/;
|
const import_re = /^(import \w+ from .*\.css)";/;
|
||||||
function extractImportLinesFromFile(path: string) {
|
|
||||||
|
function extractImportLinesFromFile(path) {
|
||||||
const source = fs.readFileSync(path, { encoding: "utf8", flag: "r" });
|
const source = fs.readFileSync(path, { encoding: "utf8", flag: "r" });
|
||||||
const lines = source?.split("\n") ?? [];
|
const lines = source?.split("\n") ?? [];
|
||||||
return lines.filter((l) => import_re.test(l));
|
return lines.filter((l) => import_re.test(l));
|
||||||
}
|
}
|
||||||
|
|
||||||
function createOneImportLine(line: string) {
|
function createOneImportLine(line) {
|
||||||
const importMatch = import_re.exec(line);
|
const importMatch = import_re.exec(line);
|
||||||
if (!importMatch) {
|
if (!importMatch) {
|
||||||
throw new Error("How did an unmatchable line get here?");
|
throw new Error("How did an unmatchable line get here?");
|
||||||
@ -43,15 +43,16 @@ function createOneImportLine(line: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isSourceFile = /\.ts$/;
|
const isSourceFile = /\.ts$/;
|
||||||
|
|
||||||
function getTheSourceFiles() {
|
function getTheSourceFiles() {
|
||||||
return Array.from(walkFilesystem(path.join(__dirname, "..", "src"))).filter((path) =>
|
return Array.from(walkFilesystem(path.join(__dirname, "..", "src"))).filter((path) =>
|
||||||
isSourceFile.test(path),
|
isSourceFile.test(path),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTheImportLines(importPaths: string[]) {
|
function getTheImportLines(importPaths) {
|
||||||
const importLines: string[] = importPaths.reduce(
|
const importLines = importPaths.reduce(
|
||||||
(acc: string[], path) => [...acc, extractImportLinesFromFile(path)].flat(),
|
(acc, path) => [...acc, extractImportLinesFromFile(path)].flat(),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
const uniqueImportLines = new Set(importLines);
|
const uniqueImportLines = new Set(importLines);
|
15
web/scripts/check-spelling.mjs
Normal file
15
web/scripts/check-spelling.mjs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { execSync } from "child_process";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
const projectRoot = execSync("git rev-parse --show-toplevel", { encoding: "utf8" }).replace(
|
||||||
|
"\n",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
const cmd = [
|
||||||
|
"codespell -D -",
|
||||||
|
`-D ${path.join(projectRoot, ".github/codespell-dictionary.txt")}`,
|
||||||
|
`-I ${path.join(projectRoot, ".github/codespell-words.txt")}`,
|
||||||
|
"-S './src/locales/**' ./src -s",
|
||||||
|
].join(" ");
|
||||||
|
|
||||||
|
console.log(execSync(cmd, { encoding: "utf8" }));
|
76
web/scripts/eslint-precommit.mjs
Normal file
76
web/scripts/eslint-precommit.mjs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { execFileSync } from "child_process";
|
||||||
|
import { ESLint } from "eslint";
|
||||||
|
import path from "path";
|
||||||
|
import process from "process";
|
||||||
|
|
||||||
|
// Code assumes this script is in the './web/scripts' folder.
|
||||||
|
const projectRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
||||||
|
encoding: "utf8",
|
||||||
|
}).replace("\n", "");
|
||||||
|
process.chdir(path.join(projectRoot, "./web"));
|
||||||
|
|
||||||
|
const eslintConfig = {
|
||||||
|
overrideConfig: {
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
es2021: true,
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:lit/recommended",
|
||||||
|
"plugin:custom-elements/recommended",
|
||||||
|
"plugin:storybook/recommended",
|
||||||
|
"plugin:sonarjs/recommended",
|
||||||
|
],
|
||||||
|
parser: "@typescript-eslint/parser",
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 12,
|
||||||
|
sourceType: "module",
|
||||||
|
},
|
||||||
|
plugins: ["@typescript-eslint", "lit", "custom-elements", "sonarjs"],
|
||||||
|
rules: {
|
||||||
|
"indent": "off",
|
||||||
|
"linebreak-style": ["error", "unix"],
|
||||||
|
"quotes": ["error", "double", { avoidEscape: true }],
|
||||||
|
"semi": ["error", "always"],
|
||||||
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
|
"sonarjs/cognitive-complexity": ["error", 9],
|
||||||
|
"sonarjs/no-duplicate-string": "off",
|
||||||
|
"sonarjs/no-nested-template-literals": "off",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const porcelainV1 = /^(..)\s+(.*$)/;
|
||||||
|
const gitStatus = execFileSync("git", ["status", "--porcelain", "."], { encoding: "utf8" });
|
||||||
|
|
||||||
|
const statuses = gitStatus.split("\n").reduce((acc, line) => {
|
||||||
|
const match = porcelainV1.exec(line.replace("\n"));
|
||||||
|
if (!match) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
const [status, path] = Array.from(match).slice(1, 3);
|
||||||
|
return [...acc, [status, path.split("\x00")[0]]];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const isModified = /^(M|\?|\s)(M|\?|\s)/;
|
||||||
|
const modified = (s) => isModified.test(s);
|
||||||
|
|
||||||
|
const isCheckable = /\.(ts|js|mjs)$/;
|
||||||
|
const checkable = (s) => isCheckable.test(s);
|
||||||
|
|
||||||
|
const updated = statuses.reduce(
|
||||||
|
(acc, [status, filename]) =>
|
||||||
|
modified(status) && checkable(filename) ? [...acc, path.join(projectRoot, filename)] : acc,
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const eslint = new ESLint(eslintConfig);
|
||||||
|
const results = await eslint.lintFiles(updated);
|
||||||
|
const formatter = await eslint.loadFormatter("stylish");
|
||||||
|
const resultText = formatter.format(results);
|
||||||
|
const errors = results.reduce((acc, result) => acc + result.errorCount, 0);
|
||||||
|
|
||||||
|
console.log(resultText);
|
||||||
|
process.exit(errors > 1 ? 1 : 0);
|
@ -4,16 +4,12 @@ import pseudolocale from "pseudolocale";
|
|||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
import { makeFormatter } from "@lit/localize-tools/lib/formatters/index.js";
|
import { makeFormatter } from "@lit/localize-tools/lib/formatters/index.js";
|
||||||
import type { Message, ProgramMessage } from "@lit/localize-tools/lib/messages.d.ts";
|
|
||||||
import { sortProgramMessages } from "@lit/localize-tools/lib/messages.js";
|
import { sortProgramMessages } from "@lit/localize-tools/lib/messages.js";
|
||||||
import { TransformLitLocalizer } from "@lit/localize-tools/lib/modes/transform.js";
|
import { TransformLitLocalizer } from "@lit/localize-tools/lib/modes/transform.js";
|
||||||
import type { Config } from "@lit/localize-tools/lib/types/config.d.ts";
|
|
||||||
import type { Locale } from "@lit/localize-tools/lib/types/locale.d.ts";
|
|
||||||
import type { TransformOutputConfig } from "@lit/localize-tools/lib/types/modes.d.ts";
|
|
||||||
|
|
||||||
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
||||||
const pseudoLocale: Locale = "pseudo-LOCALE" as Locale;
|
const pseudoLocale = "pseudo-LOCALE";
|
||||||
const targetLocales: Locale[] = [pseudoLocale];
|
const targetLocales = [pseudoLocale];
|
||||||
const baseConfig = JSON.parse(readFileSync(path.join(__dirname, "../lit-localize.json"), "utf-8"));
|
const baseConfig = JSON.parse(readFileSync(path.join(__dirname, "../lit-localize.json"), "utf-8"));
|
||||||
|
|
||||||
// Need to make some internal specifications to satisfy the transformer. It doesn't actually matter
|
// Need to make some internal specifications to satisfy the transformer. It doesn't actually matter
|
||||||
@ -21,27 +17,28 @@ const baseConfig = JSON.parse(readFileSync(path.join(__dirname, "../lit-localize
|
|||||||
// is in their common parent class, but I had to pick one. Everything else here is just pure
|
// is in their common parent class, but I had to pick one. Everything else here is just pure
|
||||||
// exploitation of the lit/localize-tools internals.
|
// exploitation of the lit/localize-tools internals.
|
||||||
|
|
||||||
const config: Config = {
|
const config = {
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
baseDir: path.join(__dirname, ".."),
|
baseDir: path.join(__dirname, ".."),
|
||||||
targetLocales,
|
targetLocales,
|
||||||
output: {
|
output: {
|
||||||
...baseConfig,
|
...baseConfig.output,
|
||||||
mode: "transform",
|
mode: "transform",
|
||||||
},
|
},
|
||||||
resolve: (path: string) => path,
|
resolve: (path) => path,
|
||||||
} as Config;
|
};
|
||||||
|
|
||||||
const pseudoMessagify = (message: ProgramMessage) => ({
|
const pseudoMessagify = (message) => ({
|
||||||
name: message.name,
|
name: message.name,
|
||||||
contents: message.contents.map((content) =>
|
contents: message.contents.map((content) =>
|
||||||
typeof content === "string" ? pseudolocale(content, { prepend: "", append: "" }) : content,
|
typeof content === "string" ? pseudolocale(content, { prepend: "", append: "" }) : content,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
const localizer = new TransformLitLocalizer(config as Config & { output: TransformOutputConfig });
|
const localizer = new TransformLitLocalizer(config);
|
||||||
const { messages } = localizer.extractSourceMessages();
|
const messages = localizer.extractSourceMessages().messages;
|
||||||
const translations = messages.map(pseudoMessagify);
|
const translations = messages.map(pseudoMessagify);
|
||||||
const sorted = sortProgramMessages([...messages]);
|
const sorted = sortProgramMessages([...messages]);
|
||||||
const formatter = makeFormatter(config);
|
const formatter = makeFormatter(config);
|
||||||
formatter.writeOutput(sorted, new Map<Locale, Message[]>([[pseudoLocale, translations]]));
|
|
||||||
|
formatter.writeOutput(sorted, new Map([[pseudoLocale, translations]]));
|
@ -93,12 +93,10 @@ export class ApplicationListPage extends TablePage<Application> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderSidebarAfter(): TemplateResult {
|
renderSidebarAfter(): TemplateResult {
|
||||||
// Rendering the wizard with .open here, as if we set the attribute in
|
|
||||||
// renderObjectCreate() it'll open two wizards, since that function gets called twice
|
|
||||||
return html`<div class="pf-c-sidebar__panel pf-m-width-25">
|
return html`<div class="pf-c-sidebar__panel pf-m-width-25">
|
||||||
<div class="pf-c-card">
|
<div class="pf-c-card">
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
<ak-markdown .md=${MDApplication}></ak-markdown>
|
<ak-markdown .md=${MDApplication} meta="applications/index.md"></ak-markdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
@ -364,6 +364,8 @@ export class OAuth2ProviderViewPage extends AKElement {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
.md=${MDProviderOAuth2}
|
.md=${MDProviderOAuth2}
|
||||||
|
meta="providers/oauth2/index.md"
|
||||||
|
;
|
||||||
></ak-markdown>
|
></ak-markdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -121,30 +121,37 @@ export class ProxyProviderViewPage extends AKElement {
|
|||||||
{
|
{
|
||||||
label: msg("Nginx (Ingress)"),
|
label: msg("Nginx (Ingress)"),
|
||||||
md: MDNginxIngress,
|
md: MDNginxIngress,
|
||||||
|
meta: "providers/proxy/_nginx_ingress.md",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: msg("Nginx (Proxy Manager)"),
|
label: msg("Nginx (Proxy Manager)"),
|
||||||
md: MDNginxPM,
|
md: MDNginxPM,
|
||||||
|
meta: "providers/proxy/_nginx_proxy_manager.md",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: msg("Nginx (standalone)"),
|
label: msg("Nginx (standalone)"),
|
||||||
md: MDNginxStandalone,
|
md: MDNginxStandalone,
|
||||||
|
meta: "providers/proxy/_nginx_standalone.md",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: msg("Traefik (Ingress)"),
|
label: msg("Traefik (Ingress)"),
|
||||||
md: MDTraefikIngress,
|
md: MDTraefikIngress,
|
||||||
|
meta: "providers/proxy/_traefik_ingress.md",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: msg("Traefik (Compose)"),
|
label: msg("Traefik (Compose)"),
|
||||||
md: MDTraefikCompose,
|
md: MDTraefikCompose,
|
||||||
|
meta: "providers/proxy/_traefik_compose.md",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: msg("Traefik (Standalone)"),
|
label: msg("Traefik (Standalone)"),
|
||||||
md: MDTraefikStandalone,
|
md: MDTraefikStandalone,
|
||||||
|
meta: "providers/proxy/_traefik_standalone.md",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: msg("Caddy (Standalone)"),
|
label: msg("Caddy (Standalone)"),
|
||||||
md: MDCaddyStandalone,
|
md: MDCaddyStandalone,
|
||||||
|
meta: "providers/proxy/_caddy_standalone.md",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const replacers: Replacer[] = [
|
const replacers: Replacer[] = [
|
||||||
@ -182,7 +189,11 @@ export class ProxyProviderViewPage extends AKElement {
|
|||||||
data-tab-title="${server.label}"
|
data-tab-title="${server.label}"
|
||||||
class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile"
|
class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile"
|
||||||
>
|
>
|
||||||
<ak-markdown .replacers=${replacers} .md=${server.md}></ak-markdown>
|
<ak-markdown
|
||||||
|
.replacers=${replacers}
|
||||||
|
.md=${server.md}
|
||||||
|
meta=${server.meta}
|
||||||
|
></ak-markdown>
|
||||||
</section>`;
|
</section>`;
|
||||||
})}</ak-tabs
|
})}</ak-tabs
|
||||||
>`;
|
>`;
|
||||||
@ -248,7 +259,10 @@ export class ProxyProviderViewPage extends AKElement {
|
|||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
|
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
<ak-markdown .md=${MDHeaderAuthentication}></ak-markdown>
|
<ak-markdown
|
||||||
|
.md=${MDHeaderAuthentication}
|
||||||
|
meta="proxy/header_authentication.md"
|
||||||
|
></ak-markdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
@ -249,7 +249,10 @@ export class SCIMProviderViewPage extends AKElement {
|
|||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card pf-l-grid__item pf-m-5-col">
|
<div class="pf-c-card pf-l-grid__item pf-m-5-col">
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
<ak-markdown .md=${MDSCIMProvider}></ak-markdown>
|
<ak-markdown
|
||||||
|
.md=${MDSCIMProvider}
|
||||||
|
meta="providers/scim/index.md"
|
||||||
|
></ak-markdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
@ -4,7 +4,7 @@ import { adaptCSS } from "@goauthentik/common/utils";
|
|||||||
import { ensureCSSStyleSheet } from "@goauthentik/elements/utils/ensureCSSStyleSheet";
|
import { ensureCSSStyleSheet } from "@goauthentik/elements/utils/ensureCSSStyleSheet";
|
||||||
|
|
||||||
import { localized } from "@lit/localize";
|
import { localized } from "@lit/localize";
|
||||||
import { LitElement } from "lit";
|
import { LitElement, ReactiveElement } from "lit";
|
||||||
|
|
||||||
import AKGlobal from "@goauthentik/common/styles/authentik.css";
|
import AKGlobal from "@goauthentik/common/styles/authentik.css";
|
||||||
import ThemeDark from "@goauthentik/common/styles/theme-dark.css";
|
import ThemeDark from "@goauthentik/common/styles/theme-dark.css";
|
||||||
@ -60,6 +60,7 @@ export class AKElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected createRenderRoot(): ShadowRoot | Element {
|
protected createRenderRoot(): ShadowRoot | Element {
|
||||||
|
this.fixElementStyles();
|
||||||
const root = super.createRenderRoot() as ShadowRoot;
|
const root = super.createRenderRoot() as ShadowRoot;
|
||||||
let styleRoot: AdoptedStyleSheetsElement = root;
|
let styleRoot: AdoptedStyleSheetsElement = root;
|
||||||
if ("ShadyDOM" in window) {
|
if ("ShadyDOM" in window) {
|
||||||
@ -78,6 +79,13 @@ export class AKElement extends LitElement {
|
|||||||
return rootInterface()?.getTheme() || UiThemeEnum.Automatic;
|
return rootInterface()?.getTheme() || UiThemeEnum.Automatic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fixElementStyles() {
|
||||||
|
// Ensure all style sheets being passed are really style sheets.
|
||||||
|
(this.constructor as typeof ReactiveElement).elementStyles = (
|
||||||
|
this.constructor as typeof ReactiveElement
|
||||||
|
).elementStyles.map(ensureCSSStyleSheet);
|
||||||
|
}
|
||||||
|
|
||||||
async _initTheme(root: AdoptedStyleSheetsElement): Promise<void> {
|
async _initTheme(root: AdoptedStyleSheetsElement): Promise<void> {
|
||||||
// Early activate theme based on media query to prevent light flash
|
// Early activate theme based on media query to prevent light flash
|
||||||
// when dark is preferred
|
// when dark is preferred
|
||||||
|
@ -2,8 +2,10 @@ import { docLink } from "@goauthentik/common/global";
|
|||||||
import "@goauthentik/elements/Alert";
|
import "@goauthentik/elements/Alert";
|
||||||
import { Level } from "@goauthentik/elements/Alert";
|
import { Level } from "@goauthentik/elements/Alert";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
|
import { matter } from "md-front-matter";
|
||||||
|
import * as showdown from "showdown";
|
||||||
|
|
||||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
import { CSSResult, PropertyValues, css, html, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||||
|
|
||||||
@ -11,22 +13,28 @@ import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
|||||||
import PFList from "@patternfly/patternfly/components/List/list.css";
|
import PFList from "@patternfly/patternfly/components/List/list.css";
|
||||||
|
|
||||||
export interface MarkdownDocument {
|
export interface MarkdownDocument {
|
||||||
html: string;
|
|
||||||
metadata: { [key: string]: string };
|
|
||||||
filename: string;
|
|
||||||
path: string;
|
path: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Replacer = (input: string, md: MarkdownDocument) => string;
|
export type Replacer = (input: string, md: MarkdownDocument) => string;
|
||||||
|
|
||||||
|
const isRelativeLink = /href="(\.[^"]*)"/gm;
|
||||||
|
const isFile = /[^/]+\.md/;
|
||||||
|
|
||||||
@customElement("ak-markdown")
|
@customElement("ak-markdown")
|
||||||
export class Markdown extends AKElement {
|
export class Markdown extends AKElement {
|
||||||
@property({ attribute: false })
|
@property()
|
||||||
md?: MarkdownDocument;
|
md: string = "";
|
||||||
|
|
||||||
|
@property()
|
||||||
|
meta: string = "";
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
replacers: Replacer[] = [];
|
replacers: Replacer[] = [];
|
||||||
|
|
||||||
|
docHtml = "";
|
||||||
|
docTitle = "";
|
||||||
|
|
||||||
defaultReplacers: Replacer[] = [
|
defaultReplacers: Replacer[] = [
|
||||||
this.replaceAdmonitions,
|
this.replaceAdmonitions,
|
||||||
this.replaceList,
|
this.replaceList,
|
||||||
@ -45,6 +53,8 @@ export class Markdown extends AKElement {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
converter = new showdown.Converter({ metadata: true });
|
||||||
|
|
||||||
replaceAdmonitions(input: string): string {
|
replaceAdmonitions(input: string): string {
|
||||||
const admonitionStart = /:::(\w+)<br\s\/>/gm;
|
const admonitionStart = /:::(\w+)<br\s\/>/gm;
|
||||||
const admonitionEnd = /:::/gm;
|
const admonitionEnd = /:::/gm;
|
||||||
@ -62,31 +72,36 @@ export class Markdown extends AKElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
replaceRelativeLinks(input: string, md: MarkdownDocument): string {
|
replaceRelativeLinks(input: string, md: MarkdownDocument): string {
|
||||||
const relativeLink = /href=".(.*)"/gm;
|
const baseName = md.path.replace(isFile, "");
|
||||||
const cwd = process.env.CWD as string;
|
const baseUrl = docLink("");
|
||||||
// cwd will point to $root/web, but the docs are in $root/website/docs
|
const result = input.replace(isRelativeLink, (match, path) => {
|
||||||
let relPath = md.path.replace(cwd + "site", "");
|
const pathName = path.replace(".md", "");
|
||||||
if (md.filename === "index.md") {
|
const link = `docs/${baseName}${pathName}`;
|
||||||
relPath = relPath.replace("index.md", "");
|
const url = new URL(link, baseUrl).toString();
|
||||||
}
|
return `href="${url}" _target="blank"`;
|
||||||
const baseURL = docLink("");
|
});
|
||||||
const fullURL = `${baseURL}${relPath}.$1`;
|
return result;
|
||||||
return input.replace(relativeLink, `href="${fullURL}" target="_blank"`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): TemplateResult {
|
willUpdate(properties: PropertyValues<this>) {
|
||||||
if (!this.md) {
|
if (properties.has("md") || properties.has("meta")) {
|
||||||
return html``;
|
const parsedContent = matter(this.md);
|
||||||
|
const parsedHTML = this.converter.makeHtml(parsedContent.content);
|
||||||
|
const replacers = [...this.defaultReplacers, ...this.replacers];
|
||||||
|
this.docTitle = parsedContent.data["title"] ?? "";
|
||||||
|
this.docHtml = replacers.reduce(
|
||||||
|
(html, replacer) => replacer(html, { path: this.meta }),
|
||||||
|
parsedHTML,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let finalHTML = this.md.html;
|
}
|
||||||
const replacers = [...this.defaultReplacers, ...this.replacers];
|
|
||||||
replacers.forEach((r) => {
|
render() {
|
||||||
if (!this.md) {
|
if (!this.md) {
|
||||||
return;
|
return nothing;
|
||||||
}
|
}
|
||||||
finalHTML = r(finalHTML, this.md);
|
|
||||||
});
|
return html`${this.docTitle ? html`<h2>${this.docTitle}</h2>` : nothing}
|
||||||
return html`${this.md?.metadata.title ? html`<h2>${this.md.metadata.title}</h2>` : html``}
|
${unsafeHTML(this.docHtml)}`;
|
||||||
${unsafeHTML(finalHTML)}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import { CSSResult } from "lit";
|
import { CSSResult, unsafeCSS } from "lit";
|
||||||
|
|
||||||
export const ensureCSSStyleSheet = (css: CSSStyleSheet | CSSResult): CSSStyleSheet =>
|
export const ensureCSSStyleSheet = (css: string | CSSStyleSheet | CSSResult): CSSStyleSheet =>
|
||||||
css instanceof CSSResult ? css.styleSheet! : css;
|
typeof css === "string"
|
||||||
|
? (unsafeCSS(css).styleSheet as CSSStyleSheet)
|
||||||
|
: css instanceof CSSResult
|
||||||
|
? (css.styleSheet as CSSStyleSheet)
|
||||||
|
: css;
|
||||||
|
@ -7857,407 +7857,549 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s7513372fe60f6387">
|
<trans-unit id="s7513372fe60f6387">
|
||||||
<source>Event volume</source>
|
<source>Event volume</source>
|
||||||
|
<target>Ēvēńţ vōĺũmē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s047a5f0211fedc72">
|
<trans-unit id="s047a5f0211fedc72">
|
||||||
<source>Require Outpost (flow can only be executed from an outpost).</source>
|
<source>Require Outpost (flow can only be executed from an outpost).</source>
|
||||||
|
<target>Ŕēǫũĩŕē Ōũţƥōśţ (ƒĺōŵ ćàń ōńĺŷ ƀē ēxēćũţēď ƒŕōm àń ōũţƥōśţ).</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s3271da6c18c25b18">
|
<trans-unit id="s3271da6c18c25b18">
|
||||||
<source>Connection settings.</source>
|
<source>Connection settings.</source>
|
||||||
|
<target>Ćōńńēćţĩōń śēţţĩńĝś.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s2f4ca2148183d692">
|
<trans-unit id="s2f4ca2148183d692">
|
||||||
<source>Successfully updated endpoint.</source>
|
<source>Successfully updated endpoint.</source>
|
||||||
|
<target>Śũććēśśƒũĺĺŷ ũƥďàţēď ēńďƥōĩńţ.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s5adee855dbe191d9">
|
<trans-unit id="s5adee855dbe191d9">
|
||||||
<source>Successfully created endpoint.</source>
|
<source>Successfully created endpoint.</source>
|
||||||
|
<target>Śũććēśśƒũĺĺŷ ćŕēàţēď ēńďƥōĩńţ.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s61e136c0658e27d5">
|
<trans-unit id="s61e136c0658e27d5">
|
||||||
<source>Protocol</source>
|
<source>Protocol</source>
|
||||||
|
<target>Ƥŕōţōćōĺ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sa062b019ff0c8809">
|
<trans-unit id="sa062b019ff0c8809">
|
||||||
<source>RDP</source>
|
<source>RDP</source>
|
||||||
|
<target>ŔĎƤ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s97f9bf19fa5b57d1">
|
<trans-unit id="s97f9bf19fa5b57d1">
|
||||||
<source>SSH</source>
|
<source>SSH</source>
|
||||||
|
<target>ŚŚĤ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s7c100119e9ffcc32">
|
<trans-unit id="s7c100119e9ffcc32">
|
||||||
<source>VNC</source>
|
<source>VNC</source>
|
||||||
|
<target>VŃĆ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s6b05f9d8801fc14f">
|
<trans-unit id="s6b05f9d8801fc14f">
|
||||||
<source>Host</source>
|
<source>Host</source>
|
||||||
|
<target>Ĥōśţ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb474f652a2c2fc76">
|
<trans-unit id="sb474f652a2c2fc76">
|
||||||
<source>Hostname/IP to connect to.</source>
|
<source>Hostname/IP to connect to.</source>
|
||||||
|
<target>Ĥōśţńàmē/ĨƤ ţō ćōńńēćţ ţō.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s8276649077e8715c">
|
<trans-unit id="s8276649077e8715c">
|
||||||
<source>Endpoint(s)</source>
|
<source>Endpoint(s)</source>
|
||||||
|
<target>Ēńďƥōĩńţ(ś)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf1dabfe0fe8a75ad">
|
<trans-unit id="sf1dabfe0fe8a75ad">
|
||||||
<source>Update Endpoint</source>
|
<source>Update Endpoint</source>
|
||||||
|
<target>Ũƥďàţē Ēńďƥōĩńţ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s008496c7716b9812">
|
<trans-unit id="s008496c7716b9812">
|
||||||
<source>These bindings control which users will have access to this endpoint. Users must also have access to the application.</source>
|
<source>These bindings control which users will have access to this endpoint. Users must also have access to the application.</source>
|
||||||
|
<target>Ţĥēśē ƀĩńďĩńĝś ćōńţŕōĺ ŵĥĩćĥ ũśēŕś ŵĩĺĺ ĥàvē àććēśś ţō ţĥĩś ēńďƥōĩńţ. Ũśēŕś mũśţ àĺśō ĥàvē àććēśś ţō ţĥē àƥƥĺĩćàţĩōń.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s38e7cd1a24e70faa">
|
<trans-unit id="s38e7cd1a24e70faa">
|
||||||
<source>Create Endpoint</source>
|
<source>Create Endpoint</source>
|
||||||
|
<target>Ćŕēàţē Ēńďƥōĩńţ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s4770c10e5b1c028c">
|
<trans-unit id="s4770c10e5b1c028c">
|
||||||
<source>RAC is in preview.</source>
|
<source>RAC is in preview.</source>
|
||||||
|
<target>ŔÀĆ ĩś ĩń ƥŕēvĩēŵ.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s168565f5ac74a89f">
|
<trans-unit id="s168565f5ac74a89f">
|
||||||
<source>Update RAC Provider</source>
|
<source>Update RAC Provider</source>
|
||||||
|
<target>Ũƥďàţē ŔÀĆ Ƥŕōvĩďēŕ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s8465a2caa2d9ea5d">
|
<trans-unit id="s8465a2caa2d9ea5d">
|
||||||
<source>Endpoints</source>
|
<source>Endpoints</source>
|
||||||
|
<target>Ēńďƥōĩńţś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s9857d883d8eb98fc">
|
<trans-unit id="s9857d883d8eb98fc">
|
||||||
<source>General settings</source>
|
<source>General settings</source>
|
||||||
|
<target>Ĝēńēŕàĺ śēţţĩńĝś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sd2066881798a1b96">
|
<trans-unit id="sd2066881798a1b96">
|
||||||
<source>RDP settings</source>
|
<source>RDP settings</source>
|
||||||
|
<target>ŔĎƤ śēţţĩńĝś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb864dc36a463a155">
|
<trans-unit id="sb864dc36a463a155">
|
||||||
<source>Ignore server certificate</source>
|
<source>Ignore server certificate</source>
|
||||||
|
<target>Ĩĝńōŕē śēŕvēŕ ćēŕţĩƒĩćàţē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s20366a8d1eaaca54">
|
<trans-unit id="s20366a8d1eaaca54">
|
||||||
<source>Enable wallpaper</source>
|
<source>Enable wallpaper</source>
|
||||||
|
<target>Ēńàƀĺē ŵàĺĺƥàƥēŕ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s1e44c5350ef7598c">
|
<trans-unit id="s1e44c5350ef7598c">
|
||||||
<source>Enable font-smoothing</source>
|
<source>Enable font-smoothing</source>
|
||||||
|
<target>Ēńàƀĺē ƒōńţ-śmōōţĥĩńĝ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s04ff5d6ae711e6d6">
|
<trans-unit id="s04ff5d6ae711e6d6">
|
||||||
<source>Enable full window dragging</source>
|
<source>Enable full window dragging</source>
|
||||||
|
<target>Ēńàƀĺē ƒũĺĺ ŵĩńďōŵ ďŕàĝĝĩńĝ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s663ccbfdf27e8dd0">
|
<trans-unit id="s663ccbfdf27e8dd0">
|
||||||
<source>Network binding</source>
|
<source>Network binding</source>
|
||||||
|
<target>Ńēţŵōŕķ ƀĩńďĩńĝ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb108a06693c67753">
|
<trans-unit id="sb108a06693c67753">
|
||||||
<source>No binding</source>
|
<source>No binding</source>
|
||||||
|
<target>Ńō ƀĩńďĩńĝ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s5aab90c74f1233b8">
|
<trans-unit id="s5aab90c74f1233b8">
|
||||||
<source>Bind ASN</source>
|
<source>Bind ASN</source>
|
||||||
|
<target>ßĩńď ÀŚŃ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s488303b048afe83b">
|
<trans-unit id="s488303b048afe83b">
|
||||||
<source>Bind ASN and Network</source>
|
<source>Bind ASN and Network</source>
|
||||||
|
<target>ßĩńď ÀŚŃ àńď Ńēţŵōŕķ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s3268dcfe0c8234dc">
|
<trans-unit id="s3268dcfe0c8234dc">
|
||||||
<source>Bind ASN, Network and IP</source>
|
<source>Bind ASN, Network and IP</source>
|
||||||
|
<target>ßĩńď ÀŚŃ, Ńēţŵōŕķ àńď ĨƤ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s226381aca231644f">
|
<trans-unit id="s226381aca231644f">
|
||||||
<source>Configure if sessions created by this stage should be bound to the Networks they were created in.</source>
|
<source>Configure if sessions created by this stage should be bound to the Networks they were created in.</source>
|
||||||
|
<target>Ćōńƒĩĝũŕē ĩƒ śēśśĩōńś ćŕēàţēď ƀŷ ţĥĩś śţàĝē śĥōũĺď ƀē ƀōũńď ţō ţĥē Ńēţŵōŕķś ţĥēŷ ŵēŕē ćŕēàţēď ĩń.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s2555a1f20f3fd93e">
|
<trans-unit id="s2555a1f20f3fd93e">
|
||||||
<source>GeoIP binding</source>
|
<source>GeoIP binding</source>
|
||||||
|
<target>ĜēōĨƤ ƀĩńďĩńĝ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s3d63c78f93c9a92e">
|
<trans-unit id="s3d63c78f93c9a92e">
|
||||||
<source>Bind Continent</source>
|
<source>Bind Continent</source>
|
||||||
|
<target>ßĩńď Ćōńţĩńēńţ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s395d5863b3a259b5">
|
<trans-unit id="s395d5863b3a259b5">
|
||||||
<source>Bind Continent and Country</source>
|
<source>Bind Continent and Country</source>
|
||||||
|
<target>ßĩńď Ćōńţĩńēńţ àńď Ćōũńţŕŷ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s625ea0c32b4b136c">
|
<trans-unit id="s625ea0c32b4b136c">
|
||||||
<source>Bind Continent, Country and City</source>
|
<source>Bind Continent, Country and City</source>
|
||||||
|
<target>ßĩńď Ćōńţĩńēńţ, Ćōũńţŕŷ àńď Ćĩţŷ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s4bc7a1a88961be90">
|
<trans-unit id="s4bc7a1a88961be90">
|
||||||
<source>Configure if sessions created by this stage should be bound to their GeoIP-based location</source>
|
<source>Configure if sessions created by this stage should be bound to their GeoIP-based location</source>
|
||||||
|
<target>Ćōńƒĩĝũŕē ĩƒ śēśśĩōńś ćŕēàţēď ƀŷ ţĥĩś śţàĝē śĥōũĺď ƀē ƀōũńď ţō ţĥēĩŕ ĜēōĨƤ-ƀàśēď ĺōćàţĩōń</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sa06cd519ff151b6d">
|
<trans-unit id="sa06cd519ff151b6d">
|
||||||
<source>RAC</source>
|
<source>RAC</source>
|
||||||
|
<target>ŔÀĆ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s28b99b59541f54ca">
|
<trans-unit id="s28b99b59541f54ca">
|
||||||
<source>Connection failed after <x id="0" equiv-text="${this.connectionAttempt}"/> attempts.</source>
|
<source>Connection failed after <x id="0" equiv-text="${this.connectionAttempt}"/> attempts.</source>
|
||||||
|
<target>Ćōńńēćţĩōń ƒàĩĺēď àƒţēŕ <x id="0" equiv-text="${this.connectionAttempt}"/> àţţēmƥţś.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s7c7d956418e1c8c8">
|
<trans-unit id="s7c7d956418e1c8c8">
|
||||||
<source>Re-connecting in <x id="0" equiv-text="${Math.max(1, delay / 1000)}"/> second(s).</source>
|
<source>Re-connecting in <x id="0" equiv-text="${Math.max(1, delay / 1000)}"/> second(s).</source>
|
||||||
|
<target>Ŕē-ćōńńēćţĩńĝ ĩń <x id="0" equiv-text="${Math.max(1, delay / 1000)}"/> śēćōńď(ś).</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sfc003381f593d943">
|
<trans-unit id="sfc003381f593d943">
|
||||||
<source>Connecting...</source>
|
<source>Connecting...</source>
|
||||||
|
<target>Ćōńńēćţĩńĝ...</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s31aa94a0b3c7edb2">
|
<trans-unit id="s31aa94a0b3c7edb2">
|
||||||
<source>Select endpoint to connect to</source>
|
<source>Select endpoint to connect to</source>
|
||||||
|
<target>Śēĺēćţ ēńďƥōĩńţ ţō ćōńńēćţ ţō</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sa2ea0fcd3ffa80e0">
|
<trans-unit id="sa2ea0fcd3ffa80e0">
|
||||||
<source>Connection expiry</source>
|
<source>Connection expiry</source>
|
||||||
|
<target>Ćōńńēćţĩōń ēxƥĩŕŷ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s6dd297c217729828">
|
<trans-unit id="s6dd297c217729828">
|
||||||
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
|
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
|
||||||
|
<target>Ďēţēŕmĩńēś ĥōŵ ĺōńĝ à śēśśĩōń ĺàśţś ƀēƒōŕē ƀēĩńĝ ďĩśćōńńēćţēď àńď ŕēǫũĩŕĩńĝ ŕē-àũţĥōŕĩźàţĩōń.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s31f1afc1bfe1cb3a">
|
<trans-unit id="s31f1afc1bfe1cb3a">
|
||||||
<source>Learn more</source>
|
<source>Learn more</source>
|
||||||
|
<target>Ĺēàŕń mōŕē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc39f6abf0daedb0f">
|
<trans-unit id="sc39f6abf0daedb0f">
|
||||||
<source>Maximum concurrent connections</source>
|
<source>Maximum concurrent connections</source>
|
||||||
|
<target>Màxĩmũm ćōńćũŕŕēńţ ćōńńēćţĩōńś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s62418cbcd2a25498">
|
<trans-unit id="s62418cbcd2a25498">
|
||||||
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
|
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
|
||||||
|
<target>Màxĩmũm ćōńćũŕŕēńţ àĺĺōŵēď ćōńńēćţĩōńś ţō ţĥĩś ēńďƥōĩńţ. Ćàń ƀē śēţ ţō -1 ţō ďĩśàƀĺē ţĥē ĺĩmĩţ.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s94d61907ee22a8c1">
|
<trans-unit id="s94d61907ee22a8c1">
|
||||||
<source>Korean</source>
|
<source>Korean</source>
|
||||||
|
<target>Ķōŕēàń</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s95d56e58f816d211">
|
<trans-unit id="s95d56e58f816d211">
|
||||||
<source>Dutch</source>
|
<source>Dutch</source>
|
||||||
|
<target>Ďũţćĥ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s16a15af46bc9aeef">
|
<trans-unit id="s16a15af46bc9aeef">
|
||||||
<source>Failed to fetch objects: <x id="0" equiv-text="${this.error.detail}"/></source>
|
<source>Failed to fetch objects: <x id="0" equiv-text="${this.error.detail}"/></source>
|
||||||
|
<target>Ƒàĩĺēď ţō ƒēţćĥ ōƀĴēćţś: <x id="0" equiv-text="${this.error.detail}"/></target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s744401846fea6e76">
|
<trans-unit id="s744401846fea6e76">
|
||||||
<source>Brand</source>
|
<source>Brand</source>
|
||||||
|
<target>ßŕàńď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sab21e1f62676b56c">
|
<trans-unit id="sab21e1f62676b56c">
|
||||||
<source>Successfully updated brand.</source>
|
<source>Successfully updated brand.</source>
|
||||||
|
<target>Śũććēśśƒũĺĺŷ ũƥďàţēď ƀŕàńď.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sa43e43fd3a23e22d">
|
<trans-unit id="sa43e43fd3a23e22d">
|
||||||
<source>Successfully created brand.</source>
|
<source>Successfully created brand.</source>
|
||||||
|
<target>Śũććēśśƒũĺĺŷ ćŕēàţēď ƀŕàńď.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s41b3f9b4c98aabd9">
|
<trans-unit id="s41b3f9b4c98aabd9">
|
||||||
<source>Use this brand for each domain that doesn't have a dedicated brand.</source>
|
<source>Use this brand for each domain that doesn't have a dedicated brand.</source>
|
||||||
|
<target>Ũśē ţĥĩś ƀŕàńď ƒōŕ ēàćĥ ďōmàĩń ţĥàţ ďōēśń'ţ ĥàvē à ďēďĩćàţēď ƀŕàńď.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s17260b71484b307f">
|
<trans-unit id="s17260b71484b307f">
|
||||||
<source>Set custom attributes using YAML or JSON. Any attributes set here will be inherited by users, if the request is handled by this brand.</source>
|
<source>Set custom attributes using YAML or JSON. Any attributes set here will be inherited by users, if the request is handled by this brand.</source>
|
||||||
|
<target>Śēţ ćũśţōm àţţŕĩƀũţēś ũśĩńĝ ŶÀMĹ ōŕ ĵŚŌŃ. Àńŷ àţţŕĩƀũţēś śēţ ĥēŕē ŵĩĺĺ ƀē ĩńĥēŕĩţēď ƀŷ ũśēŕś, ĩƒ ţĥē ŕēǫũēśţ ĩś ĥàńďĺēď ƀŷ ţĥĩś ƀŕàńď.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s79fc990a2b58f27f">
|
<trans-unit id="s79fc990a2b58f27f">
|
||||||
<source>Brands</source>
|
<source>Brands</source>
|
||||||
|
<target>ßŕàńďś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s02774bc46a167346">
|
<trans-unit id="s02774bc46a167346">
|
||||||
<source>Brand(s)</source>
|
<source>Brand(s)</source>
|
||||||
|
<target>ßŕàńď(ś)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s801bf3d03f4a3ff1">
|
<trans-unit id="s801bf3d03f4a3ff1">
|
||||||
<source>Update Brand</source>
|
<source>Update Brand</source>
|
||||||
|
<target>Ũƥďàţē ßŕàńď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s5c3efec5330e0000">
|
<trans-unit id="s5c3efec5330e0000">
|
||||||
<source>Create Brand</source>
|
<source>Create Brand</source>
|
||||||
|
<target>Ćŕēàţē ßŕàńď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sa9d13ce9e83aac17">
|
<trans-unit id="sa9d13ce9e83aac17">
|
||||||
<source>To let a user directly reset a their password, configure a recovery flow on the currently active brand.</source>
|
<source>To let a user directly reset a their password, configure a recovery flow on the currently active brand.</source>
|
||||||
|
<target>Ţō ĺēţ à ũśēŕ ďĩŕēćţĺŷ ŕēśēţ à ţĥēĩŕ ƥàśśŵōŕď, ćōńƒĩĝũŕē à ŕēćōvēŕŷ ƒĺōŵ ōń ţĥē ćũŕŕēńţĺŷ àćţĩvē ƀŕàńď.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s6709b81e1ed4e39f">
|
<trans-unit id="s6709b81e1ed4e39f">
|
||||||
<source>The current brand must have a recovery flow configured to use a recovery link</source>
|
<source>The current brand must have a recovery flow configured to use a recovery link</source>
|
||||||
|
<target>Ţĥē ćũŕŕēńţ ƀŕàńď mũśţ ĥàvē à ŕēćōvēŕŷ ƒĺōŵ ćōńƒĩĝũŕēď ţō ũśē à ŕēćōvēŕŷ ĺĩńķ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s634e2fd82c397576">
|
<trans-unit id="s634e2fd82c397576">
|
||||||
<source>Successfully updated settings.</source>
|
<source>Successfully updated settings.</source>
|
||||||
|
<target>Śũććēśśƒũĺĺŷ ũƥďàţēď śēţţĩńĝś.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb8e4edaea6f1d935">
|
<trans-unit id="sb8e4edaea6f1d935">
|
||||||
<source>Avatars</source>
|
<source>Avatars</source>
|
||||||
|
<target>Àvàţàŕś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s945856050217c828">
|
<trans-unit id="s945856050217c828">
|
||||||
<source>Configure how authentik should show avatars for users. The following values can be set:</source>
|
<source>Configure how authentik should show avatars for users. The following values can be set:</source>
|
||||||
|
<target>Ćōńƒĩĝũŕē ĥōŵ àũţĥēńţĩķ śĥōũĺď śĥōŵ àvàţàŕś ƒōŕ ũśēŕś. Ţĥē ƒōĺĺōŵĩńĝ vàĺũēś ćàń ƀē śēţ:</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf4ef4c8ce713f775">
|
<trans-unit id="sf4ef4c8ce713f775">
|
||||||
<source>Disables per-user avatars and just shows a 1x1 pixel transparent picture</source>
|
<source>Disables per-user avatars and just shows a 1x1 pixel transparent picture</source>
|
||||||
|
<target>Ďĩśàƀĺēś ƥēŕ-ũśēŕ àvàţàŕś àńď Ĵũśţ śĥōŵś à 1x1 ƥĩxēĺ ţŕàńśƥàŕēńţ ƥĩćţũŕē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s5446842a7e4a963b">
|
<trans-unit id="s5446842a7e4a963b">
|
||||||
<source>Uses gravatar with the user's email address</source>
|
<source>Uses gravatar with the user's email address</source>
|
||||||
|
<target>Ũśēś ĝŕàvàţàŕ ŵĩţĥ ţĥē ũśēŕ'ś ēmàĩĺ àďďŕēśś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s35363b9e1cc2abd3">
|
<trans-unit id="s35363b9e1cc2abd3">
|
||||||
<source>Generated avatars based on the user's name</source>
|
<source>Generated avatars based on the user's name</source>
|
||||||
|
<target>Ĝēńēŕàţēď àvàţàŕś ƀàśēď ōń ţĥē ũśēŕ'ś ńàmē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s48110ca292cad513">
|
<trans-unit id="s48110ca292cad513">
|
||||||
<source>Any URL: If you want to use images hosted on another server, you can set any URL. Additionally, these placeholders can be used:</source>
|
<source>Any URL: If you want to use images hosted on another server, you can set any URL. Additionally, these placeholders can be used:</source>
|
||||||
|
<target>Àńŷ ŨŔĹ: Ĩƒ ŷōũ ŵàńţ ţō ũśē ĩmàĝēś ĥōśţēď ōń àńōţĥēŕ śēŕvēŕ, ŷōũ ćàń śēţ àńŷ ŨŔĹ. Àďďĩţĩōńàĺĺŷ, ţĥēśē ƥĺàćēĥōĺďēŕś ćàń ƀē ũśēď:</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sbe1dfda044bdc93b">
|
<trans-unit id="sbe1dfda044bdc93b">
|
||||||
<source>The user's username</source>
|
<source>The user's username</source>
|
||||||
|
<target>Ţĥē ũśēŕ'ś ũśēŕńàmē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s653f257c9c2d4dc5">
|
<trans-unit id="s653f257c9c2d4dc5">
|
||||||
<source>The email address, md5 hashed</source>
|
<source>The email address, md5 hashed</source>
|
||||||
|
<target>Ţĥē ēmàĩĺ àďďŕēśś, mď5 ĥàśĥēď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s9c9183cd80916b4f">
|
<trans-unit id="s9c9183cd80916b4f">
|
||||||
<source>The user's UPN, if set (otherwise an empty string)</source>
|
<source>The user's UPN, if set (otherwise an empty string)</source>
|
||||||
|
<target>Ţĥē ũśēŕ'ś ŨƤŃ, ĩƒ śēţ (ōţĥēŕŵĩśē àń ēmƥţŷ śţŕĩńĝ)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="h4963ed14d7e239a9">
|
<trans-unit id="h4963ed14d7e239a9">
|
||||||
<source>An attribute path like
|
<source>An attribute path like
|
||||||
<x id="0" equiv-text="<code>"/>attributes.something.avatar<x id="1" equiv-text="</code>"/>, which can be used in
|
<x id="0" equiv-text="<code>"/>attributes.something.avatar<x id="1" equiv-text="</code>"/>, which can be used in
|
||||||
combination with the file field to allow users to upload custom
|
combination with the file field to allow users to upload custom
|
||||||
avatars for themselves.</source>
|
avatars for themselves.</source>
|
||||||
|
<target>Àń àţţŕĩƀũţē ƥàţĥ ĺĩķē
|
||||||
|
<x id="0" equiv-text="<code>"/>àţţŕĩƀũţēś.śōmēţĥĩńĝ.àvàţàŕ<x id="1" equiv-text="</code>"/>, ŵĥĩćĥ ćàń ƀē ũśēď ĩń
|
||||||
|
ćōmƀĩńàţĩōń ŵĩţĥ ţĥē ƒĩĺē ƒĩēĺď ţō àĺĺōŵ ũśēŕś ţō ũƥĺōàď ćũśţōm
|
||||||
|
àvàţàŕś ƒōŕ ţĥēmśēĺvēś.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s4c80c34a67a6f1c9">
|
<trans-unit id="s4c80c34a67a6f1c9">
|
||||||
<source>Multiple values can be set, comma-separated, and authentik will fallback to the next mode when no avatar could be found.</source>
|
<source>Multiple values can be set, comma-separated, and authentik will fallback to the next mode when no avatar could be found.</source>
|
||||||
|
<target>Mũĺţĩƥĺē vàĺũēś ćàń ƀē śēţ, ćōmmà-śēƥàŕàţēď, àńď àũţĥēńţĩķ ŵĩĺĺ ƒàĺĺƀàćķ ţō ţĥē ńēxţ mōďē ŵĥēń ńō àvàţàŕ ćōũĺď ƀē ƒōũńď.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="h2fafcc3ebafea2f8">
|
<trans-unit id="h2fafcc3ebafea2f8">
|
||||||
<source>For example, setting this to <x id="0" equiv-text="<code>"/>gravatar,initials<x id="1" equiv-text="</code>"/> will
|
<source>For example, setting this to <x id="0" equiv-text="<code>"/>gravatar,initials<x id="1" equiv-text="</code>"/> will
|
||||||
attempt to get an avatar from Gravatar, and if the user has not
|
attempt to get an avatar from Gravatar, and if the user has not
|
||||||
configured on there, it will fallback to a generated avatar.</source>
|
configured on there, it will fallback to a generated avatar.</source>
|
||||||
|
<target>Ƒōŕ ēxàmƥĺē, śēţţĩńĝ ţĥĩś ţō <x id="0" equiv-text="<code>"/>ĝŕàvàţàŕ,ĩńĩţĩàĺś<x id="1" equiv-text="</code>"/> ŵĩĺĺ
|
||||||
|
àţţēmƥţ ţō ĝēţ àń àvàţàŕ ƒŕōm Ĝŕàvàţàŕ, àńď ĩƒ ţĥē ũśēŕ ĥàś ńōţ
|
||||||
|
ćōńƒĩĝũŕēď ōń ţĥēŕē, ĩţ ŵĩĺĺ ƒàĺĺƀàćķ ţō à ĝēńēŕàţēď àvàţàŕ.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s5faec5eb5faf62ac">
|
<trans-unit id="s5faec5eb5faf62ac">
|
||||||
<source>Allow users to change name</source>
|
<source>Allow users to change name</source>
|
||||||
|
<target>Àĺĺōŵ ũśēŕś ţō ćĥàńĝē ńàmē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s078ffec0257621c0">
|
<trans-unit id="s078ffec0257621c0">
|
||||||
<source>Enable the ability for users to change their name.</source>
|
<source>Enable the ability for users to change their name.</source>
|
||||||
|
<target>Ēńàƀĺē ţĥē àƀĩĺĩţŷ ƒōŕ ũśēŕś ţō ćĥàńĝē ţĥēĩŕ ńàmē.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s456d88f3679190fd">
|
<trans-unit id="s456d88f3679190fd">
|
||||||
<source>Allow users to change email</source>
|
<source>Allow users to change email</source>
|
||||||
|
<target>Àĺĺōŵ ũśēŕś ţō ćĥàńĝē ēmàĩĺ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s5fc6c14d106f40d3">
|
<trans-unit id="s5fc6c14d106f40d3">
|
||||||
<source>Enable the ability for users to change their email.</source>
|
<source>Enable the ability for users to change their email.</source>
|
||||||
|
<target>Ēńàƀĺē ţĥē àƀĩĺĩţŷ ƒōŕ ũśēŕś ţō ćĥàńĝē ţĥēĩŕ ēmàĩĺ.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s628e414bb2367057">
|
<trans-unit id="s628e414bb2367057">
|
||||||
<source>Allow users to change username</source>
|
<source>Allow users to change username</source>
|
||||||
|
<target>Àĺĺōŵ ũśēŕś ţō ćĥàńĝē ũśēŕńàmē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s6d816a95ca43a99d">
|
<trans-unit id="s6d816a95ca43a99d">
|
||||||
<source>Enable the ability for users to change their username.</source>
|
<source>Enable the ability for users to change their username.</source>
|
||||||
|
<target>Ēńàƀĺē ţĥē àƀĩĺĩţŷ ƒōŕ ũśēŕś ţō ćĥàńĝē ţĥēĩŕ ũśēŕńàmē.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s57b52b60ed5e2bc7">
|
<trans-unit id="s57b52b60ed5e2bc7">
|
||||||
<source>Footer links</source>
|
<source>Footer links</source>
|
||||||
|
<target>Ƒōōţēŕ ĺĩńķś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s166b59f3cc5d8ec3">
|
<trans-unit id="s166b59f3cc5d8ec3">
|
||||||
<source>GDPR compliance</source>
|
<source>GDPR compliance</source>
|
||||||
|
<target>ĜĎƤŔ ćōmƥĺĩàńćē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb8b23770f899e5bb">
|
<trans-unit id="sb8b23770f899e5bb">
|
||||||
<source>When enabled, all the events caused by a user will be deleted upon the user's deletion.</source>
|
<source>When enabled, all the events caused by a user will be deleted upon the user's deletion.</source>
|
||||||
|
<target>Ŵĥēń ēńàƀĺēď, àĺĺ ţĥē ēvēńţś ćàũśēď ƀŷ à ũśēŕ ŵĩĺĺ ƀē ďēĺēţēď ũƥōń ţĥē ũśēŕ'ś ďēĺēţĩōń.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s29501761df0fe837">
|
<trans-unit id="s29501761df0fe837">
|
||||||
<source>Impersonation</source>
|
<source>Impersonation</source>
|
||||||
|
<target>Ĩmƥēŕśōńàţĩōń</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s8f503553d8432487">
|
<trans-unit id="s8f503553d8432487">
|
||||||
<source>Globally enable/disable impersonation.</source>
|
<source>Globally enable/disable impersonation.</source>
|
||||||
|
<target>Ĝĺōƀàĺĺŷ ēńàƀĺē/ďĩśàƀĺē ĩmƥēŕśōńàţĩōń.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="see1eb81c1f734079">
|
<trans-unit id="see1eb81c1f734079">
|
||||||
<source>System settings</source>
|
<source>System settings</source>
|
||||||
|
<target>Śŷśţēm śēţţĩńĝś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s47fb5504f693775b">
|
<trans-unit id="s47fb5504f693775b">
|
||||||
<source>Changes made:</source>
|
<source>Changes made:</source>
|
||||||
|
<target>Ćĥàńĝēś màďē:</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s506b6a19d12f414c">
|
<trans-unit id="s506b6a19d12f414c">
|
||||||
<source>Key</source>
|
<source>Key</source>
|
||||||
|
<target>Ķēŷ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s4fc9dc73245eab09">
|
<trans-unit id="s4fc9dc73245eab09">
|
||||||
<source>Previous value</source>
|
<source>Previous value</source>
|
||||||
|
<target>Ƥŕēvĩōũś vàĺũē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="scb3b0671d7b7f640">
|
<trans-unit id="scb3b0671d7b7f640">
|
||||||
<source>New value</source>
|
<source>New value</source>
|
||||||
|
<target>Ńēŵ vàĺũē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s4a642406b0745917">
|
<trans-unit id="s4a642406b0745917">
|
||||||
<source>Raw event info</source>
|
<source>Raw event info</source>
|
||||||
|
<target>Ŕàŵ ēvēńţ ĩńƒō</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sa65e7bc7ddd3484d">
|
<trans-unit id="sa65e7bc7ddd3484d">
|
||||||
<source>Anonymous user</source>
|
<source>Anonymous user</source>
|
||||||
|
<target>Àńōńŷmōũś ũśēŕ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s0db494ff61b48438">
|
<trans-unit id="s0db494ff61b48438">
|
||||||
<source>Add All Available</source>
|
<source>Add All Available</source>
|
||||||
|
<target>Àďď Àĺĺ Àvàĩĺàƀĺē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s2f68faa5b84ee9fd">
|
<trans-unit id="s2f68faa5b84ee9fd">
|
||||||
<source>Remove All Available</source>
|
<source>Remove All Available</source>
|
||||||
|
<target>Ŕēmōvē Àĺĺ Àvàĩĺàƀĺē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s0ba85594344a5c02">
|
<trans-unit id="s0ba85594344a5c02">
|
||||||
<source>Remove All</source>
|
<source>Remove All</source>
|
||||||
|
<target>Ŕēmōvē Àĺĺ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s1efa17e3407e0f56">
|
<trans-unit id="s1efa17e3407e0f56">
|
||||||
<source>Available options</source>
|
<source>Available options</source>
|
||||||
|
<target>Àvàĩĺàƀĺē ōƥţĩōńś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s886db4d476f66522">
|
<trans-unit id="s886db4d476f66522">
|
||||||
<source>Selected options</source>
|
<source>Selected options</source>
|
||||||
|
<target>Śēĺēćţēď ōƥţĩōńś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sd429eba21dd59f44">
|
<trans-unit id="sd429eba21dd59f44">
|
||||||
<source><x id="0" equiv-text="${availableCount}"/> item(s) marked to add.</source>
|
<source><x id="0" equiv-text="${availableCount}"/> item(s) marked to add.</source>
|
||||||
|
<target><x id="0" equiv-text="${availableCount}"/> ĩţēm(ś) màŕķēď ţō àďď.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s4abfe4e0d0665fd1">
|
<trans-unit id="s4abfe4e0d0665fd1">
|
||||||
<source><x id="0" equiv-text="${selectedTotal}"/> item(s) selected.</source>
|
<source><x id="0" equiv-text="${selectedTotal}"/> item(s) selected.</source>
|
||||||
|
<target><x id="0" equiv-text="${selectedTotal}"/> ĩţēm(ś) śēĺēćţēď.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s56b3d54118c34b2f">
|
<trans-unit id="s56b3d54118c34b2f">
|
||||||
<source><x id="0" equiv-text="${selectedCount}"/> item(s) marked to remove.</source>
|
<source><x id="0" equiv-text="${selectedCount}"/> item(s) marked to remove.</source>
|
||||||
|
<target><x id="0" equiv-text="${selectedCount}"/> ĩţēm(ś) màŕķēď ţō ŕēmōvē.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="scb76a63c68b0d81f">
|
<trans-unit id="scb76a63c68b0d81f">
|
||||||
<source>Available Applications</source>
|
<source>Available Applications</source>
|
||||||
|
<target>Àvàĩĺàƀĺē Àƥƥĺĩćàţĩōńś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sd176021da2ea0fe3">
|
<trans-unit id="sd176021da2ea0fe3">
|
||||||
<source>Selected Applications</source>
|
<source>Selected Applications</source>
|
||||||
|
<target>Śēĺēćţēď Àƥƥĺĩćàţĩōńś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s862505f29064fc72">
|
<trans-unit id="s862505f29064fc72">
|
||||||
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
<source>This option configures the footer links on the flow executor pages. It must be a valid YAML or JSON list and can be used as follows:</source>
|
||||||
|
<target>Ţĥĩś ōƥţĩōń ćōńƒĩĝũŕēś ţĥē ƒōōţēŕ ĺĩńķś ōń ţĥē ƒĺōŵ ēxēćũţōŕ ƥàĝēś. Ĩţ mũśţ ƀē à vàĺĩď ŶÀMĹ ōŕ ĵŚŌŃ ĺĩśţ àńď ćàń ƀē ũśēď àś ƒōĺĺōŵś:</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb2275335377069aa">
|
<trans-unit id="sb2275335377069aa">
|
||||||
<source>This feature requires an enterprise license.</source>
|
<source>This feature requires an enterprise license.</source>
|
||||||
|
<target>Ţĥĩś ƒēàţũŕē ŕēǫũĩŕēś àń ēńţēŕƥŕĩśē ĺĩćēńśē.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s6d3f81dc4bcacbda">
|
<trans-unit id="s6d3f81dc4bcacbda">
|
||||||
<source>Last used</source>
|
<source>Last used</source>
|
||||||
|
<target>Ĺàśţ ũśēď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s96b2703420bfbd0c">
|
<trans-unit id="s96b2703420bfbd0c">
|
||||||
<source>OAuth Access Tokens</source>
|
<source>OAuth Access Tokens</source>
|
||||||
|
<target>ŌÀũţĥ Àććēśś Ţōķēńś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sd99e668c54f78f6e">
|
<trans-unit id="sd99e668c54f78f6e">
|
||||||
<source>Credentials / Tokens</source>
|
<source>Credentials / Tokens</source>
|
||||||
|
<target>Ćŕēďēńţĩàĺś / Ţōķēńś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="saada50e84c7c646d">
|
<trans-unit id="saada50e84c7c646d">
|
||||||
<source>Permissions set on users which affect this object.</source>
|
<source>Permissions set on users which affect this object.</source>
|
||||||
|
<target>Ƥēŕmĩśśĩōńś śēţ ōń ũśēŕś ŵĥĩćĥ àƒƒēćţ ţĥĩś ōƀĴēćţ.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s37d1b74df205b528">
|
<trans-unit id="s37d1b74df205b528">
|
||||||
<source>Permissions set on roles which affect this object.</source>
|
<source>Permissions set on roles which affect this object.</source>
|
||||||
|
<target>Ƥēŕmĩśśĩōńś śēţ ōń ŕōĺēś ŵĥĩćĥ àƒƒēćţ ţĥĩś ōƀĴēćţ.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc443bfff8a5dd106">
|
<trans-unit id="sc443bfff8a5dd106">
|
||||||
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
|
<source>Permissions assigned to this user which affect all object instances of a given type.</source>
|
||||||
|
<target>Ƥēŕmĩśśĩōńś àśśĩĝńēď ţō ţĥĩś ũśēŕ ŵĥĩćĥ àƒƒēćţ àĺĺ ōƀĴēćţ ĩńśţàńćēś ōƒ à ĝĩvēń ţŷƥē.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s3eba09a350b7d6c9">
|
<trans-unit id="s3eba09a350b7d6c9">
|
||||||
<source>Permissions assigned to this user affecting specific object instances.</source>
|
<source>Permissions assigned to this user affecting specific object instances.</source>
|
||||||
|
<target>Ƥēŕmĩśśĩōńś àśśĩĝńēď ţō ţĥĩś ũśēŕ àƒƒēćţĩńĝ śƥēćĩƒĩć ōƀĴēćţ ĩńśţàńćēś.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s17032e57ba222d2f">
|
<trans-unit id="s17032e57ba222d2f">
|
||||||
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
|
<source>Permissions assigned to this role which affect all object instances of a given type.</source>
|
||||||
|
<target>Ƥēŕmĩśśĩōńś àśśĩĝńēď ţō ţĥĩś ŕōĺē ŵĥĩćĥ àƒƒēćţ àĺĺ ōƀĴēćţ ĩńśţàńćēś ōƒ à ĝĩvēń ţŷƥē.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb9b51124d1b3dca0">
|
<trans-unit id="sb9b51124d1b3dca0">
|
||||||
<source>JWT payload</source>
|
<source>JWT payload</source>
|
||||||
|
<target>ĵŴŢ ƥàŷĺōàď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sbd065743a0c599e3">
|
<trans-unit id="sbd065743a0c599e3">
|
||||||
<source>Preview for user</source>
|
<source>Preview for user</source>
|
||||||
|
<target>Ƥŕēvĩēŵ ƒōŕ ũśēŕ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="se16b4d412ad1aba9">
|
<trans-unit id="se16b4d412ad1aba9">
|
||||||
<source>Brand name</source>
|
<source>Brand name</source>
|
||||||
|
<target>ßŕàńď ńàmē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb7e68dcad68a638c">
|
<trans-unit id="sb7e68dcad68a638c">
|
||||||
<source>Remote Access Provider</source>
|
<source>Remote Access Provider</source>
|
||||||
|
<target>Ŕēmōţē Àććēśś Ƥŕōvĩďēŕ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sfb1980a471b7dfb6">
|
<trans-unit id="sfb1980a471b7dfb6">
|
||||||
<source>Remotely access computers/servers via RDP/SSH/VNC</source>
|
<source>Remotely access computers/servers via RDP/SSH/VNC</source>
|
||||||
|
<target>Ŕēmōţēĺŷ àććēśś ćōmƥũţēŕś/śēŕvēŕś vĩà ŔĎƤ/ŚŚĤ/VŃĆ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s9ffdea131dddb3c5">
|
<trans-unit id="s9ffdea131dddb3c5">
|
||||||
<source>Configure Remote Access Provider Provider</source>
|
<source>Configure Remote Access Provider Provider</source>
|
||||||
|
<target>Ćōńƒĩĝũŕē Ŕēmōţē Àććēśś Ƥŕōvĩďēŕ Ƥŕōvĩďēŕ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s56bc67239b9255a2">
|
<trans-unit id="s56bc67239b9255a2">
|
||||||
<source>Delete authorization on disconnect</source>
|
<source>Delete authorization on disconnect</source>
|
||||||
|
<target>Ďēĺēţē àũţĥōŕĩźàţĩōń ōń ďĩśćōńńēćţ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s3945449bd524d8a5">
|
<trans-unit id="s3945449bd524d8a5">
|
||||||
<source>When enabled, connection authorizations will be deleted when a client disconnects. This will force clients with flaky internet connections to re-authorize the endpoint.</source>
|
<source>When enabled, connection authorizations will be deleted when a client disconnects. This will force clients with flaky internet connections to re-authorize the endpoint.</source>
|
||||||
|
<target>Ŵĥēń ēńàƀĺēď, ćōńńēćţĩōń àũţĥōŕĩźàţĩōńś ŵĩĺĺ ƀē ďēĺēţēď ŵĥēń à ćĺĩēńţ ďĩśćōńńēćţś. Ţĥĩś ŵĩĺĺ ƒōŕćē ćĺĩēńţś ŵĩţĥ ƒĺàķŷ ĩńţēŕńēţ ćōńńēćţĩōńś ţō ŕē-àũţĥōŕĩźē ţĥē ēńďƥōĩńţ.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc93712b5fbdc6810">
|
<trans-unit id="sc93712b5fbdc6810">
|
||||||
<source>Connection Token(s)</source>
|
<source>Connection Token(s)</source>
|
||||||
|
<target>Ćōńńēćţĩōń Ţōķēń(ś)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s526a525cdc79bbdc">
|
<trans-unit id="s526a525cdc79bbdc">
|
||||||
<source>Endpoint</source>
|
<source>Endpoint</source>
|
||||||
|
<target>Ēńďƥōĩńţ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s5c18f85cda6e89be">
|
<trans-unit id="s5c18f85cda6e89be">
|
||||||
<source>Connections</source>
|
<source>Connections</source>
|
||||||
|
<target>Ćōńńēćţĩōńś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s269a190f0086d892">
|
<trans-unit id="s269a190f0086d892">
|
||||||
<source>Unconfigured</source>
|
<source>Unconfigured</source>
|
||||||
|
<target>Ũńćōńƒĩĝũŕēď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s9e24bec94ec23dc9">
|
<trans-unit id="s9e24bec94ec23dc9">
|
||||||
<source>This option will not be changed by this mapping.</source>
|
<source>This option will not be changed by this mapping.</source>
|
||||||
|
<target>Ţĥĩś ōƥţĩōń ŵĩĺĺ ńōţ ƀē ćĥàńĝēď ƀŷ ţĥĩś màƥƥĩńĝ.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sac428446e9e41f54">
|
<trans-unit id="sac428446e9e41f54">
|
||||||
<source>RAC Connections</source>
|
<source>RAC Connections</source>
|
||||||
|
<target>ŔÀĆ Ćōńńēćţĩōńś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s39002897db60bb28">
|
<trans-unit id="s39002897db60bb28">
|
||||||
<source>Sending Duo push notification...</source>
|
<source>Sending Duo push notification...</source>
|
||||||
|
<target>Śēńďĩńĝ Ďũō ƥũśĥ ńōţĩƒĩćàţĩōń...</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sd2b8c1caa0340ed6">
|
<trans-unit id="sd2b8c1caa0340ed6">
|
||||||
<source>Failed to authenticate</source>
|
<source>Failed to authenticate</source>
|
||||||
|
<target>Ƒàĩĺēď ţō àũţĥēńţĩćàţē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sffef1a8596bc58bb">
|
<trans-unit id="sffef1a8596bc58bb">
|
||||||
<source>Authenticating...</source>
|
<source>Authenticating...</source>
|
||||||
|
<target>Àũţĥēńţĩćàţĩńĝ...</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sd12f594a3184c056">
|
<trans-unit id="sd12f594a3184c056">
|
||||||
<source>Customization</source>
|
<source>Customization</source>
|
||||||
|
<target>Ćũśţōmĩźàţĩōń</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s363abde8a254ea5f">
|
<trans-unit id="s363abde8a254ea5f">
|
||||||
<source>Authentication failed. Please try again.</source>
|
<source>Authentication failed. Please try again.</source>
|
||||||
|
<target>Àũţĥēńţĩćàţĩōń ƒàĩĺēď. Ƥĺēàśē ţŕŷ àĝàĩń.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb0821a9e92cac5eb">
|
<trans-unit id="sb0821a9e92cac5eb">
|
||||||
<source>Failed to register. Please try again.</source>
|
<source>Failed to register. Please try again.</source>
|
||||||
|
<target>Ƒàĩĺēď ţō ŕēĝĩśţēŕ. Ƥĺēàśē ţŕŷ àĝàĩń.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s238784fc1dc672ae">
|
<trans-unit id="s238784fc1dc672ae">
|
||||||
<source>Registering...</source>
|
<source>Registering...</source>
|
||||||
|
<target>Ŕēĝĩśţēŕĩńĝ...</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s009bd1c98a9f5de2">
|
<trans-unit id="s009bd1c98a9f5de2">
|
||||||
<source>Failed to register</source>
|
<source>Failed to register</source>
|
||||||
|
<target>Ƒàĩĺēď ţō ŕēĝĩśţēŕ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb166ce92e8e807d6">
|
<trans-unit id="sb166ce92e8e807d6">
|
||||||
<source>Retry registration</source>
|
<source>Retry registration</source>
|
||||||
|
<target>Ŕēţŕŷ ŕēĝĩśţŕàţĩōń</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s6ecfc18dbfeedd76">
|
||||||
|
<source>Select one of the options below to continue.</source>
|
||||||
|
<target>Śēĺēćţ ōńē ōƒ ţĥē ōƥţĩōńś ƀēĺōŵ ţō ćōńţĩńũē.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s6ecfc18dbfeedd76">
|
<trans-unit id="s6ecfc18dbfeedd76">
|
||||||
<source>Select one of the options below to continue.</source>
|
<source>Select one of the options below to continue.</source>
|
||||||
|
Reference in New Issue
Block a user