Compare commits

..

475 Commits

Author SHA1 Message Date
9e75ee09bb fix(deps): update garronej_modules_update 2023-03-18 09:11:03 +00:00
93fdcb8739 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-03-16 13:33:01 +01:00
aca926e202 Fix link script command 2023-03-16 13:31:56 +01:00
9941027b10 Update package.json 2023-03-15 12:45:58 +01:00
9104de4290 Merge pull request #263 from mkreuzmayr/main
Fix start container script paths for windows
2023-03-15 12:45:43 +01:00
5dc692809c Fix start container script paths for windows 2023-03-15 10:07:01 +01:00
8dc1d1bd21 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-03-13 10:47:10 +01:00
fe588485a9 Update changelog 2023-03-13 10:47:02 +01:00
19ef1d7025 Bump version 2023-03-13 10:44:46 +01:00
62523a8662 Merge pull request #260 from InseeFrLab/lordvlad/issue257
Run keycloakify behind corporate proxy
2023-03-13 10:44:08 +01:00
6e97665e2e Merge branch 'main' into lordvlad/issue257 2023-03-09 18:33:18 +01:00
4988680353 Update description 2023-03-09 18:30:58 +01:00
c5de5c20c7 Bump version 2023-03-08 23:21:44 +01:00
1a0fee1aa2 Make things cleaner 2023-03-08 23:21:32 +01:00
06a44603cd Release candidate 2023-03-08 22:32:45 +01:00
e48459762e Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-03-08 22:28:46 +01:00
235ebeae97 Bump version 2023-03-08 22:25:10 +01:00
dfe909606e Deprecate useFormValidationSlice 2023-03-08 22:24:51 +01:00
6fd0c7726c Merge branch 'lordvlad/issue257' of github.com:InseeFrLab/keycloakify into lordvlad/issue257
* 'lordvlad/issue257' of github.com:InseeFrLab/keycloakify:
  Only test build on LTS node version
  Update dependency evt to ^2.4.15
  Update README.md
  Bump version
  Avoid passing unessesary realm values in the error.ftl page
2023-03-08 10:25:10 +01:00
819e045811 feat(proxy): respect XDG_CACHE_HOME if set 2023-03-08 10:24:52 +01:00
1ba780598d Relase candidate 2023-03-07 18:18:37 +01:00
aeb0cb3110 Only test build on LTS node version 2023-03-07 18:18:04 +01:00
88923838c5 Bump version 2023-03-07 17:29:30 +01:00
df9f6fd7fd Merge branch 'main' into lordvlad/issue257 2023-03-07 17:16:24 +01:00
98e46d6ac9 Update dependency evt to ^2.4.15 2023-03-07 17:07:49 +01:00
daff614fb4 Update README.md 2023-03-07 17:07:48 +01:00
5ea324c7f2 Bump version 2023-03-07 17:07:48 +01:00
23fedbf94a Avoid passing unessesary realm values in the error.ftl page 2023-03-07 17:07:45 +01:00
593d66d8d6 style: remove unused dependency 2023-03-07 16:45:17 +01:00
851dcd5bf7 Run keycloakify behind corporate proxy
Fixes #257

Use make-fetch-happen for the download step. This lib will use `PROXY`
and `HTTPS_PROXY` and `NO_PROXY` env vars out of the box.

Additionally we'll try and get proxy config from npm. Unfortunately,
the most straightforward options is to call npm config to do this, since
npm  config is not easily extracted as a lib and we don't want to
replicate the resolution mechanisms.
2023-03-07 16:43:12 +01:00
2e919681ae Update dependency evt to ^2.4.15 2023-03-07 10:14:46 +00:00
5da68cd48c Update README.md 2023-03-05 23:37:37 +01:00
27fdaeff46 Bump version 2023-02-27 11:55:39 +01:00
53c0079656 Use extract instead of subtype to ease copy paste into theme repo 2023-02-27 11:55:25 +01:00
93780b77e0 Bump version 2023-02-27 11:32:14 +01:00
b712ed0421 Avoid using tsafe utils to avoid forcing user to install tsafe 2023-02-27 11:32:00 +01:00
ee96f1b345 Bump version 2023-02-27 11:29:23 +01:00
d13464df3d Get rid of the ReactComponent type, classes based component are no longer used 2023-02-27 11:29:05 +01:00
6bde2e4d96 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-02-27 10:39:44 +01:00
0a4953c020 Bump version 2023-02-27 10:39:37 +01:00
96c488880c Abstract away Template logic 2023-02-27 10:39:22 +01:00
7e0adf3f66 Update README.md 2023-02-26 17:32:35 +01:00
09f716440a Bump version 2023-02-26 16:41:47 +01:00
2251c84171 Use the new syntax for importing as type 2023-02-26 16:37:06 +01:00
5cfe78dcd1 Update prettier config 2023-02-26 15:39:03 +01:00
6a48325132 Be more relax on the type safety to avoir headache 2023-02-26 15:37:52 +01:00
294be0a79a see prev commit 2023-02-26 15:36:52 +01:00
c94b264b44 Don't need a dir for a single file 2023-02-26 15:36:35 +01:00
7220c4e3e3 Fix deepAssign 2023-02-26 15:35:57 +01:00
5aadeba2ec fix clsx 2023-02-26 15:35:30 +01:00
0f47a5b6ba Small Template refactor 2023-02-25 20:11:55 +01:00
36f32d28f2 Stop auto updating powerhooks 2023-02-25 19:21:55 +01:00
6d69ccf229 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-02-25 19:21:17 +01:00
37073b42be Avoir introducing breaking changes for CSS only setup 2023-02-25 19:19:46 +01:00
837501c948 Refactor 2023-02-25 18:26:39 +01:00
b300966fa8 Refactor and get rid of unessesary dependencies 2023-02-25 18:11:23 +01:00
730eb06c84 fix(deps): update dependency powerhooks to ^0.26.2 2023-02-14 12:08:41 +00:00
aca8d3f4b7 fix(deps): update dependency powerhooks to ^0.26.1 2023-02-08 16:38:28 +00:00
b5b3af4659 Bump version 2023-02-07 01:32:36 +01:00
6cd231426d Import Blob from node builtins 2023-02-07 01:32:20 +01:00
0c7cd1cd75 Bump version 2023-02-07 01:21:17 +01:00
2425704ead Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-02-07 01:20:33 +01:00
4e22159206 Bump version 2023-02-07 01:20:26 +01:00
52cf1ba02c Fix tsafe related warnings 2023-02-07 01:20:12 +01:00
516e84182f fix(deps): update dependency powerhooks to ^0.26.0 2023-02-05 15:10:42 +00:00
a3a9853e18 bump version 2023-02-05 14:58:53 +01:00
08e26600fd Use keycloakify as bundler by default 2023-02-05 14:58:38 +01:00
7793c2c6ba Update package.json 2023-02-05 14:41:32 +01:00
9e826d16dd Merge pull request #241 from lordvlad/mvn-begone
Mvn begone addendum
2023-02-05 14:41:13 +01:00
80618bbd9c Merge branch 'main' into mvn-begone 2023-02-05 13:36:52 +01:00
38ad47ea75 use hand-crafted promise, pipeline does not resolve properly 2023-02-05 13:32:24 +01:00
45ed359bef fix keycloak theme source path for internal bundler 2023-02-05 13:31:34 +01:00
fcc26c3e7a now that main is a promise, we shuold catch errors 2023-02-05 13:31:03 +01:00
d4ff6b1f40 fix: bundler fix missing directory 2023-02-05 12:59:05 +01:00
557de34eea fix: bundler fix missing change 2023-02-05 12:56:01 +01:00
e034dc4d90 Merge branch 'mvn-begone' of github.com:lordvlad/keycloakify into mvn-begone
* 'mvn-begone' of github.com:lordvlad/keycloakify:
  fix(deps): update garronej_modules_update
  Update README.md
  Rollback via update
  Bump version
  keycloak test script: use env to launch bash
  fix(deps): update dependency powerhooks to ^0.22.0
  Update dependency powerhooks to ^0.21.0
  Relase candidate
  fmt
  Update README.md
  Bump version
  Update src/bin/tools/downloadAndUnzip.ts
  Bump version
  #232
  Bump version
  keycloak test script: use env to launch bash
  fix(deps): update dependency powerhooks to ^0.22.0
  Update dependency powerhooks to ^0.21.0
2023-02-05 12:35:15 +01:00
cfbd1e5e4b fix(bundler): fix type mismatch introduced in last-minute 'fixes' 2023-02-05 12:34:48 +01:00
0df661819f Bump version 2023-02-04 20:51:06 +01:00
1a9f6d10d4 Actually run the top level await 2023-02-04 20:50:53 +01:00
a787215c95 Bump version 2023-02-04 20:39:38 +01:00
64ab400af5 Temporarly restore mvn as default bundler 2023-02-04 20:38:50 +01:00
a463878bf2 Bump version 2023-02-04 20:22:45 +01:00
9f72024c61 Merge pull request #240 from InseeFrLab/mvn-begone
Mvn begone
2023-02-04 19:47:36 +01:00
243fbd4dc9 Set the artifactId name in the build option 2023-02-04 19:36:42 +01:00
4e6a290693 Make new node based bundler the default 2023-02-04 18:02:39 +01:00
ac05d529ca Minor fixes 2023-02-04 17:44:02 +01:00
b38d79004a Merge branch 'main' into mvn-begone 2023-02-03 14:42:05 +01:00
f4a547df11 introduce options to choose a bundle strategy
Pick from 'none', 'keycloakify' or 'mvn', default to 'mvn'. 'none' will
not create a jar, 'keycloakify' will create a jar file using only tools
available to native nodejs, no additional  system library required.
Choosing 'mvn' will behave as before, starting maven in a subprocess.

The bundler can be chosen in `package.json` or via `KEYCLOAKIFY_BUNDLER`
env var.

This commit also adds `KEYCLOAKIFY_GROUP_ID` and
`KEYCLOAKIFY_ARTIFACT_ID` env vars, which will be used to
define group id and artifact id in pom.xml and pom.properties, if given.
2023-02-03 14:28:06 +01:00
2b87c35058 introduce utils for creating a jar archive 2023-02-03 14:06:24 +01:00
b11833e450 fix typo 2023-02-03 13:48:32 +01:00
fa8e119514 fix(deps): update garronej_modules_update 2023-02-01 15:16:03 +00:00
677cb5c330 Update README.md 2023-01-27 15:46:30 +01:00
6e74c79bfe Merge pull request #233 from InseeFrLab/windows_compat
Windows compat
2023-01-27 15:45:31 +01:00
54474f5908 Merge branch 'main' into windows_compat 2023-01-27 15:45:25 +01:00
99cc0f519b Rollback via update 2023-01-27 01:13:16 +01:00
92a01f89ef Bump version 2023-01-27 01:12:46 +01:00
fd83a0c743 keycloak test script: use env to launch bash
This minor change allows users to use the latest version of `bash` on Mac OS.

`/bin/bash` is very old on Mac OS, and many users will have `bash` from MacPorts or Homebrew. This change allows such users to be able to use the newer `bash` installation.
2023-01-27 01:12:46 +01:00
988e46c875 fix(deps): update dependency powerhooks to ^0.22.0 2023-01-27 01:12:46 +01:00
f081c2fc20 Update dependency powerhooks to ^0.21.0 2023-01-27 01:12:46 +01:00
b4b376a1a5 Relase candidate 2023-01-27 01:09:11 +01:00
0db4179d47 fmt 2023-01-27 00:32:41 +01:00
795b7c6234 Update README.md 2023-01-27 00:27:46 +01:00
091b9a57f5 Bump version 2023-01-27 00:22:28 +01:00
564e1422ac Merge pull request #226 from lordvlad/fix-win-build
windows compatibility
2023-01-27 00:20:12 +01:00
8ed4ed3fc4 Update src/bin/tools/downloadAndUnzip.ts 2023-01-26 22:29:51 +01:00
29fe4566a7 style: remove unused variable 2023-01-26 22:20:16 +01:00
ae3bfb28ed refactor: follow suggesion to make methods read a bit more sync 2023-01-26 22:00:31 +01:00
14aab97d8a Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-01-26 10:50:01 +01:00
52d7a47cd7 Bump version 2023-01-26 10:48:57 +01:00
f338dcbeed #232 2023-01-26 10:48:24 +01:00
dcec058a22 Bump version 2023-01-23 02:57:12 +01:00
2bdc6b156b Merge pull request #231 from rome-user/patch-1
keycloak test script: use env to launch bash
2023-01-23 02:56:32 +01:00
84ca9e6b81 keycloak test script: use env to launch bash
This minor change allows users to use the latest version of `bash` on Mac OS.

`/bin/bash` is very old on Mac OS, and many users will have `bash` from MacPorts or Homebrew. This change allows such users to be able to use the newer `bash` installation.
2023-01-22 17:28:27 -08:00
11cb0fd2db Merge pull request #230 from InseeFrLab/renovate_main-garronej_modules_update
Update dependency powerhooks to ^0.22.0 (main)
2023-01-18 00:58:38 +01:00
3f620ffb6f fix(deps): update dependency powerhooks to ^0.22.0 2023-01-16 18:23:50 +00:00
1a0e05d073 rewrite download and unzip to use nodejs native methods 2023-01-16 14:42:20 +01:00
a4d2de23a1 Update dependency powerhooks to ^0.21.0 2023-01-16 02:38:07 +00:00
85cecc9811 fix(build): _properly_ fix bin paths 2023-01-12 22:56:02 +01:00
9899f742a8 fix(build): also rewrite bin paths in dist/package.json 2023-01-12 22:51:48 +01:00
b5484740b7 fix(build): remove dependency on *nix chown 2023-01-12 22:27:22 +01:00
016b15b437 Bump version 2023-01-10 09:05:33 +01:00
6fb936798e https://github.com/InseeFrLab/keycloakify/issues/217#issuecomment-1376849781 2023-01-10 09:05:19 +01:00
a692b87843 Bump version 2023-01-08 15:01:12 +01:00
19663885a4 Merge pull request #222 from InseeFrLab/pr/lordvlad/221
Pr/lordvlad/221
2023-01-08 14:59:22 +01:00
49b87777f9 Fmt 2023-01-08 14:57:18 +01:00
d4523bb1e6 Merge branch 'main' into patch-1 2023-01-08 12:22:14 +01:00
e3200899e2 fix: replace rm system calls with nodejs native functions; closes #219 2023-01-08 12:19:41 +01:00
36c7a1ab9e Bump version 2023-01-08 00:14:01 +01:00
c54fbd5eca Merge pull request #218 from pedrodsRocha1/improve-secure-login-flow
fix - handle username and password login errors the same way
2023-01-08 00:03:16 +01:00
bbe828071e fix - handle username and password login errors the same 2023-01-06 15:41:01 +01:00
23f6c7db00 Update garronej_modules_update 2022-11-29 18:31:14 +00:00
b1ea9e7a71 Update dependency powerhooks to ^0.20.27 2022-11-16 05:21:45 +00:00
fb71d0e272 Update dependency powerhooks to ^0.20.26 2022-11-13 22:11:59 +00:00
fa72a29999 Bump version 2022-11-11 15:49:05 +01:00
af77b31d54 Update default test container to Keycloak 20.0.1 2022-11-11 15:48:32 +01:00
8280dace26 Update garronej_modules_update 2022-10-25 03:18:01 +00:00
ecaf1c7b7c Bump version 2022-10-16 00:51:11 +02:00
8702ec29a8 Drop dependency to @emotion/react 2022-10-16 00:49:49 +02:00
d8206434bc Bump version 2022-10-15 15:39:56 +02:00
c71c2a8710 Fix breaking change of 6.8 2022-10-15 15:39:32 +02:00
e55b881017 Bump version 2022-10-15 14:22:46 +02:00
ab906ec417 #192: fix bad logic in ftl to js script 2022-10-15 14:22:11 +02:00
0b1ff529f7 Update garronej_modules_update 2022-10-14 17:37:32 +00:00
85a6835748 Fix CI 2022-10-13 12:10:08 +02:00
259271bc0f Update CI 2022-10-13 12:07:32 +02:00
b7bc0f178b Bump version 2022-10-13 12:03:24 +02:00
688455d0aa #191: Enable to only customize the Template 2022-10-13 11:58:31 +02:00
3c96d2ea42 Update garronej_modules_update 2022-10-13 01:16:45 +00:00
ab81481e5a Update garronej_modules_update 2022-10-11 05:10:22 +00:00
a429ad5dcf Bump version 2022-10-06 20:43:42 +02:00
5e1c5b510b Merge pull request #185 from Mstrodl/feature/mstrodl/webauthn-authenticate
Add support for `webauthn-authenticate.ftl`
2022-10-06 20:41:31 +02:00
9e63183f4b WebauthnAuthenticate: refactor authentication flow
Lots of weird syntax here, and we were using `useCallback` rather than
`useConstCallback`.
2022-10-06 14:25:04 -04:00
b1e740f026 Bump version 2022-10-06 01:09:00 +02:00
ce4ea55438 Add a big red warning when kcContext mock is enabled 2022-10-06 01:08:07 +02:00
18ab7cd22f Bump version 2022-10-06 00:37:51 +02:00
8807743daf Ignore mock when in Keycloak: https://github.com/InseeFrLab/keycloakify/discussions/186#discussioncomment-3809320 2022-10-06 00:36:46 +02:00
aad50377ff Merge pull request #187 from InseeFrLab/renovate_main-garronej_modules_update
Update dependency tss-react to ^4.3.4 (main)
2022-10-06 00:01:38 +02:00
4b3ae58ea7 Add support for webauthn-authenticate.ftl
Wow! This one sucks. Certainly more in need of review compared to
`login-username.ftl` and `login-password.ftl`...
2022-10-05 02:54:03 -04:00
ce2c68ecc9 Update dependency tss-react to ^4.3.4 2022-10-04 19:43:22 +00:00
0c155a7a2e Bump version 2022-10-04 21:42:47 +02:00
afddfe8b58 Merge pull request #184 from Mstrodl/feature/mstrodl/login-password
Add support for `login-password.ftl`
2022-10-04 21:38:55 +02:00
5fa0915271 Update changelog 2022-10-04 21:34:42 +02:00
6a0a170b17 Add support for login-password.ftl 2022-10-04 15:01:08 -04:00
4dde5b6e45 Bump version 2022-10-04 18:48:22 +02:00
4b93a1cb9e Merge pull request #183 from Mstrodl/feature/mstrodl/login-username 2022-10-04 18:43:02 +02:00
e3a0639a0c Add login-username support
Shown for the "Username Form" login step.

I would also like to work on:
- `login-password.ftl`
- `webauthn-authenticate.ftl`

But first want to see opinions on this!
2022-10-04 11:22:03 -04:00
4d3220820b Update dependency tss-react to ^4.3.3 2022-10-04 03:50:15 +00:00
a4ac9fb0f3 Update garronej_modules_update 2022-10-01 17:47:21 +00:00
1ff79ecf07 Update garronej_modules_update 2022-09-29 08:53:47 +00:00
1166b16420 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-09-27 21:35:52 +02:00
213224942f Bump version 2022-09-27 21:35:16 +02:00
ff16e66275 #177: Provide a simple way to disable fetching of default resources 2022-09-27 21:30:33 +02:00
3c338e983f Update garronej_modules_update 2022-09-14 16:33:35 +00:00
2c11ba6520 Bump version 2022-09-13 10:50:36 +02:00
9a21656706 Apply #164 on v6 2022-09-13 10:49:20 +02:00
e96ee5ba53 Bump version 2022-09-12 23:49:14 +02:00
b421633a8a Default test container use 19.0.1 2022-09-12 23:48:34 +02:00
e2e0d62560 Cleaner npm scripts 2022-09-11 01:53:56 +02:00
c71fb06940 Update garronej_modules_update 2022-09-10 20:14:00 +00:00
e2171af99c Bump version 2022-09-09 17:24:58 +02:00
8cebf049d4 Render Markdown in Terms 2022-09-09 17:24:43 +02:00
ef139ed1cc Bump version 2022-09-09 14:37:41 +02:00
d717de006a Update kcContext def 2022-09-09 14:37:27 +02:00
a44f091878 Bump version 2022-09-09 12:56:31 +02:00
1b37ba5339 Feat idp-review-user-profile.ftl #164 2022-09-09 12:56:09 +02:00
bbaa90e997 Rename misnamed default exports, removing doInsertPasswordFields 2022-09-09 10:24:50 +02:00
86e6c4a419 Bump version (changelog ignore) 2022-09-09 02:10:18 +02:00
4159883791 Feature update-user-profile.ftl #164 2022-09-09 02:07:49 +02:00
d8b00da3a1 Update tss-react 2022-09-08 15:27:41 +02:00
a24945bc1b Bump version 2022-09-08 15:18:21 +02:00
158759493f Merge pull request #170 from Tasyp/feature/silent
feat: add silent flag
2022-09-08 15:17:45 +02:00
36e32d6ddc Update src/bin/download-builtin-keycloak-theme.ts 2022-09-08 15:13:21 +02:00
84908e2ec0 Update src/bin/generate-i18n-messages.ts 2022-09-08 15:13:15 +02:00
a2dc51d811 Update src/bin/create-keycloak-email-directory.ts 2022-09-08 15:13:09 +02:00
fb3b0e2c29 fix: make sure external assets flag is a boolean 2022-09-08 15:46:27 +03:00
1a3e4c68bb test: update tests to include silent flag 2022-09-08 15:43:03 +03:00
11b2342da0 feat: add silent flag 2022-09-08 13:52:10 +03:00
80d4a808d3 Update readme 2022-09-07 13:45:34 +02:00
da4146eb59 Bump version (changelog ignore) 2022-09-07 13:40:18 +02:00
a0be35db8b Fix compat with StrictMode react 18 2022-09-07 13:39:54 +02:00
14db9cd523 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-09-07 12:02:35 +02:00
0c315385dd Update fix tests 2022-09-07 12:00:23 +02:00
c0a0eb02fb Remove Open collectivity 2022-09-07 11:59:42 +02:00
ee407c32ad Create FUNDING.yaml 2022-09-07 11:53:18 +02:00
9262d21829 Bump version 2022-09-07 11:50:41 +02:00
a13f710325 Important fix for --external-assets 2022-09-07 11:50:24 +02:00
eac1a6036f Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-09-07 11:26:18 +02:00
987f3d7586 Bump version (changelog ignore) 2022-09-07 11:26:10 +02:00
875322669c Rename isAppAndKeycloakServerSharingSameDomain to areAppAndKeycloakServerSharingSameDomain #145 2022-09-07 11:25:46 +02:00
33a264b3d0 Update README.md 2022-09-07 00:32:38 +02:00
c059eff170 Update README 2022-09-06 19:14:39 +02:00
b4a22fc9dd Fix readme 2022-09-06 19:13:46 +02:00
6d1cbdc463 Bump version 2022-09-06 19:12:59 +02:00
2bfbba4daf Upgrade tss-react 2022-09-06 17:43:30 +02:00
21ffe82bde Bump version 2022-09-06 17:40:06 +02:00
8e6f597027 Fix bug with --external-assets 2022-09-06 17:39:47 +02:00
16c5065560 Bump version 2022-09-05 00:09:07 +02:00
c4b985f1a4 Fix replacers 2022-09-05 00:08:50 +02:00
042747c7d2 Bump version 2022-09-04 23:19:53 +02:00
e4a46f31de Make it allright not to provide validators object on mock data 2022-09-04 23:19:33 +02:00
6d9e62d2b4 Remove unessesary log 2022-09-04 21:48:46 +02:00
9caaa507b1 Bump version 2022-09-01 22:35:15 +02:00
5c7d3c5b44 lib target ES2017 instead of ES2020 2022-09-01 22:35:01 +02:00
8bac57d87a Bump version 2022-09-01 21:31:41 +02:00
b8d759cd63 Minor refactor for dryness 2022-09-01 21:31:27 +02:00
da72e3e5ac Bump version (changelog ignore) 2022-09-01 21:16:39 +02:00
2afd36fee0 Avoid fetching locale twitch (react 18 useEffect) 2022-09-01 21:16:25 +02:00
b7e75d8828 Bump beta version 2022-09-01 17:22:35 +02:00
30e20f4e7d Avoid redefining the properties 2022-09-01 17:22:15 +02:00
ce0ab8dccf Better linking script 2022-09-01 16:36:38 +02:00
5b20ab2f7c Upgrade to tss-react v4 2022-09-01 15:13:24 +02:00
daaaed43df Rename keycloakify-demo-app in keycloakify-starter 2022-09-01 14:58:59 +02:00
3a4bd791ad Fix release (changelog ignore) 2022-08-28 12:37:03 +07:00
eecddd7f6b Update CI (changelog ignore) 2022-08-28 12:31:58 +07:00
a34eaa136e Update ts-ci (changelog ignore) 2022-08-28 12:16:27 +07:00
53be8b5e96 Updat README 2022-08-28 12:10:45 +07:00
f0ae5ea908 Enable the CI to run on the v6 branch 2022-08-26 16:17:03 +07:00
9910556a8b Bump version (changelog ignore) 2022-08-26 15:43:50 +07:00
5997416e1b Update i18n API JSDoc comments 2022-08-26 15:43:32 +07:00
9a9fc56f85 Improve documentation comment i18n (changelog ignore) 2022-08-23 07:58:39 +07:00
2a5e919f29 Bump version (changelog ignore) 2022-08-22 17:18:09 +07:00
8031d51e15 Rename build-keycloak-theme -> keycloakify 2022-08-22 17:17:35 +07:00
56ce9c0d0d Bump version (changelog ignore) 2022-08-20 14:56:40 +07:00
8cd584cbd5 Implementation of #160 and #103 for v6 2022-08-20 14:56:20 +07:00
f5b87f4669 Fix option parsing 2022-08-20 14:04:47 +07:00
a1a65c5529 Prettier: switch to trailing coma: none 2022-08-20 11:44:48 +07:00
832434095e Restore tsproject.json 2022-08-16 14:45:18 +07:00
b85f1ef351 Merge branch 'v6' of https://github.com/InseeFrLab/keycloakify into v6 2022-08-16 14:42:05 +07:00
8bee5d788e #145 2022-08-16 14:41:06 +07:00
0752d857e2 Merge pull request #155 from schlich/hotfix-ci 2022-08-14 11:58:07 +07:00
07e4056694 change outDir to path at root per NPM CI error message 2022-08-13 23:00:09 -05:00
0eb4ab85b3 Fix lazy loading of css chunks #141 2022-08-02 21:00:52 +02:00
69ef47daf8 Bump beta version (changelog ignore) 2022-08-01 06:04:23 +02:00
6eaa1f69ac Fix dynamic imports 2022-08-01 06:03:48 +02:00
5aab75fae0 Only downoad terms on the Terms page 2022-08-01 04:54:02 +02:00
7407c98005 Stop self promotion in log 2022-08-01 04:49:48 +02:00
dcd4322e44 Fix CI 2022-08-01 04:06:10 +02:00
81a4d46b08 Bump beta version (changelog ignore) 2022-08-01 03:57:57 +02:00
e85895ab55 User switch for dynamic import of local 2022-08-01 03:57:24 +02:00
095bdb16ba Make new build setup compatible with enable short import path 2022-08-01 03:56:53 +02:00
68de7f897d Remove ts-node 2022-08-01 03:23:49 +02:00
51b4c6b1bd Merge branch 'main' into v6 2022-08-01 03:23:16 +02:00
6d4ac977c1 update yarn.lock 2022-08-01 03:14:30 +02:00
a73fc5ebc1 Bump beta version 2022-08-01 03:07:59 +02:00
3c8461a39f update lock file 2022-08-01 03:07:20 +02:00
de76d06e48 Fix build 2022-08-01 03:07:06 +02:00
a27c28c24f Avoid downloading the Terms Markdown twice in safe mode 2022-08-01 00:29:58 +02:00
ed234ec88b Bump version (changelog ignore) 2022-07-31 22:30:51 +02:00
7a0a046596 Refactor: Use hook instead of Context for i18n 2022-07-31 22:30:32 +02:00
0641151ca1 Fix specialization of i18n using global 2022-07-31 20:00:57 +02:00
c6dc2377fa output i18n messages to be imporable on a perl local basis 2022-07-31 19:03:20 +02:00
a3050b3983 squash 2022-07-31 19:00:57 +02:00
69c15bd473 New i18n with dynamic imports 2022-07-31 18:57:30 +02:00
2be40816b2 lib needs to be able to import bin 2022-07-31 00:41:25 +02:00
a98bb25133 Bump version (changelog ignore) 2022-07-30 01:49:33 +02:00
d130c23f5d Do not export components in the index 2022-07-30 01:49:02 +02:00
cd936ee4ef Pretier: Ignoore dist_test 2022-07-30 01:47:44 +02:00
67e3dca0c3 Minor refactor (changelog ignore) 2022-07-30 00:51:29 +02:00
de53f1ff40 Fix retrocompat with React 16 and TypeScript 3 https://github.com/garronej/tss-react/issues/95 2022-07-30 00:45:17 +02:00
d79081dee4 Ship /src/lib in ESM for enabeling dynamic imports 2022-07-30 00:42:38 +02:00
449f100bc0 Using <Suspense /> and React.lazy() 2022-07-30 00:30:57 +02:00
0612b2d0a4 Update garronej_modules_update 2022-07-30 00:30:57 +02:00
583a3e541a Fix readme error (changelog ignore) 2022-07-30 00:30:57 +02:00
9d8d30a864 Update ts-ci, remove changelog, use github release generated body instead 2022-07-30 00:30:57 +02:00
95cda8538f Bump version (changelog ignore) 2022-07-30 00:30:57 +02:00
b116f22152 Support for React.lazy() #141 🎉 2022-07-30 00:30:57 +02:00
ff2fb0d6dc Attempt to activate renovate on project's landingpage (changelog ignore) 2022-07-30 00:30:56 +02:00
020823e933 Update garronej_modules_update 2022-07-28 04:44:01 +00:00
5879972924 Fix readme error (changelog ignore) 2022-07-28 04:38:04 +02:00
8031294230 Update ts-ci, remove changelog, use github release generated body instead 2022-07-28 04:32:27 +02:00
e0a6935c49 Bump version (changelog ignore) 2022-07-28 04:27:38 +02:00
3d581a5454 Support for React.lazy() #141 🎉 2022-07-28 04:24:10 +02:00
317ad8386c Attempt to activate renovate on project's landingpage (changelog ignore) 2022-07-23 17:45:04 +02:00
7ad5011280 Merge pull request #142 from InseeFrLab/renovate/configure
Configure Renovate
2022-07-23 16:10:40 +02:00
76990702f0 Add renovate.json
update
2022-07-23 16:09:14 +02:00
9a27824fe9 Only run the unit test in the CI pipeline (changelog ignore) 2022-07-22 18:25:09 +02:00
d877d90bf3 Writing tests for #141 2022-07-22 18:20:50 +02:00
0b790c47e6 fmt (changelog ignore) 2022-07-22 17:34:52 +02:00
6b49c8dd95 complete exhausive unit testing of replacer (changelog ignore) 2022-07-22 17:33:53 +02:00
e56f9b144e Improve unit tests (changelog ignore) 2022-07-22 17:22:23 +02:00
3c82944daf improve replacer unit test (changelog ignore) 2022-07-22 16:38:44 +02:00
5e3070a6c4 Improve unit test (changelog ignore) 2022-07-22 15:35:23 +02:00
a3a0e9eebe Add closest thing to a migration guide from v4 to v5 (changelog ignore) 2022-07-21 19:49:10 +02:00
c6593f03bc Update changelog v5.7.3 2022-07-17 01:34:02 +00:00
54dc4f650c Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-07-17 03:30:10 +02:00
dec7af0381 Bump version (changelog ignore) 2022-07-17 03:29:17 +02:00
ae001eea54 update evt and powerhooks (changelog ignore) 2022-07-17 03:28:35 +02:00
4d0e17a11e Update changelog v5.7.1 2022-07-17 03:28:34 +02:00
c708e619e9 Update changelog v5.7.2 2022-07-13 01:30:23 +00:00
2cceb3c929 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-07-13 03:27:38 +02:00
aee8704691 Bump version (changelog ignore) 2022-07-13 03:27:29 +02:00
43af60237b #135 2022-07-13 03:26:46 +02:00
e615479b41 Update changelog v5.7.1 2022-07-11 17:25:30 +00:00
973fb4d2d5 Bump version (changelog ignore) 2022-07-11 19:18:42 +02:00
964feae846 #134 2022-07-11 19:18:15 +02:00
ea3d8a5634 Update changelog v5.7.0 2022-07-07 11:56:00 +00:00
48aab2d92a Bump version (changelog ignore) 2022-07-07 13:53:20 +02:00
00eab73954 Merge pull request #120 from revolunet/logout
feat: add logout-confirm
2022-07-07 13:40:17 +02:00
5f6f8b12bd fix: use kcMessages 2022-07-07 01:05:18 +02:00
c2d4d6fd49 fix: add translations FR 2022-07-07 01:05:18 +02:00
04b660ff9b feat: add logout-confirm 2022-07-07 01:05:15 +02:00
c292d926be Update changelog v5.6.5 2022-07-06 10:01:52 +00:00
23b83ceef7 Bump version (changelog ignore) 2022-07-06 11:58:43 +02:00
1324648db6 Merge pull request #133 from bardius/fix/Issue-131-include-all-nested-folders-in-artifact-unzip
fix: Issue-131: include all nested folders in artifact unzip
2022-07-06 11:56:00 +02:00
735bff3348 Merge pull request #132 from bardius/fix/Issue-130-fix-equality-detection-of-nested-ftl-object-properties
fix: Issue-130: fix equality detection of nested ftl object property …
2022-07-06 11:55:18 +02:00
05a6aee782 fix: Issue-131: include all nested folders in artifact unzip 2022-07-06 11:44:25 +03:00
c7349c2556 fix: Issue-130: fix equality detection of nested ftl object property paths 2022-07-06 11:38:47 +03:00
51f3d06752 Update changelog v5.6.4 2022-07-06 00:08:51 +00:00
31759d86ab Bump version (changelog ignore) 2022-07-06 02:05:45 +02:00
7c6eed99d2 Fix login-register-email.ftl 2022-07-06 02:05:08 +02:00
bc4b0ec17d Update to Keycloak 18.0.2 for the test container 2022-07-06 01:30:30 +02:00
f766348b87 Update changelog v5.6.3 2022-07-03 22:05:43 +00:00
82281303d0 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-07-04 00:02:47 +02:00
1caa17beb0 Bump version (changelog ignore) 2022-07-04 00:02:39 +02:00
1c4d346f9f update powerhooks 2022-07-04 00:02:19 +02:00
4320efb049 Update changelog v5.6.2 2022-07-03 18:07:07 +00:00
a756423768 Bump version (changelog ignore) 2022-07-03 20:03:35 +02:00
8525fc74c0 Update powerhooks and EVT 2022-07-03 20:03:19 +02:00
30c0cc5aa8 Update changelog v5.6.1 2022-07-03 14:01:24 +00:00
b3bbd7c07d Bump version (changelog ignore) 2022-07-03 15:58:41 +02:00
09d4ba2bb0 Refactor (avoid using else) changelog ignore 2022-07-03 15:58:00 +02:00
30315027c1 Merge pull request #128 from Ann2827/pull
Fix bugs on error.ftl template
2022-07-03 15:52:42 +02:00
05acefe70e fix: bugs on error.ftl template 2022-07-02 11:01:14 +03:00
6c14758e33 Merge pull request #52 from InseeFrLab/main
Update fork
2022-07-02 10:39:39 +03:00
b93ec20119 Update changelog v5.6.0 2022-06-28 21:39:13 +00:00
ce04646576 Update React (changelog ignore) #127 2022-06-28 23:36:16 +02:00
9282dfe491 Bump version (changelog ignore) 2022-06-28 21:53:02 +02:00
fca6280bcc Merge pull request #127 from aidangilmore/add-totp-support
feat: add login-config-totp.ftl page
2022-06-28 21:49:04 +02:00
cdeb575ec6 Fix unknown algorithm name lookup in LoginConfigTotp 2022-06-28 15:21:09 -04:00
271dbe4fb7 Add totp config support 2022-06-28 14:37:17 -04:00
9a0337114d Update changelog v5.5.0 2022-06-28 04:54:43 +00:00
2d28f4eb55 Attempt to fix ci (changelog ignore) 2022-06-28 06:51:49 +02:00
f673927e16 [CI]: update npm-install (changelog ignore) 2022-06-28 06:45:59 +02:00
52896b82a9 Update yarn.lock (changelog ignore) 2022-06-28 06:40:51 +02:00
9d53ecb0cd Bump version (changelog ignore) 2022-06-28 06:37:05 +02:00
aec3ac32e5 Make it possible to redirect to login by repacing the url (should be default in most case) 2022-06-28 06:36:30 +02:00
f150f1568e Update changelog v5.4.7 2022-06-19 21:33:05 +00:00
309189c55d Bump version (changelog ignore) 2022-06-19 23:30:38 +02:00
f68c54cd3a #121 2022-06-19 23:30:05 +02:00
bef8545161 Merge pull request #48 from InseeFrLab/main
Update fork
2022-06-18 17:17:08 +03:00
c21cd14ac2 fmt 2022-06-17 18:25:54 +02:00
275d7f0072 Create CONTRIBUTING.md 2022-06-17 17:00:55 +02:00
58c8306cf4 Enable users to link keycloak in their own app 2022-06-17 16:32:20 +02:00
f782b684ad Update changelog v5.4.6 2022-06-16 23:51:09 +00:00
092b2a5f52 Bump version (changelog ignore) 2022-06-17 01:45:32 +02:00
42b2d40ad6 Update powerhooks (changelog ignore) 2022-06-17 01:45:17 +02:00
3f6fe6cfc0 Use keycloak 18.0.1 i18n resources #120 2022-06-17 01:43:14 +02:00
1abf542a74 Update changelog v5.4.5 2022-06-14 21:07:14 +00:00
c4720ca03d Bump version (changelog ignore) 2022-06-14 23:04:59 +02:00
4316878cce Merge pull request #119 from dro-sh/fix-locale-on-useFormValidationSlice
pass locale to getGetErrors to get correct messages
2022-06-14 23:04:28 +02:00
c180d75a83 pass locale to getGetErrors to get correct messages 2022-06-14 21:52:18 +03:00
4a040b32c0 Display downloads by month (changelog ignore) 2022-06-11 03:36:03 +02:00
ea330a1eef Update changelog v5.4.4 2022-06-05 03:49:23 +00:00
2451ba0a77 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-06-05 05:46:26 +02:00
2c276a56e5 Bump version (changelog ignore) 2022-06-05 05:46:20 +02:00
708030b8b5 Update powerhooks (changelog ignore) 2022-06-05 05:46:03 +02:00
d5fc0582bc Update changelog v5.4.3 2022-06-01 23:37:41 +00:00
f9dce82c83 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-06-02 01:32:57 +02:00
e82602f994 Bump version (changelog ignore) 2022-06-02 01:32:49 +02:00
1d36395e5a Update EVT and powerhook (changelog ignore) 2022-06-02 01:32:26 +02:00
8f8857bc22 Update changelog v5.4.2 2022-06-01 22:21:23 +00:00
226247b3b6 Bump version (changelog ignore) 2022-06-02 00:15:25 +02:00
b2ea5014f3 Update evt (changelog ignore) 2022-06-02 00:15:10 +02:00
48bc416aa7 Update powerhooks (changelog ignore) 2022-06-02 00:14:36 +02:00
386e7203b2 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-06-01 05:51:41 +02:00
9bdb224631 Prevent rate limite in CI by authenticating 2022-06-01 05:51:33 +02:00
dd36aacbee Update changelog v5.4.1 2022-06-01 03:45:04 +00:00
6b57b1c720 Bump version (changelog ignore) 2022-06-01 05:42:23 +02:00
9e9e6d41ff Update dependencies (changelog ignore) 2022-06-01 05:38:12 +02:00
5140389502 Update changelog v5.4.0 2022-05-23 14:59:15 +00:00
fc6328131f Bump version (changelog ignore) 2022-05-23 16:51:58 +02:00
9de0083ca6 #109 2022-05-23 16:51:18 +02:00
f5231b840d Update changelog v5.3.2 2022-05-04 10:16:04 +00:00
afb6596c4b Bump version (changelog ignore) 2022-05-04 12:13:10 +02:00
dde9afef92 Merge pull request #101 from Romcol/bugfix/99
Issue #99 - Make replace less greedy in remplaceImportFromStatic
2022-05-04 12:12:06 +02:00
6595e9c3cb [IMP] Issue #99 - Make replace less greedy in remplaceImportFromStatic 2022-05-04 11:22:58 +02:00
c0e3b5fe06 Update changelog v5.3.1 2022-04-29 16:41:12 +00:00
6b8f3bbc51 Bump version (changelog ignore) 2022-04-29 18:35:39 +02:00
9a5a021e64 Comment out missleading informations 2022-04-29 18:35:07 +02:00
14c05fec8c Update changelog v5.3.0 2022-04-28 07:52:08 +00:00
eaf7a455cd Fix name of npx script for generating email dir (changelog ignore) 2022-04-28 09:46:49 +02:00
55bb21f3ee Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-04-28 09:36:41 +02:00
f123bc0912 Bump version (changelog ignore) 2022-04-28 09:35:53 +02:00
572eb7b1c0 Rename keycloak_theme_email to keycloak_email (it's shorter) 2022-04-28 09:34:01 +02:00
2befaff8a8 Update changelog sumup in readme (changelog ignore) 2022-04-27 23:32:47 +02:00
437a9ce2d3 Update changelog v5.2.0 2022-04-27 19:27:37 +00:00
1b967b250a Bump version (changelog ignore) 2022-04-27 21:23:10 +02:00
e221f39e07 Export KcApp 2022-04-27 21:22:55 +02:00
21a8838a24 Update changelog v5.1.0 2022-04-27 19:20:36 +00:00
fad91ccae0 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-04-27 21:17:40 +02:00
825914aa4b Export kcLanguageTags 2022-04-27 21:17:26 +02:00
a8246d12ee Update changelog v5.0.0 2022-04-27 19:05:48 +00:00
abb8bf2ebb Bump version (changelog ingore) 2022-04-27 21:02:29 +02:00
7e7071305f i18n rebuild from the ground up 2022-04-27 21:02:10 +02:00
cc8b2e72c1 Update changelog v4.10.0 2022-04-26 14:45:04 +00:00
a3d6ee44a1 Bump version (changelog ignore) 2022-04-25 13:26:24 +02:00
ac99e2f41f Merge pull request #92 from Tasyp/add-login-idp-link-email
feat: add login-idp-link-email page
2022-04-25 13:23:54 +02:00
bf1839c061 feat: add mock data for login-idp-link-email page 2022-04-25 14:15:40 +03:00
fd5c132a40 feat: supply broker context with context 2022-04-25 14:14:03 +03:00
4dfa268eb3 Update changelog v4.9.0 2022-04-25 11:09:16 +00:00
332ca084f5 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-04-25 13:06:10 +02:00
01cbb8680a Bump version (changelog ignore) 2022-04-25 13:05:59 +02:00
bbdaaf30bc Test by default with kc 18. Update instructions to use quay.io/keycloak/keycloak instead of jboss/keycloak #93 2022-04-25 13:05:13 +02:00
0550b9ff8b Update changelog v4.8.7 2022-04-25 10:33:29 +00:00
b1a4c5cca5 Bump version (changelog ignore) 2022-04-25 12:30:29 +02:00
785080e14a Update instructions to test on Keycloak 18 https://github.com/keycloak/keycloak-web/issues/306 #93 2022-04-25 12:30:28 +02:00
3c7e093a3c Move the documentation form the readme to docs.keycloakify.dev 2022-04-23 22:01:15 +02:00
89be9f3a86 fmt (changelog ignore) 2022-04-23 05:14:43 +02:00
6f2ffa7861 Update README.md 2022-04-22 20:01:26 +02:00
7091f283f2 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-04-22 20:00:50 +02:00
2d28003451 Update demo video 2022-04-22 20:00:41 +02:00
f0ba7d3c0d Update changelog v4.8.6 2022-04-22 16:26:51 +00:00
cd5f346895 Bump version (changelog ignore) 2022-04-22 18:23:35 +02:00
66cd5aef0c always offer to download v11.0.3 2022-04-22 18:22:28 +02:00
6f8ec53e8b feat: add login-idp-link-email page 2022-04-22 17:54:47 +03:00
622504ff72 Update changelog v4.8.5 2022-04-22 14:45:25 +00:00
c9d47c483c Bump version (changelog ignore) 2022-04-22 16:39:04 +02:00
07098b89a5 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-04-22 16:38:26 +02:00
c583b83cbb #91 2022-04-22 16:38:20 +02:00
1670e1fe42 Update changelog v4.8.4 2022-04-22 11:59:31 +00:00
de8809608c Bump version (changelog ignore) 2022-04-22 13:56:54 +02:00
0e194ee045 #90 2022-04-22 13:56:29 +02:00
4205f6ecbe Remove no longer relevent link (changelog ignore) 2022-04-21 02:10:23 +02:00
4d90ec60e2 Remove no longer relevent section of the readme (changelog ignore) 2022-04-21 02:03:29 +02:00
d126a6563b Update changelog v4.8.3 2022-04-20 20:32:12 +00:00
aecb6ae79c Bump version (changelog ignore) 2022-04-20 22:26:33 +02:00
a65c826717 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-04-20 22:26:16 +02:00
66c3705f2b Keycloak 18 container hasn't been published yet (changelog ignore) 2022-04-20 22:26:10 +02:00
d18ebb45f8 Update changelog v4.8.2 2022-04-20 20:20:03 +00:00
d8e01f2c5d Bump version (changelog ignore) 2022-04-20 22:14:06 +02:00
4abbaa3841 Tell pepoles they can test with different keycloak version 2022-04-20 22:13:42 +02:00
42a463b348 Update changelog v4.8.1 2022-04-20 19:19:55 +00:00
8e15cf1d45 Bump version (changelog ignore) 2022-04-20 21:17:05 +02:00
2468b4108e Update changelog highlights 2022-04-20 21:15:58 +02:00
528b1bb607 Add missing shebang 2022-04-20 21:13:32 +02:00
b4449bb289 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2022-04-20 21:09:53 +02:00
737e00b490 Add video demo for npx download-builtin-keycloak-theme 2022-04-20 21:09:36 +02:00
55d4c7f4ab Update changelog v4.8.0 2022-04-20 18:59:10 +00:00
7afb078efd fmt (changelog ignore) 2022-04-20 20:55:53 +02:00
2c04f6c1e9 Bump version (changelog ignore) 2022-04-20 20:44:32 +02:00
2ad5ed7e73 Document email template customization feature #9 2022-04-20 20:44:31 +02:00
f2b7fe46a2 Add mention of download-builtin-keycloak-theme 2022-04-20 20:22:19 +02:00
1a1af62f62 Improve readme (changelog ignore) 2022-04-20 20:11:26 +02:00
98f715e652 Let the choice of kc version be auto in GH Action 2022-04-20 12:23:28 +02:00
fa5f1c230a Only test on node v15 and v14 (bellow is no longer supported (rmSync) 2022-04-20 11:59:19 +02:00
c92ae9cfa9 Fix broken link in readme (changelog ignore) 2022-04-20 11:43:15 +02:00
3dcb3a1a5b cange name of temp dir (changelog ignore) 2022-04-20 01:26:22 +02:00
efde71d07c Feature email customization #9 2022-04-20 00:39:40 +02:00
bff8cf2f32 Update README (changelog ignore) 2022-04-15 12:19:41 +02:00
72730135f1 Mention tested wit CRA 5.0.0 (changelog ignore) 2022-04-14 21:55:42 +02:00
50cf27b686 Mention test on Keycloak 17.0.1 (changelog ignore) 2022-04-14 21:53:27 +02:00
b293abffa4 Update changelog v4.7.6 2022-04-12 16:57:59 +00:00
be84ea299c Bump version (changelog ignore) 2022-04-12 18:51:30 +02:00
d54586426a Fix bugs with language switch #85 2022-04-12 18:51:03 +02:00
f15c0ecbb0 Merge pull request #46 from InseeFrLab/main
Update fork
2022-04-04 17:41:06 +03:00
359 changed files with 41481 additions and 24235 deletions

2
.gitattributes vendored
View File

@ -1,3 +1,3 @@
src/lib/i18n/generated_kcMessages/* linguist-documentation
src/bin/build-keycloak-theme/index.ts -linguist-detectable
src/bin/keycloakify/index.ts -linguist-detectable
src/bin/install-builtin-keycloak-themes.ts -linguist-detectable

4
.github/FUNDING.yaml vendored Normal file
View File

@ -0,0 +1,4 @@
# These are supported funding model platforms
github: [garronej]
custom: ['https://www.ringerhq.com/experts/garronej']

25
.github/release.yaml vendored Normal file
View File

@ -0,0 +1,25 @@
changelog:
exclude:
labels:
- ignore-for-release
authors:
- octocat
categories:
- title: Breaking Changes 🛠
labels:
- breaking
- title: Exciting New Features 🎉
labels:
- feature
- title: Fixes 🔧
labels:
- fix
- title: Documentation 🔧
labels:
- docs
- title: CI 👷
labels:
- ci
- title: Other Changes
labels:
- '*'

View File

@ -9,11 +9,12 @@ on:
jobs:
test_formatting:
test_lint:
runs-on: ubuntu-latest
if: ${{ !github.event.created && github.repository != 'garronej/ts-ci' }}
steps:
- uses: actions/checkout@v2.3.4
- uses: actions/setup-node@v2.1.3
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: bahmutov/npm-install@v1
- name: If this step fails run 'yarn format' then commit again.
run: |
@ -23,20 +24,21 @@ jobs:
fi
$PACKAGE_MANAGER run format:check
test:
runs-on: macos-10.15
needs: test_formatting
runs-on: ${{ matrix.os }}
needs: test_lint
strategy:
matrix:
node: [ '15', '14', '13' ]
name: Test with Node v${{ matrix.node }}
node: [ '14','16' ]
os: [ windows-latest, ubuntu-latest ]
name: Test with Node v${{ matrix.node }} on ${{ matrix.os }}
steps:
- name: Tell if project is using npm or yarn
id: step1
uses: garronej/ts-ci@v1.1.6
uses: garronej/ts-ci@v2.0.2
with:
action_name: tell_if_project_uses_npm_or_yarn
- uses: actions/checkout@v2.3.4
- uses: actions/setup-node@v2.1.3
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: bahmutov/npm-install@v1
@ -62,64 +64,49 @@ jobs:
from_version: ${{ steps.step1.outputs.from_version }}
to_version: ${{ steps.step1.outputs.to_version }}
is_upgraded_version: ${{ steps.step1.outputs.is_upgraded_version }}
is_release_beta: ${{steps.step1.outputs.is_release_beta }}
is_pre_release: ${{steps.step1.outputs.is_pre_release }}
steps:
- uses: garronej/ts-ci@v1.1.6
- uses: garronej/ts-ci@v2.0.2
id: step1
with:
action_name: is_package_json_version_upgraded
branch: ${{ github.head_ref || github.ref }}
update_changelog:
runs-on: ubuntu-latest
needs: check_if_version_upgraded
if: needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true'
steps:
- uses: garronej/ts-ci@v1.1.6
with:
action_name: update_changelog
branch: ${{ github.head_ref || github.ref }}
create_github_release:
runs-on: ubuntu-latest
# We create a release only if the version have been upgraded and we are on the main branch
# or if we are on a branch of the repo that has an PR open on main.
if: |
needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true' &&
(
github.event_name == 'push' ||
needs.check_if_version_upgraded.outputs.is_pre_release == 'true'
)
needs:
- update_changelog
- check_if_version_upgraded
steps:
- name: Build GitHub release body
id: step1
run: |
if [ "$FROM_VERSION" = "0.0.0" ]; then
echo "::set-output name=body::🚀"
else
echo "::set-output name=body::📋 [CHANGELOG](https://github.com/$GITHUB_REPOSITORY/blob/v$TO_VERSION/CHANGELOG.md)"
fi
env:
FROM_VERSION: ${{ needs.check_if_version_upgraded.outputs.from_version }}
TO_VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}
- uses: garronej/action-gh-release@v0.2.0
- uses: softprops/action-gh-release@v1
with:
name: Release v${{ needs.check_if_version_upgraded.outputs.to_version }}
tag_name: v${{ needs.check_if_version_upgraded.outputs.to_version }}
target_commitish: ${{ github.head_ref || github.ref }}
body: ${{ steps.step1.outputs.body }}
generate_release_notes: true
draft: false
prerelease: ${{ needs.check_if_version_upgraded.outputs.is_release_beta == 'true' }}
prerelease: ${{ needs.check_if_version_upgraded.outputs.is_pre_release == 'true' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish_on_npm:
runs-on: macos-10.15
runs-on: ubuntu-latest
needs:
- update_changelog
- create_github_release
- check_if_version_upgraded
steps:
- uses: actions/checkout@v2.3.4
- uses: actions/checkout@v3
with:
ref: ${{ github.ref }}
- uses: actions/setup-node@v2.1.3
- uses: actions/setup-node@v3
with:
node-version: '15'
registry-url: https://registry.npmjs.org/
- uses: bahmutov/npm-install@v1
- run: |
@ -128,7 +115,7 @@ jobs:
PACKAGE_MANAGER=yarn
fi
$PACKAGE_MANAGER run build
- run: npx -y -p denoify@0.6.5 denoify_enable_short_npm_import_path
- run: npx -y -p denoify@1.2.2 enable_short_npm_import_path
env:
DRY_RUN: "0"
- name: Publishing on NPM
@ -142,11 +129,11 @@ jobs:
false
fi
EXTRA_ARGS=""
if [ "$IS_BETA" = "true" ]; then
EXTRA_ARGS="--tag beta"
if [ "$IS_PRE_RELEASE" = "true" ]; then
EXTRA_ARGS="--tag next"
fi
npm publish $EXTRA_ARGS
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}
IS_BETA: ${{ needs.check_if_version_upgraded.outputs.is_release_beta }}
IS_PRE_RELEASE: ${{ needs.check_if_version_upgraded.outputs.is_pre_release }}

4
.gitignore vendored
View File

@ -41,8 +41,12 @@ jspm_packages
.DS_Store
/dist
/dist_test
/sample_react_project/
/.yarn_home/
.idea
/keycloak_email
/build_keycloak

View File

@ -1,7 +1,10 @@
node_modules/
/dist/
/dist_test/
/CHANGELOG.md
/.yarn_home/
/src/test/apps/
/src/tools/types/
/sample_react_project
/sample_react_project
/build_keycloak/
/src/lib/i18n/generated_messages/

View File

@ -5,7 +5,7 @@
"semi": true,
"singleQuote": false,
"quoteProps": "preserve",
"trailingComma": "all",
"trailingComma": "none",
"bracketSpacing": true,
"arrowParens": "avoid"
}

View File

@ -1,833 +0,0 @@
### **4.7.5** (2022-04-09)
- Fix #85
### **4.7.4** (2022-04-09)
- M1 Mac compat (for real this time)
### **4.7.3** (2022-04-08)
- Mention that there is still problems with M1 Mac
### **4.7.2** (2022-04-06)
- #43: M1 Mac support
### **4.7.1** (2022-03-30)
- Improve browser autofill
- factorization
## **4.7.0** (2022-03-17)
- Add support for options validator
- remove duplicate dependency
## **4.6.0** (2022-03-07)
- Remove powerhooks as dev dependency
### **4.5.5** (2022-03-07)
- Update tss-react
### **4.5.4** (2022-03-06)
- Remove tss-react from peerDependencies (it becomes a dependency)
- (dev script) Use tsconfig.json to tell we are at the root of the project
### **4.5.3** (2022-01-26)
- Themes no longer have to break on minor Keycloakify update
### **4.5.2** (2022-01-20)
- Test container uses Keycloak 16.1.0
- Merge pull request #78 from InseeFrLab/Ann2827/pull
Ann2827/pull
- Refactor #78
- Compat with Keycloak 16 (and probably 17, 18) #79
- Warning about compat issues with Keycloak 16
- fix: changes
- fix: Errors on pages login-idp-link-confirm and login-idp-link-email
ref: https://github.com/InseeFrLab/keycloakify/issues/75
### **4.5.1** (2022-01-18)
- fix previous version
## **4.5.0** (2022-01-18)
- Read public/CNAME for domain name in --externel-assets mode
## **4.4.0** (2022-01-01)
- Merge pull request #73 from lazToum/main
(feature) added login-page-expired.ftl
- added login-page-expired.ftl
- Add update instruction for 4.3.0
## **4.3.0** (2021-12-27)
- Merge pull request #72 from praiz/main
feat(*): added login-update-password
- feat(*): added login-update-password
### **4.2.21** (2021-12-27)
- update dependencies
### **4.2.19** (2021-12-21)
- Merge pull request #70 from VBustamante/patch-1
- Added realm name field to KcContext mocks object
- Merge pull request #69 from VBustamante/patch-1
Adding name field to realm in KcContext type
- Adding name field to realm in KcContext type
### **4.2.18** (2021-12-17)
- Improve css url() import (fix CRA 5)
### **4.2.17** (2021-12-16)
- Fix path.join polyfill
### **4.2.16** (2021-12-16)
### **4.2.15** (2021-12-16)
- use custom polyfill for path.join (fix webpack 5 build)
### **4.2.14** (2021-12-12)
- Merge pull request #65 from InseeFrLab/doge_ftl_errors
Prevent ftl errors in Keycloak log
- Encourage users to report errors in logs
- Fix ftl error related to url.loginAction in saml-post-form.ftl
- Ftl prevent error with updateProfileCtx
- Ftl prevent error with auth.attemptedUsername
- Fix ftl error as comment formatting
- Merge remote-tracking branch 'origin/main' into doge_ftl_errors
- Update README, remove all instruction about errors in logs
- Avoid error in Keycloak logs, fix long template loading time
- Add missing collon in README sample code
Add miss ','
### **4.2.13** (2021-12-08)
- Fix broken link about how to import fonts #62
- Add a video to show how to test the theme in a local container
### **4.2.12** (2021-12-08)
- Update post build instructions
### **4.2.11** (2021-12-07)
### **4.2.10** (2021-11-12)
- Export an exaustive list of KcLanguageTag
### **4.2.9** (2021-11-11)
- Fix useAdvancedMsg
### **4.2.8** (2021-11-10)
- Update doc about pattern that can be used for user attributes #50
- Bring back Safari compat
### **4.2.7** (2021-11-09)
- Fix useFormValidationSlice
### **4.2.6** (2021-11-08)
- Fix deepClone so we can overwrite with undefined in when we mock kcContext
### **4.2.5** (2021-11-07)
- Better debugging experience with user profile
### **4.2.4** (2021-11-01)
- Better autoComplete typings
### **4.2.3** (2021-11-01)
- Make it more easy to understand that error in the log are expected
### **4.2.2** (2021-10-27)
- Replace 'path' by 'browserify-path' #47
### **4.2.1** (2021-10-26)
- useFormValidationSlice: update when params have changed
- Explains that the password can't be validated
## **4.2.0** (2021-10-26)
- Export types definitions for Attribue and Validator
## **4.1.0** (2021-10-26)
- Document what's new in v4
# **4.0.0** (2021-10-26)
- fix RegisterUserProfile password confirmation field
- Much better support for frontend field validation
- Fix css injection order
- Makes the download output predictable. This fixes the case where GitHub redirects and wget was trying to download a filename called "15.0.2", and then unzip wouldn't pick it up.
Changes wget to curl because curl is awesome. -L is to follow the GitHub redirects.
- Remove duplicates
### **3.0.2** (2021-10-18)
- Scan deeper to retreive user attribute
### **3.0.1** (2021-10-17)
- Add client.description in type kcContext type def
# **3.0.0** (2021-10-16)
### **2.5.3** (2021-10-16)
### **2.5.2** (2021-10-13)
### **2.5.1** (2021-10-13)
- Update tss-react
## **2.5.0** (2021-10-12)
- register-user-profile.ftl tested working
- Make kcMessage more easily hackable
- fix useKcMessage
- Implement and type validators
- Remove syntax error in ftl and make it more directly debugable
- Support register-user-profile.ftl
## **2.4.0** (2021-10-08)
- #38: Implement messagesPerField existsError and get
## **2.3.0** (2021-10-07)
- #20: Support advancedMsg
## **2.2.0** (2021-10-07)
- Feat scrip: download-builtin-keycloak-theme for downloading any version of the builtin themes
- Use the latest version of keycloak for testing
- Test locally with 15.0.2 instead of 11.0.3
## **2.1.0** (2021-10-06)
- Support Hungarian and Danish (use Keycloak 15 language resources)
### **2.0.20** (2021-10-05)
- Update README.md
### **2.0.19** (2021-09-17)
- Fix kcContext type definitions
### **2.0.18** (2021-09-14)
### **2.0.17** (2021-09-14)
### **2.0.16** (2021-09-12)
- Add explaination about errors in logs
### **2.0.15** (2021-08-31)
- Update tss-react
### **2.0.14** (2021-08-20)
- Update tss-react
### **2.0.13** (2021-08-04)
- Merge pull request #28 from marcmrf/main
fix(mvn): scoped packages compatibility
- fix(mvn): scoped packages compatibility
### **2.0.12** (2021-07-28)
- Merge pull request #27 from jchn-codes/patch-1
add maven to requirements
- add maven to requirements
- Add #bluehats in the keyworks
### **2.0.11** (2021-07-21)
- Spaces in file path #22
- uptdate dependnecies
- Inport specific powerhooks files to reduce bundle size
### **2.0.10** (2021-07-16)
- Update dependencies
### **2.0.9** (2021-07-14)
- Fix #21
### **2.0.8** (2021-07-12)
- Fix previous release
- #20: Add def for clientId and name on kcContext.client
### **2.0.6** (2021-07-08)
- Merge pull request #18 from asashay/add-custom-props-to-theme-properties
Add possibility to add custom properties to theme.properties file
- add possibility to add custom properties to theme.properties file
### **2.0.5** (2021-07-05)
- Fix broken url for big stylesheet #16
### **2.0.4** (2021-07-03)
- Fix: #7
### **2.0.3** (2021-06-30)
- Escape double quote in ftl to js conversion #15
- Update readme
### **2.0.2** (2021-06-28)
- Updagte README for implementing non incuded pages
### **2.0.1** (2021-06-28)
- Update documentation for v2
# **2.0.0** (2021-06-28)
- Fix last bugs before relasing v2
- Implement a mechanism to overload kcContext
- Give the option in template to pull the default assets or not
- Enable possiblity to support custom pages (without forking keycloakify)
- Implement a getter for kcContext
- Update README.md
# **2.0.0** (2021-06-28)
- Fix last bugs before relasing v2
- Implement a mechanism to overload kcContext
- Give the option in template to pull the default assets or not
- Enable possiblity to support custom pages (without forking keycloakify)
- Implement a getter for kcContext
- Update README.md
### **1.2.1** (2021-06-22)
- Remove unessesary log
## **1.2.0** (2021-06-22)
- Generate kcContext automatically :rocket:
### **1.1.6** (2021-06-21)
- Fix: Alert messages sometimes includes HTML that is not rendered
- Update dist
### **1.1.5** (2021-06-15)
- #11: Provide socials in the register
### **1.1.4** (2021-06-15)
- Merge pull request #12 from InseeFrLab/email-typo
Fix typo on email
- Fix typo on email
### **1.1.3** (2021-06-14)
- Add missing key in Login for providers
### **1.1.2** (2021-06-14)
### **1.1.1** (2021-06-14)
## **1.1.0** (2021-06-14)
- Add login-idp-link-confirm.ftl
- Fix login-update-profile.ftl
- Add login-update-profile.ftl page
- Fix default background bug
- Remove unused 'markdown' dependency
- Fix warning related to powerhooks_useGlobalState_kcLanguageTag
- Update README.md
### **1.0.4** (2021-05-28)
- Instructions for custom themes with custom components
### **1.0.3** (2021-05-23)
- Instuction about how to integrate with non CRA projects
- Add mention to awesome list
### **1.0.2** (2021-05-01)
### **1.0.1** (2021-05-01)
- Fix: LoginOtp (and not otc)
# **1.0.0** (2021-05-01)
- #4: Guide for implementing a missing page
- Support OTP #4
### **0.4.4** (2021-04-29)
- Fix previous release
### **0.4.3** (2021-04-29)
- Add infos about the plugin that defines authorizedMailDomains
### **0.4.2** (2021-04-29)
- Client side validation of allowed email domains
- Support email whitlisting
- Restore kickstart video in the readme
- Update README.md
- Update README.md
- Important readme update
### **0.4.1** (2021-04-11)
- Quietly re-introduce --external-assets
- Give example of customization
## **0.4.0** (2021-04-09)
- Acual support of Therms of services
### **0.3.24** (2021-04-08)
- Add missing dependency: markdown
### **0.3.23** (2021-04-08)
- Allow to lazily load therms
### **0.3.22** (2021-04-08)
- update powerhooks
- Support terms and condition
- Fix info.ftl
- For useKcMessage we prefer returning callbacks with a changing references
### **0.3.21** (2021-04-04)
- Update powerhooks
### **0.3.20** (2021-04-01)
- Always catch freemarker errors
### **0.3.19** (2021-04-01)
- Fix previous release
### **0.3.18** (2021-04-01)
- Fix error.ftt, Adopt best effort strategy to convert ftl values into JS
### **0.3.17** (2021-03-29)
- Use push instead of replace in keycloak-js adapter to enable going back
### **0.3.15** (2021-03-28)
- Remove all reference to --external-assets, broken feature
### **0.3.14** (2021-03-28)
- Fix standalone mode: imports from js
### **0.3.13** (2021-03-26)
### **0.3.12** (2021-03-26)
- Fix mocksContext
### **0.3.11** (2021-03-26)
- Fix previous build, improve README
### **0.3.10** (2021-03-26)
- Handle <style> tag, improve documentation
### **0.3.9** (2021-03-25)
- Update readme
- Document --external-assets
- Update README.md
- Update README.md
- Update README.md
### **0.3.8** (2021-03-22)
- Make standalone mode the default
### **0.3.7** (2021-03-22)
- (test) external asset mode by default
### **0.3.6** (2021-03-22)
- Fix previous release
### **0.3.5** (2021-03-22)
- support homepage with urlPath
### **0.3.4** (2021-03-22)
- Bugfix: Import assets from CSS
### **0.3.3** (2021-03-22)
- Fix submit not receving correct text
### **0.3.2** (2021-03-21)
- Fix broken previous release
### **0.3.1** (2021-03-21)
- kcHeaderClass can be updated after initial mount
## **0.3.0** (2021-03-20)
- Bump version
- Feat: Cary over states using URL search params
- Bugfix: with kcHtmlClass
### **0.2.10** (2021-03-19)
- Remove dependency to denoify
### **0.2.9** (2021-03-19)
- Update deps and CI workflow
### **0.2.8** (2021-03-19)
- Bugfix: keycloak_build that grow and grow in size
- Add disclaimer about maitainment strategy
- Add a note for tested version support
### **0.2.7** (2021-03-13)
- Bump version
- Update README.md
- Update README.md
### **0.2.6** (2021-03-10)
- Fix generated gitignore
### **0.2.5** (2021-03-10)
- Fix generated .gitignore
### **0.2.4** (2021-03-10)
- Update README.md
### **0.2.3** (2021-03-09)
- fix gitignore generation
### **0.2.2** (2021-03-08)
- Add table of content
- Update README.md
- Update README.md
## **0.2.1** (2021-03-08)
- Update ci.yaml
- Update readme
- Update readme
- update deps
- Update readme
- Add all mocks for testing
- many small fixes
### **0.1.6** (2021-03-07)
- Fix Turkish
### **0.1.5** (2021-03-07)
- Fix getKcLanguageLabel
### **0.1.4** (2021-03-07)
### **0.1.3** (2021-03-07)
- Implement LoginVerifyEmail
- Implement login-reset-password.ftl
### **0.1.2** (2021-03-07)
- Fix build
- Fix build
### **0.1.1** (2021-03-06)
- Implement Error page
- rename pageBasename by pageId
- Implement reactive programing for language switching
- Add Info page, refactor
## **0.1.0** (2021-03-05)
- Rename keycloakify
### **0.0.33** (2021-03-05)
- Fix syncronization with non react pages
### **0.0.32** (2021-03-05)
- bump version
- Add log to tell when we are using react
- Fix missing parentesis
### **0.0.31** (2021-03-05)
- Fix typo
- Fix register page 500
### **0.0.30** (2021-03-05)
- Edit language statistique
### **0.0.30** (2021-03-05)
- avoid escaping urls
- Use default value instead of value
- Fix double single quote problem in messages
- Fix typo
- Fix non editable username
- Fix some bugs
- Fix Object.deepAssign
- Make the dongle to download smaller
- Split kcContext among pages
- Implement register
### **0.0.29** (2021-03-04)
- Fix build
- Fix i18n
- Login appear to be working now
- closer but not there yet
### **0.0.28** (2021-03-03)
- fix build
- There is no reason not to let use translations outside of keycloak
### **0.0.27** (2021-03-02)
- Implement entrypoint
### **0.0.26** (2021-03-02)
- Login page implemented
- Implement login
- remove unesseary log
### **0.0.25** (2021-03-02)
- Fix build and reduce size
- Implement the template
### **0.0.24** (2021-03-01)
- update
- update
- update
### **0.0.23** (2021-03-01)
- update
### **0.0.23** (2021-03-01)
- update
- update
### **0.0.23** (2021-03-01)
- update
- update
### **0.0.23** (2021-03-01)
- update
- Handle formatting in translation function
### **0.0.22** (2021-02-28)
- Split page messages
### **0.0.21** (2021-02-28)
- Restore yarn file
- Multiple fixes
- Update deps
- Update deps
- includes translations
- Update README.md
- improve docs
- update
- Update README.md
- update
- update
- update
- update
### **0.0.20** (2021-02-27)
- update
- update
### **0.0.19** (2021-02-27)
- update
- update
### **0.0.18** (2021-02-23)
- Bump version number
- Moving on with implementation of the lib
- Update readme
- Readme eddit
- Fixing video link
### **0.0.16** (2021-02-23)
- Bump version
- Give test container credentials
### **0.0.14** (2021-02-23)
- Bump version number
- enable the docker container to be run from the root of the react project
### **0.0.13** (2021-02-23)
- bump version
### **0.0.12** (2021-02-23)
- update readme
### **0.0.11** (2021-02-23)
- Add documentation
### **0.0.10** (2021-02-23)
- Remove extra closing bracket
### **0.0.9** (2021-02-22)
- fix container startup script
- minor update
### **0.0.8** (2021-02-21)
- Include theme properties
### **0.0.7** (2021-02-21)
- fix build
- Fix bundle
### **0.0.6** (2021-02-21)
- Include missing files in the release bundle
### **0.0.5** (2021-02-21)
- Bump version number
- Make the install faster
### **0.0.4** (2021-02-21)
- Fix script visibility
### **0.0.3** (2021-02-21)
- Do not run tests on window
- Add script for downloading base themes
- Generate debug files to be able to test the container
- Fix many little bugs
- refactor
- Almoste there
- Things are starting to take form
- Seems to be working
- First draft
- Remove eslint and prettyer
### **0.0.2** (2021-02-20)
- Update package.json

3
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,3 @@
Looking to contribute? Thank you! PR are more than welcome.
Please refers to [this documentation page](https://docs.keycloakify.dev/contributing) that will help you get started.

565
README.md
View File

@ -2,17 +2,14 @@
<img src="https://user-images.githubusercontent.com/6702424/109387840-eba11f80-7903-11eb-9050-db1dad883f78.png">
</p>
<p align="center">
<i>🔏 Create Keycloak themes using React 🔏</i>
<i>🔏 Create Keycloak themes using React 🔏</i>
<br>
<br>
<a href="https://github.com/garronej/keycloakify/actions">
<img src="https://github.com/garronej/keycloakify/workflows/ci/badge.svg?branch=main">
</a>
<a href="https://bundlephobia.com/package/keycloakify">
<img src="https://img.shields.io/bundlephobia/minzip/keycloakify">
</a>
<a href="https://www.npmjs.com/package/keycloakify">
<img src="https://img.shields.io/npm/dw/keycloakify">
<img src="https://img.shields.io/npm/dm/keycloakify">
</a>
<a href="https://github.com/garronej/keycloakify/blob/main/LICENSE">
<img src="https://img.shields.io/npm/l/keycloakify">
@ -23,475 +20,137 @@
<a href="https://github.com/thomasdarimont/awesome-keycloak">
<img src="https://awesome.re/mentioned-badge.svg"/>
</a>
<p align="center">
<a href="https://www.keycloakify.dev">Home</a>
-
<a href="https://docs.keycloakify.dev">Documentation</a>
-
<a href="https://github.com/codegouvfr/keycloakify-starter">Starter project</a>
</p>
</p>
> New with v4.7.4: **M1 Mac** support (for testing locally with a dockerized Keycloak).
<p align="center">
<i>Ultimately this build tool generates a Keycloak theme</i>
<i>Ultimately this build tool generates a Keycloak theme <a href="https://www.keycloakify.dev">Learn more</a></i>
<img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png">
</p>
# Motivations
Keycloak provides [theme support](https://www.keycloak.org/docs/latest/server_development/#_themes) for web pages. This allows customizing the look and feel of end-user facing pages so they can be integrated with your applications.
It involves, however, a lot of raw JS/CSS/[FTL]() hacking, and bundling the theme is not exactly straightforward.
Beyond that, if you use Keycloak for a specific app you want your login page to be tightly integrated with it.
Ideally, you don't want the user to notice when he is being redirected away.
Trying to reproduce the look and feel of a specific app in another stack is not an easy task not to mention
the cheer amount of maintenance that it involves.
<p align="center">
<i>Without keycloakify, users suffers from a harsh context switch, no fronted form pre-validation</i><br>
<img src="https://user-images.githubusercontent.com/6702424/134997335-a28b4a57-0884-47ec-9341-a0e49f835c4d.gif">
</p>
Wouldn't it be great if we could just design the login and register pages as if they were part of our app?
Here is `keycloakify` for you 🍸
<p align="center">
<i> <a href="https://datalab.sspcloud.fr">With keycloakify:</a> </i>
<br>
<img src="https://user-images.githubusercontent.com/6702424/114332075-c5e37900-9b45-11eb-910b-48a05b3d90d9.gif">
</p>
**TL;DR**: [Here](https://github.com/garronej/keycloakify-demo-app) is a Hello World React project with Keycloakify set up.
If you already have a Keycloak custom theme, it can be easily ported to Keycloakify.
---
- [Motivations](#motivations)
- [Requirements](#requirements)
- [My framework doesnt seem to be supported, what can I do?](#my-framework-doesnt-seem-to-be-supported-what-can-i-do)
- [How to use](#how-to-use)
- [Setting up the build tool](#setting-up-the-build-tool)
- [Changing just the look of the default Keycloak theme](#changing-just-the-look-of-the-default-keycloak-theme)
- [Advanced pages configuration](#advanced-pages-configuration)
- [Hot reload](#hot-reload)
- [Enable loading in a blink of an eye of login pages ⚡ (--external-assets)](#enable-loading-in-a-blink-of-an-eye-of-login-pages----external-assets)
- [User profile and frontend form validation](#user-profile-and-frontend-form-validation)
- [Support for Terms and conditions](#support-for-terms-and-conditions)
- [Some pages still have the default theme. Why?](#some-pages-still-have-the-default-theme-why)
- [GitHub Actions](#github-actions)
- [Limitations](#limitations)
- [`process.env.PUBLIC_URL` not supported.](#processenvpublic_url-not-supported)
- [`@font-face` importing fonts from the `src/` dir](#font-face-importing-fonts-from-the-src-dir)
- [Example of setup that **won't** work](#example-of-setup-that-wont-work)
- [Possible workarounds](#possible-workarounds)
- [Implement context persistence (optional)](#implement-context-persistence-optional)
- [Kickstart video](#kickstart-video)
- [FTL errors related to `ftl_object_to_js_code_declaring_an_object` in Keycloak logs.](#ftl-errors-related-to-ftl_object_to_js_code_declaring_an_object-in-keycloak-logs)
- [Adding custom message (to `i18n/useKcMessage.tsx`)](#adding-custom-message-to-i18nusekcmessagetsx)
- [Email domain whitelist](#email-domain-whitelist)
- [Changelog highlights](#changelog-highlights)
- [v4](#v4)
- [v3](#v3)
- [v2.5](#v25)
- [v2](#v2)
# Requirements
On Windows OS you'll have to use [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10). More info [here](https://github.com/InseeFrLab/keycloakify/issues/54%23issuecomment-984834217)
Tested with the following Keycloak versions:
- [11.0.3](https://hub.docker.com/layers/jboss/keycloak/11.0.3/images/sha256-4438f1e51c1369371cb807dffa526e1208086b3ebb9cab009830a178de949782?context=explore)
- [12.0.4](https://hub.docker.com/layers/jboss/keycloak/12.0.4/images/sha256-67e0c88e69bd0c7aef972c40bdeb558a974013a28b3668ca790ed63a04d70584?context=explore)
- [15.0.2](https://hub.docker.com/layers/jboss/keycloak/15.0.2/images/sha256-d8ed1ee5df42a178c341f924377da75db49eab08ea9f058ff39a8ed7ee05ec93?context=explore)
- [16.1.0](https://hub.docker.com/layers/jboss/keycloak/16.1.0/images/sha256-6ecb9492224c6cfbb55d43f64a5ab634145d8cc1eba14eae8c37e3afde89546e?context=explore)
This tool will be maintained to stay compatible with Keycloak v11 and up, however, the default pages you will get
(before you customize it) will always be the ones of Keycloak v11.
This tool assumes you are bundling your app with Webpack (tested with the versions that ships with CRA v4.44.2 and v5.0.0) .
It assumes there is a `build/` directory at the root of your react project directory containing a `index.html` file
and a `build/static/` directory generated by webpack.
For more information see [this issue](https://github.com/InseeFrLab/keycloakify/issues/5#issuecomment-832296432)
**All this is defaults with [`create-react-app`](https://create-react-app.dev)** (tested with 4.0.3)
- `mvn` ([Maven](https://maven.apache.org/)), `rm`, `mkdir`, `curl`, `unzip` are assumed to be available.
- `docker` must be up and running when running `start_keycloak_testing_container.sh` (Instructions provided after running `yarn keycloak`).
## My framework doesnt seem to be supported, what can I do?
Currently Keycloakify is only compatible with `create-react-app` apps.
It doesnt mean that you can't use Keycloakify if you are using Next.js, Express or any other
framework that involves SSR but your Keycloak theme will need to be a standalone project.
Find specific instructions about how to get started [**here**](https://github.com/garronej/keycloakify-demo-app#keycloak-theme-only).
To share your styles between your main app and your login pages you will need to externalize your design system by making it a
separate module. Checkout [ts_ci](https://github.com/garronej/ts_ci), it can help with that.
# How to use
## Setting up the build tool
```bash
yarn add keycloakify @emotion/react
```
[`package.json`](https://github.com/garronej/keycloakify-demo-app/blob/main/package.json)
```json
"scripts": {
"keycloak": "yarn build && build-keycloak-theme",
}
```
```bash
yarn keycloak # generates keycloak-theme.jar
```
On the console will be printed all the instructions about how to load the generated theme in Keycloak
### Changing just the look of the default Keycloak theme
The first approach is to only customize the style of the default Keycloak login by providing
your own class names.
If you have created a new React project specifically to create a Keycloak theme and nothing else then
your index should look something like:
`src/index.tsx`
```tsx
import { App } from "./<wherever>/App";
import { KcApp, defaultKcProps, getKcContext } from "keycloakify";
import { css } from "tss-react/@emotion/css";
const { kcContext } = getKcContext();
const myClassName = css({ "color": "red" });
reactDom.render(
<KcApp
kcContext={kcContext}
{...{
...defaultKcProps,
"kcHeaderWrapperClass": myClassName,
}}
/>,
document.getElementById("root"),
);
```
If you share a unique project for your app and the Keycloak theme, your index should look
more like this:
`src/index.tsx`
```tsx
import { App } from "./<wherever>/App";
import { KcApp, defaultKcProps, getKcContext } from "keycloakify";
import { css } from "tss-react/@emotion/css";
const { kcContext } = getKcContext();
const myClassName = css({ "color": "red" });
reactDom.render(
// Unless the app is currently being served by Keycloak
// kcContext is undefined.
kcContext !== undefined ? (
<KcApp
kcContext={kcContext}
{...{
...defaultKcProps,
"kcHeaderWrapperClass": myClassName,
}}
/>
) : (
<App />
), // Your actual app
document.getElementById("root"),
);
```
<p align="center">
<i>result:</i></br>
<img src="https://user-images.githubusercontent.com/6702424/114326299-6892fc00-9b34-11eb-8d75-85696e55458f.png">
</p>
Example of a customization using only CSS: [here](https://github.com/InseeFrLab/onyxia-web/blob/012639d62327a9a56be80c46e32c32c9497b82db/src/app/components/KcApp.tsx)
(the [index.tsx](https://github.com/InseeFrLab/onyxia-web/blob/012639d62327a9a56be80c46e32c32c9497b82db/src/app/index.tsx#L89-L94) )
and the result you can expect:
<p align="center">
<i> <a href="https://datalab.sspcloud.fr">Customization using only CSS:</a> </i>
<br>
<img src="https://github.com/InseeFrLab/keycloakify/releases/download/v0.3.8/keycloakify_after.gif">
</p>
### Advanced pages configuration
If you want to go beyond only customizing the CSS you can re-implement some of the
pages or even add new ones.
If you want to go this way checkout the demo setup provided [here](https://github.com/garronej/keycloakify-demo-app/tree/look_and_feel).
If you prefer a real life example you can checkout [onyxia-web's source](https://github.com/InseeFrLab/onyxia-web/tree/main/src/ui/components/KcApp).
The web app is in production [here](https://datalab.sspcloud.fr).
Main takeaways are:
- You must declare your custom pages in the package.json. [example](https://github.com/garronej/keycloakify-demo-app/blob/4eb2a9f63e9823e653b2d439495bda55e5ecc134/package.json#L17-L22)
- (TS only) You must declare theses page in the type argument of the getter
function for the `kcContext` in order to have the correct typings. [example](https://github.com/garronej/keycloakify-demo-app/blob/4eb2a9f63e9823e653b2d439495bda55e5ecc134/src/KcApp/kcContext.ts#L16-L21)
- (TS only) If you use Keycloak plugins that defines non standard `.ftl` values
(Like for example [this plugin](https://github.com/micedre/keycloak-mail-whitelisting)
that define `authorizedMailDomains` in `register.ftl`) you should
declare theses value to get the type. [example](https://github.com/garronej/keycloakify-demo-app/blob/4eb2a9f63e9823e653b2d439495bda55e5ecc134/src/KcApp/kcContext.ts#L6-L13)
- You should provide sample data for all the non standard value if you want to be able
to debug the page outside of keycloak. [example](https://github.com/garronej/keycloakify-demo-app/blob/4eb2a9f63e9823e653b2d439495bda55e5ecc134/src/KcApp/kcContext.ts#L28-L43)
WARNING: If you chose to go this way use:
```json
"dependencies": {
"keycloakify": "~X.Y.Z"
}
```
### Hot reload
Rebuild the theme each time you make a change to see the result is not practical.
If you want to test your login screens outside of Keycloak you can mock a given `kcContext`:
```tsx
import {
KcApp,
defaultKcProps,
getKcContext
} from "keycloakify";
const { kcContext } = getKcContext({
"mockPageId": "login.ftl"
});
reactDom.render(
<KcApp
kcContext={kcContextMocks.kcLoginContext}
{...defaultKcProps}
/>
document.getElementById("root")
);
```
Then `yarn start`, you will see your login page.
Checkout [this concrete example](https://github.com/garronej/keycloakify-demo-app/blob/main/src/index.tsx)
## Enable loading in a blink of an eye of login pages ⚡ (--external-assets)
By default the theme generated is standalone. Meaning that when your users
reach the login pages all scripts, images and stylesheet are downloaded from the Keycloak server.
If you are specifically building a theme to integrate with an app or a website that allows users
to first browse unauthenticated before logging in, you will get a significant
performance boost if you jump through those hoops:
- Provide the url of your app in the `homepage` field of package.json. [ex](https://github.com/garronej/keycloakify-demo-app/blob/7847cc70ef374ab26a6cc7953461cf25603e9a6d/package.json#L2) or in a `public/CNAME` file. [ex](https://github.com/garronej/keycloakify-demo-app/blob/main/public/CNAME).
- Build the theme using `npx build-keycloak-theme --external-assets` [ex](https://github.com/garronej/keycloakify-demo-app/blob/7847cc70ef374ab26a6cc7953461cf25603e9a6d/.github/workflows/ci.yaml#L21)
- Enable [long-term assets caching](https://create-react-app.dev/docs/production-build/#static-file-caching) on the server hosting your app.
- Make sure not to build your app and the keycloak theme separately
and remember to update the Keycloak theme every time you update your app.
- Be mindful that if your app is down your login pages are down as well.
Checkout a complete setup [here](https://github.com/garronej/keycloakify-demo-app#about-keycloakify)
# User profile and frontend form validation
<p align="center">
<a href="https://github.com/InseeFrLab/keycloakify/releases/download/v0.0.1/keycloakify_fontend_validation.mp4">
<img src="https://user-images.githubusercontent.com/6702424/138880146-6fef3280-c4a5-46d2-bbb3-8b9598c057a5.gif">
</a>
</p>
NOTE: In reality the regexp used in this gif doesn't work server side, the regexp pattern should be `^[^@]@gmail\.com$` 😬.
User Profile is a Keycloak feature that enables to
[define, from the admin console](https://user-images.githubusercontent.com/6702424/136872461-1f5b64ef-d2ef-4c6b-bb8d-07d4729552b3.png),
what information you want to collect on your users in the register page and to validate inputs
[**on the frontend**, in realtime](https://github.com/InseeFrLab/keycloakify/blob/6dca6a93d8cfe634ee4d8574ad0c091641220092/src/lib/getKcContext/KcContextBase.ts#L225-L261)!
NOTE: User profile is only available in Keycloak 15 and it's a beta feature that
[needs to be enabled when launching keycloak](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/bin/build-keycloak-theme/build-keycloak-theme.ts#L116-L117)
and [enabled in the console](https://user-images.githubusercontent.com/6702424/136874428-b071d614-c7f7-440d-9b2e-670faadc0871.png).
Keycloakify, in [`register-user-profile.ftl`](https://github.com/InseeFrLab/keycloakify/blob/main/src/lib/components/RegisterUserProfile.tsx),
provides frontend validation out of the box.
For implementing your own `register-user-profile.ftl` page, you can use [`import { useFormValidationSlice } from "keycloakify";`](https://github.com/InseeFrLab/keycloakify/blob/main/src/lib/useFormValidationSlice.tsx).
Find usage example [`here`](https://github.com/InseeFrLab/keycloakify/blob/d3a07edfcb3739e30032dc96fc2a55944dfc3387/src/lib/components/RegisterUserProfile.tsx#L79-L112).
As for right now [it's not possible to define a pattern for the password](https://keycloak.discourse.group/t/make-password-policies-available-to-freemarker/11632)
from the admin console. You can however pass validators for it to the `useFormValidationSlice` function.
# Support for Terms and conditions
[Many organizations have a requirement that when a new user logs in for the first time, they need to agree to the terms and conditions of the website.](https://www.keycloak.org/docs/4.8/server_admin/#terms-and-conditions).
First you need to enable the required action on the Keycloak server admin console:
![image](https://user-images.githubusercontent.com/6702424/114280501-dad2e600-9a39-11eb-9c39-a225572dd38a.png)
Then to load your own therms of services using [like this](https://github.com/garronej/keycloakify-demo-app/blob/8168c928a66605f2464f9bd28a4dc85fb0a231f9/src/index.tsx#L42-L66).
# Some pages still have the default theme. Why?
This project only support out of the box the most common user facing pages of Keycloak login.
[Here](https://user-images.githubusercontent.com/6702424/116787906-227fe700-aaa7-11eb-92ee-22e7673717c2.png) is the complete list of pages (you get them after running `yarn test`)
and [here](https://github.com/InseeFrLab/keycloakify/tree/main/src/lib/components) are the pages currently implemented by this module.
If you need to customize pages that are not supported yet or if you need to implement some non standard `.ftl` pages please refer to [Advanced pages configuration](#advanced-pages-configuration).
# GitHub Actions
![image](https://user-images.githubusercontent.com/6702424/114286938-47aea600-9a63-11eb-936e-17159e8826e8.png)
[Here is a demo repo](https://github.com/garronej/keycloakify-demo-app) to show how to automate
the building and publishing of the theme (the .jar file).
# Limitations
## `process.env.PUBLIC_URL` not supported.
You won't be able to [import things from your public directory **in your JavaScript code**](https://create-react-app.dev/docs/using-the-public-folder/#adding-assets-outside-of-the-module-system).
(This isn't recommended anyway).
## `@font-face` importing fonts from the `src/` dir
If you are building the theme with [--external-assets](#enable-loading-in-a-blink-of-a-eye-of-login-pages-)
this limitation doesn't apply, you can import fonts however you see fit.
### Example of setup that **won't** work
- We have a `fonts/` directory in `src/`
- We import the font like this [`src: url("/fonts/my-font.woff2") format("woff2");`](https://github.com/garronej/keycloakify-demo-app/blob/07d54a3012ef354ee12b1374c6f7ad1cb125d56b/src/fonts.scss#L4) in a `.scss` a file.
### Possible workarounds
- Use [`--external-assets`](#enable-loading-in-a-blink-of-a-eye-of-login-pages-).
- If it is possible, use Google Fonts or any other font provider.
- If you want to host your font recommended approach is to move your fonts into the `public`
directory and to place your `@font-face` statements in the `public/index.html`.
Example [here](https://github.com/garronej/keycloakify-demo-app/blob/9aa2dbaec28a7786d6b2983c9a59d393dec1b2d6/public/index.html#L27-L73)
(and the font are [here](https://github.com/garronej/keycloakify-demo-app/tree/main/public/fonts/WorkSans)).
- You can also [use non relative url](https://github.com/garronej/keycloakify-demo-app/blob/2de8a9eb6f5de9c94f9cd3991faad0377e63268c/src/fonts.scss#L16) but don't forget [`Access-Control-Allow-Origin`](https://github.com/garronej/keycloakify-demo-app/blob/2de8a9eb6f5de9c94f9cd3991faad0377e63268c/nginx.conf#L17-L19).
# Implement context persistence (optional)
If, before logging in, a user has selected a specific language
you don't want it to be reset to default when the user gets redirected to
the login or register pages.
Same goes for the dark mode, you don't want, if the user had it enabled
to show the login page with light themes.
The problem is that you are probably using `localStorage` to persist theses values across
reload but, as the Keycloak pages are not served on the same domain that the rest of your
app you won't be able to carry over states using `localStorage`.
The only reliable solution is to inject parameters into the URL before
redirecting to Keycloak. We integrate with
[`keycloak-js`](https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/javascript-adapter.adoc),
by providing you a way to tell `keycloak-js` that you would like to inject
some search parameters before redirecting.
The method also works with [`@react-keycloak/web`](https://www.npmjs.com/package/@react-keycloak/web) (use the `initOptions`).
You can implement your own mechanism to pass the states in the URL and
restore it on the other side but we recommend using `powerhooks/useGlobalState`
from the library [`powerhooks`](https://www.powerhooks.dev) that provide an elegant
way to handle states such as `isDarkModeEnabled` or `selectedLanguage`.
Let's modify [the example](https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/javascript-adapter.adoc) from the official `keycloak-js` documentation to
enables the states of `useGlobalStates` to be injected in the URL before redirecting.
Note that the states are automatically restored on the other side by `powerhooks`
```typescript
import keycloak_js from "keycloak-js";
import { injectGlobalStatesInSearchParams } from "powerhooks/useGlobalState";
import { createKeycloakAdapter } from "keycloakify";
//...
const keycloakInstance = keycloak_js({
"url": "http://keycloak-server/auth",
"realm": "myrealm",
"clientId": "myapp",
});
keycloakInstance.init({
"onLoad": "check-sso",
"silentCheckSsoRedirectUri": window.location.origin + "/silent-check-sso.html",
"adapter": createKeycloakAdapter({
"transformUrlBeforeRedirect": injectGlobalStatesInSearchParams,
keycloakInstance,
}),
});
//...
```
If you really want to go the extra miles and avoid having the white
flash of the blank html before the js bundle have been evaluated
[here is a snippet](https://github.com/InseeFrLab/onyxia-web/blob/e1c1f309aaa3d5f860df39ba0b75cce89c88a9de/public/index.html#L117-L166) that you can place in your `public/index.html` if you are using `powerhooks/useGlobalState`.
# Kickstart video
_NOTE: keycloak-react-theming was renamed keycloakify since this video was recorded_
[![kickstart_video](https://user-images.githubusercontent.com/6702424/108877866-f146ee80-75ff-11eb-8120-003b3c5f6dd8.png)](https://youtu.be/xTz0Rj7i2v8)
# FTL errors related to `ftl_object_to_js_code_declaring_an_object` in Keycloak logs.
If you ever encounter one of these errors:
```log
FTL stack trace ("~" means nesting-related):
- Failed at: #local value = object[key] [in template "login.ftl" in macro "ftl_object_to_js_code_declaring_an_object" at line 70, column 21]
- Reached through: @compress [in template "login.ftl" in macro "ftl_object_to_js_code_declaring_an_object" at line 36, column 5]
- Reached through: @ftl_object_to_js_code_declaring_an_object object=value depth=(dep... [in template "login.ftl" in macro "ftl_object_to_js_code_declaring_an_object" at line 81, column 27]
- Reached through: @compress [in template "login.ftl" in macro "ftl_object_to_js_code_declaring_an_object" at line 36, column 5]
- Reached through: @ftl_object_to_js_code_declaring_an_object object=(.data_model) de... [in template "login.ftl" at line 163, column 43]
```
It's just noise, they can be safely ignored.
You can, however, and are encouraged to, report any that you would spot.
Just open an issue about it and I will release a patched version of Keycloakify in the better delays.
# Adding custom message (to `i18n/useKcMessage.tsx`)
You can reproduce [this approach](https://github.com/garronej/keycloakify-demo-app/blob/main/src/kcMessagesExtension.ts)
( don't forget to [evaluate the code](https://github.com/garronej/keycloakify-demo-app/blob/0a6d349dba89a5702f98ba48bca6c76ac7265e1f/src/index.tsx#L15) ).
This approach is a bit hacky as it doesn't provide type safety but it works.
# Email domain whitelist
NOTE: This have been kind of deprecated by [user attribute](#user-profile-and-frontend-form-validation) you could
use a pattern [like this one](https://github.com/InseeFrLab/onyxia-web/blob/f1206e0329b3b8d401ca7bffa95ca9c213cb190a/src/app/components/KcApp/kcContext.ts#L106) to whitelist email domains.
If you want to restrict the emails domain that can register, you can use [this plugin](https://github.com/micedre/keycloak-mail-whitelisting)
and `kcRegisterContext["authorizedMailDomains"]` to validate on.
> 🗣 V6 have been released 🎉
> [It features major improvements](https://github.com/InseeFrLab/keycloakify#600).
> Checkout [the migration guide](https://docs.keycloakify.dev/v5-to-v6).
# Changelog highlights
# v4.7.4
## 6.13
- Build work behind corporate proxies, [see issue](https://github.com/InseeFrLab/keycloakify/issues/257).
## 6.12
Massive improvement in the developer experience:
- There is now only one starter repo: https://github.com/codegouvfr/keycloakify-starter
- A lot of comments have been added in the code of the starter to make it easier to get started.
- The doc has been updated: https://docs.keycloakify.dev
- A lot of improvements in the type system.
## 6.11.4
- You no longer need to have Maven installed to build the theme. Thanks to @lordvlad, [see PR](https://github.com/InseeFrLab/keycloakify/pull/239).
- Feature new build options: [`bundler`](https://docs.keycloakify.dev/build-options#keycloakify.bundler), [`groupId`](https://docs.keycloakify.dev/build-options#keycloakify.groupid), [`artifactId`](https://docs.keycloakify.dev/build-options#keycloakify.artifactid), [`version`](https://docs.keycloakify.dev/build-options#version).
Theses options can be user to customize the output name of the .jar. You can use environnement variables to overrides the values read in the package.json. Thanks to @lordvlad.
## 6.10.0
- Widows compat (thanks to @lordvlad, [see PR](https://github.com/InseeFrLab/keycloakify/pull/226)). WSL is no longer required 🎉
## 6.8.4
- `@emotion/react` is no longer a peer dependency of Keycloakify.
## 6.8.0
- It is now possible to pass a custom `<Template />` component as a prop to `<KcApp />` and every
individual page (`<Login />`, `<RegisterUserProfile />`, ...) it enables to customize only the header and footer for
example without having to switch to a full-component level customization. [See issue](https://github.com/InseeFrLab/keycloakify/issues/191).
## 6.7.0
- Add support for `webauthn-authenticate.ftl` thanks to [@mstrodl](https://github.com/Mstrodl)'s hacktoberfest [PR](https://github.com/InseeFrLab/keycloakify/pull/185).
## 6.6.0
- Add support for `login-password.ftl` thanks to [@mstrodl](https://github.com/Mstrodl)'s hacktoberfest [PR](https://github.com/InseeFrLab/keycloakify/pull/184).
## 6.5.0
- Add support for `login-username.ftl` thanks to [@mstrodl](https://github.com/Mstrodl)'s hacktoberfest [PR](https://github.com/InseeFrLab/keycloakify/pull/183).
## 6.4.0
- You can now optionally pass a `doFetchDefaultThemeResources: boolean` prop to every page component and the default `<KcApp />`
This enables you to prevent the default CSS and JS that comes with the builtin Keycloak theme to be downloaded.
You'll get [a black slate](https://user-images.githubusercontent.com/6702424/192619083-4baa5df4-4a21-4ec7-8e28-d200d1208299.png).
## 6.0.0
- Bundle size drastically reduced, locals and component dynamically loaded.
- First print much quicker, use of React.lazy() everywhere.
- Real i18n API.
- Actual documentation for build options.
Checkout [the migration guide](https://docs.keycloakify.dev/v5-to-v6)
## 5.8.0
- [React.lazy()](https://reactjs.org/docs/code-splitting.html#reactlazy) support 🎉. [#141](https://github.com/InseeFrLab/keycloakify/issues/141)
## 5.7.0
- Feat `logout-confirm.ftl`. [PR](https://github.com/InseeFrLab/keycloakify/pull/120)
## 5.6.4
Fix `login-verify-email.ftl` page. [Before](https://user-images.githubusercontent.com/6702424/177436014-0bad22c4-5bfb-45bb-8fc9-dad65143cd0c.png) - [After](https://user-images.githubusercontent.com/6702424/177435797-ec5d7db3-84cf-49cb-8efc-3427a81f744e.png)
## v5.6.0
Add support for `login-config-totp.ftl` page [#127](https://github.com/InseeFrLab/keycloakify/pull/127).
## v5.3.0
Rename `keycloak_theme_email` to `keycloak_email`.
If you already had a `keycloak_theme_email` you should rename it `keycloak_email`.
## v5.0.0
[Migration guide](https://github.com/garronej/keycloakify-demo-app/blob/a5b6a50f24bc25e082931f5ad9ebf47492acd12a/src/index.tsx#L46-L63)
New i18n system.
Import of terms and services have changed. [See example](https://github.com/garronej/keycloakify-demo-app/blob/a5b6a50f24bc25e082931f5ad9ebf47492acd12a/src/index.tsx#L46-L63).
## v4.10.0
Add `login-idp-link-email.ftl` page [See PR](https://github.com/InseeFrLab/keycloakify/pull/92).
## v4.8.0
[Email template customization.](#email-template-customization)
## v4.7.4
**M1 Mac** support (for testing locally with a dockerized Keycloak).
# v4.7.2
## v4.7.2
> WARNING: This is broken.
> Testing with local Keycloak container working with M1 Mac. Thanks to [@eduardosanzb](https://github.com/InseeFrLab/keycloakify/issues/43#issuecomment-975699658).
> Be aware: When running M1s you are testing with Keycloak v15 else the local container spun will be a Keycloak v16.1.0.
# v4.7.0
## v4.7.0
Register with user profile enabled: Out of the box `options` validator support.
[Example](https://user-images.githubusercontent.com/6702424/158911163-81e6bbe8-feb0-4dc8-abff-de199d7a678e.mov)
# v4.6.0
## v4.6.0
`tss-react` and `powerhooks` are no longer peer dependencies of `keycloakify`.
After updating Keycloakify you can remove `tss-react` and `powerhooks` from your dependencies if you don't use them explicitly.

59
package.json Executable file → Normal file
View File

@ -1,7 +1,7 @@
{
"name": "keycloakify",
"version": "4.7.5",
"description": "Keycloak theme generator for Reacts app",
"version": "6.13.2",
"description": "Create Keycloak themes using React",
"repository": {
"type": "git",
"url": "git://github.com/garronej/keycloakify.git"
@ -9,19 +9,23 @@
"main": "dist/lib/index.js",
"types": "dist/lib/index.d.ts",
"scripts": {
"clean": "rimraf dist/",
"build": "yarn clean && tsc && yarn grant-exec-perms && yarn copy-files",
"build": "rimraf dist/ && tsc -p src/bin && tsc -p src/lib && yarn grant-exec-perms && yarn copy-files dist/",
"build:test": "rimraf dist_test/ && tsc -p src/test && yarn copy-files dist_test/",
"grant-exec-perms": "node dist/bin/tools/grant-exec-perms.js",
"test": "node dist/test/bin/main && node dist/test/lib",
"copy-files": "copyfiles -u 1 src/**/*.ftl src/**/*.xml src/**/*.js dist/",
"generate-messages": "node dist/bin/generate-i18n-messages.js",
"link_in_test_app": "node dist/bin/link_in_test_app.js",
"copy-files": "copyfiles -u 1 src/**/*.ftl",
"pretest": "yarn build:test",
"test": "node dist_test/test/bin && node dist_test/test/lib",
"_format": "prettier '**/*.{ts,tsx,json,md}'",
"format": "yarn _format --write",
"format:check": "yarn _format --list-different"
"format:check": "yarn _format --list-different",
"generate-messages": "ts-node --skipProject src/scripts/generate-i18n-messages.ts",
"link-in-app": "ts-node --skipProject src/scripts/link-in-app.ts",
"link-in-starter": "yarn link-in-app keycloakify-starter",
"tsc-watch": "tsc -p src/bin -w & tsc -p src/lib -w "
},
"bin": {
"build-keycloak-theme": "dist/bin/build-keycloak-theme/index.js",
"keycloakify": "dist/bin/keycloakify/index.js",
"create-keycloak-email-directory": "dist/bin/create-keycloak-email-directory.js",
"download-builtin-keycloak-theme": "dist/bin/download-builtin-keycloak-theme.js"
},
"lint-staged": {
@ -38,9 +42,8 @@
"license": "MIT",
"files": [
"src/",
"!src/test/",
"!src/scripts",
"dist/",
"!dist/test/",
"!dist/tsconfig.tsbuildinfo"
],
"keywords": [
@ -55,31 +58,37 @@
],
"homepage": "https://github.com/garronej/keycloakify",
"peerDependencies": {
"@emotion/react": "^11.4.1",
"react": "^16.8.0 || ^17.0.0"
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"devDependencies": {
"@emotion/react": "^11.4.1",
"@types/node": "^10.0.0",
"@types/react": "^17.0.0",
"@babel/core": "^7.0.0",
"@types/make-fetch-happen": "^10.0.1",
"@types/minimist": "^1.2.2",
"@types/node": "^18.14.1",
"@types/react": "18.0.9",
"copyfiles": "^2.4.1",
"husky": "^4.3.8",
"lint-staged": "^11.0.0",
"prettier": "^2.3.0",
"properties-parser": "^0.3.1",
"react": "^17.0.1",
"react": "18.1.0",
"rimraf": "^3.0.2",
"typescript": "^4.2.3"
"scripting-tools": "^0.19.13",
"ts-node": "^10.9.1",
"typescript": "^4.9.5"
},
"dependencies": {
"@octokit/rest": "^18.12.0",
"cheerio": "^1.0.0-rc.5",
"evt": "2.0.0-beta.39",
"minimal-polyfills": "^2.2.1",
"cli-select": "^1.1.2",
"evt": "^2.4.18",
"make-fetch-happen": "^11.0.3",
"minimal-polyfills": "^2.2.2",
"minimist": "^1.2.6",
"path-browserify": "^1.0.1",
"react-markdown": "^5.0.3",
"scripting-tools": "^0.19.13",
"tsafe": "^0.9.0",
"tss-react": "^3.5.2",
"powerhooks": "^0.14.0"
"rfc4648": "^1.5.2",
"tsafe": "^1.6.0",
"zod": "^3.17.10"
}
}

27
renovate.json Normal file
View File

@ -0,0 +1,27 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"baseBranches": ["main", "landingpage"],
"extends": ["config:base"],
"dependencyDashboard": false,
"bumpVersion": "patch",
"rangeStrategy": "bump",
"ignorePaths": [".github/**"],
"branchPrefix": "renovate_",
"vulnerabilityAlerts": {
"enabled": false
},
"packageRules": [
{
"packagePatterns": ["*"],
"excludePackagePatterns": ["tsafe", "evt"],
"enabled": false
},
{
"packagePatterns": ["tsafe", "evt"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true,
"automergeType": "branch",
"groupName": "garronej_modules_update"
}
]
}

View File

@ -1,147 +0,0 @@
import { generateKeycloakThemeResources } from "./generateKeycloakThemeResources";
import { generateJavaStackFiles } from "./generateJavaStackFiles";
import { join as pathJoin, relative as pathRelative, basename as pathBasename } from "path";
import * as child_process from "child_process";
import { generateStartKeycloakTestingContainer } from "./generateStartKeycloakTestingContainer";
import { URL } from "url";
import * as fs from "fs";
type ParsedPackageJson = {
name: string;
version: string;
homepage?: string;
};
const reactProjectDirPath = process.cwd();
const doUseExternalAssets = process.argv[2]?.toLowerCase() === "--external-assets";
const parsedPackageJson: ParsedPackageJson = require(pathJoin(reactProjectDirPath, "package.json"));
export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak");
function sanitizeThemeName(name: string) {
return name
.replace(/^@(.*)/, "$1")
.split("/")
.join("-");
}
export function main() {
console.log("🔏 Building the keycloak theme...⌚");
const extraPagesId: string[] = (parsedPackageJson as any)["keycloakify"]?.["extraPages"] ?? [];
const extraThemeProperties: string[] = (parsedPackageJson as any)["keycloakify"]?.["extraThemeProperties"] ?? [];
const themeName = sanitizeThemeName(parsedPackageJson.name);
generateKeycloakThemeResources({
keycloakThemeBuildingDirPath,
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
themeName,
...(() => {
const url = (() => {
const { homepage } = parsedPackageJson;
if (homepage !== undefined) {
return new URL(homepage);
}
const cnameFilePath = pathJoin(reactProjectDirPath, "public", "CNAME");
if (fs.existsSync(cnameFilePath)) {
return new URL(`https://${fs.readFileSync(cnameFilePath).toString("utf8").replace(/\s+$/, "")}`);
}
return undefined;
})();
return {
"urlPathname": url === undefined ? "/" : url.pathname.replace(/([^/])$/, "$1/"),
"urlOrigin": !doUseExternalAssets
? undefined
: (() => {
if (url === undefined) {
console.error("ERROR: You must specify 'homepage' in your package.json");
process.exit(-1);
}
return url.origin;
})(),
};
})(),
extraPagesId,
extraThemeProperties,
//We have to leave it at that otherwise we break our default theme.
//Problem is that we can't guarantee that the the old resources
//will still be available on the newer keycloak version.
"keycloakVersion": "11.0.3",
});
const { jarFilePath } = generateJavaStackFiles({
version: parsedPackageJson.version,
themeName,
homepage: parsedPackageJson.homepage,
keycloakThemeBuildingDirPath,
});
child_process.execSync("mvn package", {
"cwd": keycloakThemeBuildingDirPath,
});
generateStartKeycloakTestingContainer({
keycloakThemeBuildingDirPath,
themeName,
//We want, however to test in a container running the latest Keycloak version
"keycloakVersion": "17.0.1",
});
console.log(
[
"",
`✅ Your keycloak theme has been generated and bundled into ./${pathRelative(reactProjectDirPath, jarFilePath)} 🚀`,
`It is to be placed in "/opt/jboss/keycloak/standalone/deployments" in the container running a jboss/keycloak Docker image.`,
"",
"Using Helm (https://github.com/codecentric/helm-charts), edit to reflect:",
"",
"value.yaml: ",
" extraInitContainers: |",
" - name: realm-ext-provider",
" image: curlimages/curl",
" imagePullPolicy: IfNotPresent",
" command:",
" - sh",
" args:",
" - -c",
` - curl -L -f -S -o /extensions/${pathBasename(jarFilePath)} https://AN.URL.FOR/${pathBasename(jarFilePath)}`,
" volumeMounts:",
" - name: extensions",
" mountPath: /extensions",
" ",
" extraVolumeMounts: |",
" - name: extensions",
" mountPath: /opt/jboss/keycloak/standalone/deployments",
" extraEnv: |",
" - name: KEYCLOAK_USER",
" value: admin",
" - name: KEYCLOAK_PASSWORD",
" value: xxxxxxxxx",
" - name: JAVA_OPTS",
" value: -Dkeycloak.profile=preview",
"",
"",
"To test your theme locally, with hot reloading, you can spin up a Keycloak container image with the theme loaded by running:",
"",
`👉 $ ./${pathRelative(reactProjectDirPath, pathJoin(keycloakThemeBuildingDirPath, generateStartKeycloakTestingContainer.basename))} 👈`,
"",
"Once your container is up and running: ",
"- Log into the admin console 👉 http://localhost:8080/admin username: admin, password: admin 👈",
'- Create a realm named "myrealm"',
'- Create a client with id "myclient" and root url: "https://www.keycloak.org/app/"',
`- Select Login Theme: ${themeName} (don't forget to save at the bottom of the page)`,
`- Go to 👉 https://www.keycloak.org/app/ 👈 Click "Save" then "Sign in". You should see your login page`,
"",
"Video demoing this process: https://youtu.be/N3wlBoH4hKg",
"",
].join("\n"),
);
}

View File

@ -1,135 +0,0 @@
import cheerio from "cheerio";
import { replaceImportsFromStaticInJsCode, replaceImportsInInlineCssCode, generateCssCodeToDefineGlobals } from "../replaceImportFromStatic";
import fs from "fs";
import { join as pathJoin } from "path";
import { objectKeys } from "tsafe/objectKeys";
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
export const pageIds = [
"login.ftl",
"register.ftl",
"register-user-profile.ftl",
"info.ftl",
"error.ftl",
"login-reset-password.ftl",
"login-verify-email.ftl",
"terms.ftl",
"login-otp.ftl",
"login-update-profile.ftl",
"login-update-password.ftl",
"login-idp-link-confirm.ftl",
"login-page-expired.ftl",
] as const;
export type PageId = typeof pageIds[number];
export function generateFtlFilesCodeFactory(params: {
cssGlobalsToDefine: Record<string, string>;
indexHtmlCode: string;
urlPathname: string;
urlOrigin: undefined | string;
}) {
const { cssGlobalsToDefine, indexHtmlCode, urlPathname, urlOrigin } = params;
const $ = cheerio.load(indexHtmlCode);
$("script:not([src])").each((...[, element]) => {
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"jsCode": $(element).html()!,
urlOrigin,
});
$(element).text(fixedJsCode);
});
$("style").each((...[, element]) => {
const { fixedCssCode } = replaceImportsInInlineCssCode({
"cssCode": $(element).html()!,
"urlPathname": params.urlPathname,
urlOrigin,
});
$(element).text(fixedCssCode);
});
(
[
["link", "href"],
["script", "src"],
] as const
).forEach(([selector, attrName]) =>
$(selector).each((...[, element]) => {
const href = $(element).attr(attrName);
if (href === undefined) {
return;
}
$(element).attr(
attrName,
urlOrigin !== undefined
? href.replace(/^\//, `${urlOrigin}/`)
: href.replace(new RegExp(`^${urlPathname.replace(/\//g, "\\/")}`), "${url.resourcesPath}/build/"),
);
}),
);
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
const replaceValueBySearchValue = {
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': fs
.readFileSync(pathJoin(__dirname, "ftl_object_to_js_code_declaring_an_object.ftl"))
.toString("utf8")
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1],
"<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->": [
"<#if scripts??>",
" <#list scripts as script>",
' <script src="${script}" type="text/javascript"></script>',
" </#list>",
"</#if>",
].join("\n"),
};
$("head").prepend(
[
...(Object.keys(cssGlobalsToDefine).length === 0
? []
: [
"",
"<style>",
generateCssCodeToDefineGlobals({
cssGlobalsToDefine,
urlPathname,
}).cssCodeToPrependInHead,
"</style>",
"",
]),
"<script>",
` window.${ftlValuesGlobalName}= ${objectKeys(replaceValueBySearchValue)[0]};`,
"</script>",
"",
objectKeys(replaceValueBySearchValue)[1],
].join("\n"),
);
const partiallyFixedIndexHtmlCode = $.html();
function generateFtlFilesCode(params: { pageId: string }): {
ftlCode: string;
} {
const { pageId } = params;
const $ = cheerio.load(partiallyFixedIndexHtmlCode);
let ftlCode = $.html();
Object.entries({
...replaceValueBySearchValue,
//If updated, don't forget to change in the ftl script as well.
"PAGE_ID_xIgLsPgGId9D8e": pageId,
}).map(([searchValue, replaceValue]) => (ftlCode = ftlCode.replace(searchValue, replaceValue)));
return { ftlCode };
}
return { generateFtlFilesCode };
}

View File

@ -1,142 +0,0 @@
import { transformCodebase } from "../tools/transformCodebase";
import * as fs from "fs";
import { join as pathJoin, basename as pathBasename } from "path";
import { replaceImportsInCssCode, replaceImportsFromStaticInJsCode } from "./replaceImportFromStatic";
import { generateFtlFilesCodeFactory, pageIds } from "./generateFtl";
import { downloadBuiltinKeycloakTheme } from "../download-builtin-keycloak-theme";
import * as child_process from "child_process";
import { resourcesCommonPath, resourcesPath, subDirOfPublicDirBasename } from "../../lib/getKcContext/kcContextMocks/urlResourcesPath";
import { isInside } from "../tools/isInside";
export function generateKeycloakThemeResources(params: {
themeName: string;
reactAppBuildDirPath: string;
keycloakThemeBuildingDirPath: string;
urlPathname: string;
//If urlOrigin is not undefined then it means --externals-assets
urlOrigin: undefined | string;
extraPagesId: string[];
extraThemeProperties: string[];
keycloakVersion: string;
}) {
const {
themeName,
reactAppBuildDirPath,
keycloakThemeBuildingDirPath,
urlPathname,
urlOrigin,
extraPagesId,
extraThemeProperties,
keycloakVersion,
} = params;
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
let allCssGlobalsToDefine: Record<string, string> = {};
transformCodebase({
"destDirPath": urlOrigin === undefined ? pathJoin(themeDirPath, "resources", "build") : reactAppBuildDirPath,
"srcDirPath": reactAppBuildDirPath,
"transformSourceCode": ({ filePath, sourceCode }) => {
//NOTE: Prevent cycles, excludes the folder we generated for debug in public/
if (
urlOrigin === undefined &&
isInside({
"dirPath": pathJoin(reactAppBuildDirPath, subDirOfPublicDirBasename),
filePath,
})
) {
return undefined;
}
if (urlOrigin === undefined && /\.css?$/i.test(filePath)) {
const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode({
"cssCode": sourceCode.toString("utf8"),
});
allCssGlobalsToDefine = {
...allCssGlobalsToDefine,
...cssGlobalsToDefine,
};
return {
"modifiedSourceCode": Buffer.from(fixedCssCode, "utf8"),
};
}
if (/\.js?$/i.test(filePath)) {
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"jsCode": sourceCode.toString("utf8"),
urlOrigin,
});
return {
"modifiedSourceCode": Buffer.from(fixedJsCode, "utf8"),
};
}
return urlOrigin === undefined ? { "modifiedSourceCode": sourceCode } : undefined;
},
});
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
"cssGlobalsToDefine": allCssGlobalsToDefine,
"indexHtmlCode": fs.readFileSync(pathJoin(reactAppBuildDirPath, "index.html")).toString("utf8"),
urlPathname,
urlOrigin,
});
[...pageIds, ...extraPagesId].forEach(pageId => {
const { ftlCode } = generateFtlFilesCode({ pageId });
fs.mkdirSync(themeDirPath, { "recursive": true });
fs.writeFileSync(pathJoin(themeDirPath, pageId), Buffer.from(ftlCode, "utf8"));
});
{
const tmpDirPath = pathJoin(themeDirPath, "..", "tmp_xxKdLpdIdLd");
downloadBuiltinKeycloakTheme({
keycloakVersion,
"destDirPath": tmpDirPath,
});
const themeResourcesDirPath = pathJoin(themeDirPath, "resources");
transformCodebase({
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "login", "resources"),
"destDirPath": themeResourcesDirPath,
});
const reactAppPublicDirPath = pathJoin(reactAppBuildDirPath, "..", "public");
transformCodebase({
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "common", "resources"),
"destDirPath": pathJoin(themeResourcesDirPath, pathBasename(resourcesCommonPath)),
});
transformCodebase({
"srcDirPath": themeResourcesDirPath,
"destDirPath": pathJoin(reactAppPublicDirPath, resourcesPath),
});
const keycloakResourcesWithinPublicDirPath = pathJoin(reactAppPublicDirPath, subDirOfPublicDirBasename);
fs.writeFileSync(
pathJoin(keycloakResourcesWithinPublicDirPath, "README.txt"),
Buffer.from(
["This is just a test folder that helps develop", "the login and register page without having to run a Keycloak container"].join(" "),
),
);
fs.writeFileSync(pathJoin(keycloakResourcesWithinPublicDirPath, ".gitignore"), Buffer.from("*", "utf8"));
child_process.execSync(`rm -r ${tmpDirPath}`);
}
fs.writeFileSync(
pathJoin(themeDirPath, "theme.properties"),
Buffer.from("parent=keycloak".concat("\n\n", extraThemeProperties.join("\n\n")), "utf8"),
);
}

View File

@ -1,44 +0,0 @@
import * as fs from "fs";
import { join as pathJoin } from "path";
generateStartKeycloakTestingContainer.basename = "start_keycloak_testing_container.sh";
const containerName = "keycloak-testing-container";
/** Files for being able to run a hot reload keycloak container */
export function generateStartKeycloakTestingContainer(params: { keycloakVersion: string; themeName: string; keycloakThemeBuildingDirPath: string }) {
const { themeName, keycloakThemeBuildingDirPath, keycloakVersion } = params;
fs.writeFileSync(
pathJoin(keycloakThemeBuildingDirPath, generateStartKeycloakTestingContainer.basename),
Buffer.from(
[
"#!/bin/bash",
"",
`docker rm ${containerName} || true`,
"",
`cd ${keycloakThemeBuildingDirPath}`,
"",
"docker run \\",
" -p 8080:8080 \\",
` --name ${containerName} \\`,
" -e KEYCLOAK_ADMIN=admin \\",
" -e KEYCLOAK_ADMIN_PASSWORD=admin \\",
" -e JAVA_OPTS=-Dkeycloak.profile=preview \\",
` -v ${pathJoin(
keycloakThemeBuildingDirPath,
"src",
"main",
"resources",
"theme",
themeName,
)}:/opt/keycloak/themes/${themeName}:rw \\`,
` -it quay.io/keycloak/keycloak:${keycloakVersion} \\`,
` start-dev`,
"",
].join("\n"),
"utf8",
),
{ "mode": 0o755 },
);
}

View File

@ -1,8 +0,0 @@
#!/usr/bin/env node
export * from "./build-keycloak-theme";
import { main } from "./build-keycloak-theme";
if (require.main === module) {
main();
}

View File

@ -1,92 +0,0 @@
import * as crypto from "crypto";
import { ftlValuesGlobalName } from "./ftlValuesGlobalName";
export function replaceImportsFromStaticInJsCode(params: { jsCode: string; urlOrigin: undefined | string }): { fixedJsCode: string } {
/*
NOTE:
When we have urlOrigin defined it means that
we are building with --external-assets
so we have to make sur that the fixed js code will run
inside and outside keycloak.
When urlOrigin isn't defined we can assume the fixedJsCode
will always run in keycloak context.
*/
const { jsCode, urlOrigin } = params;
const fixedJsCode = jsCode
.replace(/([a-z]+\.[a-z]+)\+"static\//g, (...[, group]) =>
urlOrigin === undefined
? `window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`
: `("${ftlValuesGlobalName}" in window ? "${urlOrigin}" : "") + ${group} + "static/`,
)
.replace(/".chunk.css",([a-z])+=([a-z]+\.[a-z]+)\+([a-z]+),/, (...[, group1, group2, group3]) =>
urlOrigin === undefined
? `".chunk.css",${group1} = window.${ftlValuesGlobalName}.url.resourcesPath + "/build/" + ${group3},`
: `".chunk.css",${group1} = ("${ftlValuesGlobalName}" in window ? "${urlOrigin}" : "") + ${group2} + ${group3},`,
);
return { fixedJsCode };
}
export function replaceImportsInInlineCssCode(params: { cssCode: string; urlPathname: string; urlOrigin: undefined | string }): {
fixedCssCode: string;
} {
const { cssCode, urlPathname, urlOrigin } = params;
const fixedCssCode = cssCode.replace(
urlPathname === "/" ? /url\(["']?\/([^/][^)"']+)["']?\)/g : new RegExp(`url\\(["']?${urlPathname}([^)"']+)["']?\\)`, "g"),
(...[, group]) => `url(${urlOrigin === undefined ? "${url.resourcesPath}/build/" + group : params.urlOrigin + urlPathname + group})`,
);
return { fixedCssCode };
}
export function replaceImportsInCssCode(params: { cssCode: string }): {
fixedCssCode: string;
cssGlobalsToDefine: Record<string, string>;
} {
const { cssCode } = params;
const cssGlobalsToDefine: Record<string, string> = {};
new Set(cssCode.match(/url\(["']?\/[^/][^)"']+["']?\)[^;}]*/g) ?? []).forEach(
match => (cssGlobalsToDefine["url" + crypto.createHash("sha256").update(match).digest("hex").substring(0, 15)] = match),
);
let fixedCssCode = cssCode;
Object.keys(cssGlobalsToDefine).forEach(
cssVariableName =>
//NOTE: split/join pattern ~ replace all
(fixedCssCode = fixedCssCode.split(cssGlobalsToDefine[cssVariableName]).join(`var(--${cssVariableName})`)),
);
return { fixedCssCode, cssGlobalsToDefine };
}
export function generateCssCodeToDefineGlobals(params: { cssGlobalsToDefine: Record<string, string>; urlPathname: string }): {
cssCodeToPrependInHead: string;
} {
const { cssGlobalsToDefine, urlPathname } = params;
return {
"cssCodeToPrependInHead": [
":root {",
...Object.keys(cssGlobalsToDefine)
.map(cssVariableName =>
[
`--${cssVariableName}:`,
cssGlobalsToDefine[cssVariableName].replace(
new RegExp(`url\\(${urlPathname.replace(/\//g, "\\/")}`, "g"),
"url(${url.resourcesPath}/build/",
),
].join(" "),
)
.map(line => ` ${line};`),
"}",
].join("\n"),
};
}

View File

@ -0,0 +1,41 @@
#!/usr/bin/env node
import { downloadBuiltinKeycloakTheme } from "./download-builtin-keycloak-theme";
import { keycloakThemeEmailDirPath } from "./keycloakify";
import { join as pathJoin, basename as pathBasename } from "path";
import { transformCodebase } from "./tools/transformCodebase";
import { promptKeycloakVersion } from "./promptKeycloakVersion";
import * as fs from "fs";
import { getCliOptions } from "./tools/cliOptions";
import { getLogger } from "./tools/logger";
if (require.main === module) {
(async () => {
const { isSilent } = getCliOptions(process.argv.slice(2));
const logger = getLogger({ isSilent });
if (fs.existsSync(keycloakThemeEmailDirPath)) {
logger.warn(`There is already a ./${pathBasename(keycloakThemeEmailDirPath)} directory in your project. Aborting.`);
process.exit(-1);
}
const { keycloakVersion } = await promptKeycloakVersion();
const builtinKeycloakThemeTmpDirPath = pathJoin(keycloakThemeEmailDirPath, "..", "tmp_xIdP3_builtin_keycloak_theme");
downloadBuiltinKeycloakTheme({
keycloakVersion,
"destDirPath": builtinKeycloakThemeTmpDirPath,
isSilent
});
transformCodebase({
"srcDirPath": pathJoin(builtinKeycloakThemeTmpDirPath, "base", "email"),
"destDirPath": keycloakThemeEmailDirPath
});
logger.log(`./${pathBasename(keycloakThemeEmailDirPath)} ready to be customized`);
fs.rmSync(builtinKeycloakThemeTmpDirPath, { "recursive": true, "force": true });
})();
}

View File

@ -1,38 +1,40 @@
#!/usr/bin/env node
import { keycloakThemeBuildingDirPath } from "./build-keycloak-theme";
import { keycloakThemeBuildingDirPath } from "./keycloakify";
import { join as pathJoin } from "path";
import { downloadAndUnzip } from "./tools/downloadAndUnzip";
import { promptKeycloakVersion } from "./promptKeycloakVersion";
import { getCliOptions } from "./tools/cliOptions";
import { getLogger } from "./tools/logger";
export function downloadBuiltinKeycloakTheme(params: { keycloakVersion: string; destDirPath: string }) {
const { keycloakVersion, destDirPath } = params;
export async function downloadBuiltinKeycloakTheme(params: { keycloakVersion: string; destDirPath: string; isSilent: boolean }) {
const { keycloakVersion, destDirPath, isSilent } = params;
for (const ext of ["", "-community"]) {
downloadAndUnzip({
await downloadAndUnzip({
"destDirPath": destDirPath,
"url": `https://github.com/keycloak/keycloak/archive/refs/tags/${keycloakVersion}.zip`,
"pathOfDirToExtractInArchive": `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`,
"cacheDirPath": pathJoin(keycloakThemeBuildingDirPath, ".cache"),
isSilent
});
}
}
if (require.main === module) {
const keycloakVersion = (() => {
const keycloakVersion = process.argv[2] as string | undefined;
(async () => {
const { isSilent } = getCliOptions(process.argv.slice(2));
const logger = getLogger({ isSilent });
const { keycloakVersion } = await promptKeycloakVersion();
if (keycloakVersion === undefined) {
return "11.0.3";
}
const destDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme");
return keycloakVersion;
logger.log(`Downloading builtins theme of Keycloak ${keycloakVersion} here ${destDirPath}`);
await downloadBuiltinKeycloakTheme({
keycloakVersion,
destDirPath,
isSilent
});
})();
const destDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme");
console.log(`Downloading builtins theme of Keycloak ${keycloakVersion} here ${destDirPath}`);
downloadBuiltinKeycloakTheme({
keycloakVersion,
destDirPath,
});
}

View File

@ -1,74 +0,0 @@
import "minimal-polyfills/Object.fromEntries";
import * as fs from "fs";
import { join as pathJoin, relative as pathRelative } from "path";
import { crawl } from "./tools/crawl";
import { downloadBuiltinKeycloakTheme } from "./download-builtin-keycloak-theme";
import { getProjectRoot } from "./tools/getProjectRoot";
import { rm_rf, rm_r } from "./tools/rm";
//@ts-ignore
const propertiesParser = require("properties-parser");
for (const keycloakVersion of ["11.0.3", "15.0.2", "16.1.0"]) {
console.log({ keycloakVersion });
const tmpDirPath = pathJoin(getProjectRoot(), "tmp_xImOef9dOd44");
rm_rf(tmpDirPath);
downloadBuiltinKeycloakTheme({
keycloakVersion,
"destDirPath": tmpDirPath,
});
type Dictionary = { [idiomId: string]: string };
const record: { [typeOfPage: string]: { [language: string]: Dictionary } } = {};
{
const baseThemeDirPath = pathJoin(tmpDirPath, "base");
crawl(baseThemeDirPath).forEach(filePath => {
const match = filePath.match(/^([^/]+)\/messages\/messages_([^.]+)\.properties$/);
if (match === null) {
return;
}
const [, typeOfPage, language] = match;
(record[typeOfPage] ??= {})[language.replace(/_/g, "-")] = Object.fromEntries(
Object.entries(propertiesParser.parse(fs.readFileSync(pathJoin(baseThemeDirPath, filePath)).toString("utf8"))).map(
([key, value]: any) => [key, value.replace(/''/g, "'")],
),
);
});
}
rm_r(tmpDirPath);
const targetDirPath = pathJoin(getProjectRoot(), "src", "lib", "i18n", "generated_kcMessages", keycloakVersion);
fs.mkdirSync(targetDirPath, { "recursive": true });
Object.keys(record).forEach(pageType => {
const filePath = pathJoin(targetDirPath, `${pageType}.ts`);
fs.writeFileSync(
filePath,
Buffer.from(
[
`//This code was automatically generated by running ${pathRelative(getProjectRoot(), __filename)}`,
"//PLEASE DO NOT EDIT MANUALLY",
"",
"/* spell-checker: disable */",
`export const kcMessages= ${JSON.stringify(record[pageType], null, 2)};`,
"/* spell-checker: enable */",
].join("\n"),
"utf8",
),
);
console.log(`${filePath} wrote`);
});
}

View File

@ -0,0 +1,207 @@
import { z } from "zod";
import { assert } from "tsafe/assert";
import type { Equals } from "tsafe";
import { id } from "tsafe/id";
import { parse as urlParse } from "url";
import { typeGuard } from "tsafe/typeGuard";
import { symToStr } from "tsafe/symToStr";
const bundlers = ["mvn", "keycloakify", "none"] as const;
type Bundler = (typeof bundlers)[number];
type ParsedPackageJson = {
name: string;
version: string;
homepage?: string;
keycloakify?: {
extraPages?: string[];
extraThemeProperties?: string[];
areAppAndKeycloakServerSharingSameDomain?: boolean;
artifactId?: string;
groupId?: string;
bundler?: Bundler;
};
};
const zParsedPackageJson = z.object({
"name": z.string(),
"version": z.string(),
"homepage": z.string().optional(),
"keycloakify": z
.object({
"extraPages": z.array(z.string()).optional(),
"extraThemeProperties": z.array(z.string()).optional(),
"areAppAndKeycloakServerSharingSameDomain": z.boolean().optional(),
"artifactId": z.string().optional(),
"groupId": z.string().optional(),
"bundler": z.enum(bundlers).optional()
})
.optional()
});
assert<Equals<ReturnType<(typeof zParsedPackageJson)["parse"]>, ParsedPackageJson>>();
/** Consolidated build option gathered form CLI arguments and config in package.json */
export type BuildOptions = BuildOptions.Standalone | BuildOptions.ExternalAssets;
export namespace BuildOptions {
export type Common = {
isSilent: boolean;
version: string;
themeName: string;
extraPages?: string[];
extraThemeProperties?: string[];
groupId: string;
artifactId: string;
bundler: Bundler;
};
export type Standalone = Common & {
isStandalone: true;
urlPathname: string | undefined;
};
export type ExternalAssets = ExternalAssets.SameDomain | ExternalAssets.DifferentDomains;
export namespace ExternalAssets {
export type CommonExternalAssets = Common & {
isStandalone: false;
};
export type SameDomain = CommonExternalAssets & {
areAppAndKeycloakServerSharingSameDomain: true;
};
export type DifferentDomains = CommonExternalAssets & {
areAppAndKeycloakServerSharingSameDomain: false;
urlOrigin: string;
urlPathname: string | undefined;
};
}
}
export function readBuildOptions(params: {
packageJson: string;
CNAME: string | undefined;
isExternalAssetsCliParamProvided: boolean;
isSilent: boolean;
}): BuildOptions {
const { packageJson, CNAME, isExternalAssetsCliParamProvided, isSilent } = params;
const parsedPackageJson = zParsedPackageJson.parse(JSON.parse(packageJson));
const url = (() => {
const { homepage } = parsedPackageJson;
let url: URL | undefined = undefined;
if (homepage !== undefined) {
url = new URL(homepage);
}
if (CNAME !== undefined) {
url = new URL(`https://${CNAME.replace(/\s+$/, "")}`);
}
if (url === undefined) {
return undefined;
}
return {
"origin": url.origin,
"pathname": (() => {
const out = url.pathname.replace(/([^/])$/, "$1/");
return out === "/" ? undefined : out;
})()
};
})();
const common: BuildOptions.Common = (() => {
const { name, keycloakify = {}, version, homepage } = parsedPackageJson;
const { extraPages, extraThemeProperties, groupId, artifactId, bundler } = keycloakify ?? {};
const themeName = name
.replace(/^@(.*)/, "$1")
.split("/")
.join("-");
return {
themeName,
"bundler": (() => {
const { KEYCLOAKIFY_BUNDLER } = process.env;
assert(
typeGuard<Bundler | undefined>(
KEYCLOAKIFY_BUNDLER,
[undefined, ...id<readonly string[]>(bundlers)].includes(KEYCLOAKIFY_BUNDLER)
),
`${symToStr({ KEYCLOAKIFY_BUNDLER })} should be one of ${bundlers.join(", ")}`
);
return KEYCLOAKIFY_BUNDLER ?? bundler ?? "keycloakify";
})(),
"artifactId": process.env.KEYCLOAKIFY_ARTIFACT_ID ?? artifactId ?? `${themeName}-keycloak-theme`,
"groupId": (() => {
const fallbackGroupId = `${themeName}.keycloak`;
return (
process.env.KEYCLOAKIFY_GROUP_ID ??
groupId ??
(!homepage
? fallbackGroupId
: urlParse(homepage)
.host?.replace(/:[0-9]+$/, "")
?.split(".")
.reverse()
.join(".") ?? fallbackGroupId) + ".keycloak"
);
})(),
"version": process.env.KEYCLOAKIFY_VERSION ?? version,
extraPages,
extraThemeProperties,
isSilent
};
})();
if (isExternalAssetsCliParamProvided) {
const commonExternalAssets = id<BuildOptions.ExternalAssets.CommonExternalAssets>({
...common,
"isStandalone": false
});
if (parsedPackageJson.keycloakify?.areAppAndKeycloakServerSharingSameDomain) {
return id<BuildOptions.ExternalAssets.SameDomain>({
...commonExternalAssets,
"areAppAndKeycloakServerSharingSameDomain": true
});
} else {
assert(
url !== undefined,
[
"Can't compile in external assets mode if we don't know where",
"the app will be hosted.",
"You should provide a homepage field in the package.json (or create a",
"public/CNAME file.",
"Alternatively, if your app and the Keycloak server are on the same domain, ",
"eg https://example.com is your app and https://example.com/auth is the keycloak",
'admin UI, you can set "keycloakify": { "areAppAndKeycloakServerSharingSameDomain": true }',
"in your package.json"
].join(" ")
);
return id<BuildOptions.ExternalAssets.DifferentDomains>({
...commonExternalAssets,
"areAppAndKeycloakServerSharingSameDomain": false,
"urlOrigin": url.origin,
"urlPathname": url.pathname
});
}
}
return id<BuildOptions.Standalone>({
...common,
"isStandalone": true,
"urlPathname": url?.pathname
});
}

View File

@ -30,50 +30,84 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
</#attempt>
"printIfExists": function (fieldName, x) {
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return "${messagesPerField.printIfExists(fieldName,'1')}" ? x : undefined;
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
<#if !messagesPerField?? >
return undefined;
<#else>
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
return <#if messagesPerField.existsError('username', 'password')>x<#else>undefined</#if>;
<#else>
return <#if messagesPerField.existsError('${fieldName}')>x<#else>undefined</#if>;
</#if>
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
</#if>
},
"existsError": function (fieldName) {
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return <#if messagesPerField.existsError('${fieldName}')>true<#else>false</#if>;
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
<#if !messagesPerField?? >
return false;
<#else>
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
return <#if messagesPerField.existsError('username', 'password')>true<#else>false</#if>;
<#else>
return <#if messagesPerField.existsError('${fieldName}')>true<#else>false</#if>;
</#if>
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
</#if>
},
"get": function (fieldName) {
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
<#if messagesPerField.existsError('${fieldName}')>
return "${messagesPerField.get('${fieldName}')?no_esc}";
</#if>
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
<#if !messagesPerField?? >
return '';
<#else>
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
<#if messagesPerField.existsError('username', 'password')>
return 'Invalid username or password.';
</#if>
<#else>
<#if messagesPerField.existsError('${fieldName}')>
return "${messagesPerField.get('${fieldName}')?no_esc}";
</#if>
</#if>
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
</#if>
},
"exists": function (fieldName) {
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return <#if messagesPerField.exists('${fieldName}')>true<#else>false</#if>;
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
<#if !messagesPerField?? >
return false;
<#else>
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
return <#if messagesPerField.exists('username') || messagesPerField.exists('password')>true<#else>false</#if>;
<#else>
return <#if messagesPerField.exists('${fieldName}')>true<#else>false</#if>;
</#if>
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
</#if>
}
};
@ -122,10 +156,13 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
key == "updateProfileCtx" &&
are_same_path(path, [])
) || (
<#-- https://github.com/InseeFrLab/keycloakify/pull/65#issuecomment-991896344 -->
<#-- https://github.com/InseeFrLab/keycloakify/pull/65#issuecomment-991896344 (reports with saml-post-form.ftl) -->
<#-- https://github.com/InseeFrLab/keycloakify/issues/91#issue-1212319466 (reports with error.ftl and Kc18) -->
<#-- https://github.com/InseeFrLab/keycloakify/issues/109#issuecomment-1134610163 -->
key == "loginAction" &&
are_same_path(path, ["url"]) &&
pageId == "saml-post-form.ftl"
["saml-post-form.ftl", "error.ftl", "info.ftl"]?seq_contains(pageId) &&
!(auth?has_content && auth.showTryAnotherWayLink())
) || (
["contextData", "idpConfig", "idp", "authenticationSession"]?seq_contains(key) &&
are_same_path(path, ["brokerContext"]) &&
@ -134,6 +171,13 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
key == "identityProviderBrokerCtx" &&
are_same_path(path, []) &&
["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(pageId)
) || (
["masterAdminClient", "delegateForUpdate", "defaultRole"]?seq_contains(key) &&
are_same_path(path, ["realm"])
) || (
"error.ftl" == pageId &&
are_same_path(path, ["realm"]) &&
!["name", "displayName", "displayNameHtml", "internationalizationEnabled", "registrationEmailAsUsername" ]?seq_contains(key)
)
>
<#local out_seq += ["/*If you need '" + key + "' on " + pageId + ", please submit an issue to the Keycloakify repo*/"]>
@ -151,7 +195,7 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
</#attempt>
</#if>
<#attempt>
<#if !object[key]??>
<#continue>
@ -199,6 +243,31 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
</#attempt>
<#if isMethod>
<#if are_same_path(path, ["auth", "showUsername"])>
<#attempt>
<#return auth.showUsername()?c>
<#recover>
<#return "ABORT: Couldn't evaluate auth.showUsername()">
</#attempt>
</#if>
<#if are_same_path(path, ["auth", "showResetCredentials"])>
<#attempt>
<#return auth.showResetCredentials()?c>
<#recover>
<#return "ABORT: Couldn't evaluate auth.showResetCredentials()">
</#attempt>
</#if>
<#if are_same_path(path, ["auth", "showTryAnotherWayLink"])>
<#attempt>
<#return auth.showTryAnotherWayLink()?c>
<#recover>
<#return "ABORT: Couldn't evaluate auth.showTryAnotherWayLink()">
</#attempt>
</#if>
<#return "ABORT: It's a method">
</#if>
@ -229,6 +298,11 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
<#list object as array_item>
<#if !array_item??>
<#local out_seq += ["null,"]>
<#continue>
</#if>
<#local rec_out = ftl_object_to_js_code_declaring_an_object(array_item, path + [ i ])>
<#local i = i + 1>
@ -262,7 +336,7 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
</#function>
<#function are_same_path path searchedPath>
<#if path?size != path?size>
<#if path?size != searchedPath?size>
<#return false>
</#if>

View File

@ -0,0 +1,190 @@
import cheerio from "cheerio";
import { replaceImportsFromStaticInJsCode } from "../replacers/replaceImportsFromStaticInJsCode";
import { generateCssCodeToDefineGlobals } from "../replacers/replaceImportsInCssCode";
import { replaceImportsInInlineCssCode } from "../replacers/replaceImportsInInlineCssCode";
import * as fs from "fs";
import { join as pathJoin } from "path";
import { objectKeys } from "tsafe/objectKeys";
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
import type { BuildOptions } from "../BuildOptions";
import { assert } from "tsafe/assert";
import { Reflect } from "tsafe/Reflect";
// https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java
export const pageIds = [
"login.ftl",
"login-username.ftl",
"login-password.ftl",
"webauthn-authenticate.ftl",
"register.ftl",
"register-user-profile.ftl",
"info.ftl",
"error.ftl",
"login-reset-password.ftl",
"login-verify-email.ftl",
"terms.ftl",
"login-otp.ftl",
"login-update-profile.ftl",
"login-update-password.ftl",
"login-idp-link-confirm.ftl",
"login-idp-link-email.ftl",
"login-page-expired.ftl",
"login-config-totp.ftl",
"logout-confirm.ftl",
"update-user-profile.ftl",
"idp-review-user-profile.ftl"
] as const;
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
export namespace BuildOptionsLike {
export type Standalone = {
isStandalone: true;
urlPathname: string | undefined;
};
export type ExternalAssets = ExternalAssets.SameDomain | ExternalAssets.DifferentDomains;
export namespace ExternalAssets {
export type CommonExternalAssets = {
isStandalone: false;
};
export type SameDomain = CommonExternalAssets & {
areAppAndKeycloakServerSharingSameDomain: true;
};
export type DifferentDomains = CommonExternalAssets & {
areAppAndKeycloakServerSharingSameDomain: false;
urlOrigin: string;
urlPathname: string | undefined;
};
}
}
{
const buildOptions = Reflect<BuildOptions>();
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
}
export type PageId = (typeof pageIds)[number];
export function generateFtlFilesCodeFactory(params: {
indexHtmlCode: string;
//NOTE: Expected to be an empty object if external assets mode is enabled.
cssGlobalsToDefine: Record<string, string>;
buildOptions: BuildOptionsLike;
}) {
const { cssGlobalsToDefine, indexHtmlCode, buildOptions } = params;
const $ = cheerio.load(indexHtmlCode);
fix_imports_statements: {
if (!buildOptions.isStandalone && buildOptions.areAppAndKeycloakServerSharingSameDomain) {
break fix_imports_statements;
}
$("script:not([src])").each((...[, element]) => {
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"jsCode": $(element).html()!,
buildOptions
});
$(element).text(fixedJsCode);
});
$("style").each((...[, element]) => {
const { fixedCssCode } = replaceImportsInInlineCssCode({
"cssCode": $(element).html()!,
buildOptions
});
$(element).text(fixedCssCode);
});
(
[
["link", "href"],
["script", "src"]
] as const
).forEach(([selector, attrName]) =>
$(selector).each((...[, element]) => {
const href = $(element).attr(attrName);
if (href === undefined) {
return;
}
$(element).attr(
attrName,
buildOptions.isStandalone
? href.replace(new RegExp(`^${(buildOptions.urlPathname ?? "/").replace(/\//g, "\\/")}`), "${url.resourcesPath}/build/")
: href.replace(/^\//, `${buildOptions.urlOrigin}/`)
);
})
);
if (Object.keys(cssGlobalsToDefine).length !== 0) {
$("head").prepend(
[
"",
"<style>",
generateCssCodeToDefineGlobals({
cssGlobalsToDefine,
buildOptions
}).cssCodeToPrependInHead,
"</style>",
""
].join("\n")
);
}
}
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
const replaceValueBySearchValue = {
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': fs
.readFileSync(pathJoin(__dirname, "ftl_object_to_js_code_declaring_an_object.ftl"))
.toString("utf8")
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1],
"<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->": [
"<#if scripts??>",
" <#list scripts as script>",
' <script src="${script}" type="text/javascript"></script>',
" </#list>",
"</#if>"
].join("\n")
};
$("head").prepend(
[
"<script>",
` window.${ftlValuesGlobalName}= ${objectKeys(replaceValueBySearchValue)[0]};`,
"</script>",
"",
objectKeys(replaceValueBySearchValue)[1]
].join("\n")
);
const partiallyFixedIndexHtmlCode = $.html();
function generateFtlFilesCode(params: { pageId: string }): {
ftlCode: string;
} {
const { pageId } = params;
const $ = cheerio.load(partiallyFixedIndexHtmlCode);
let ftlCode = $.html();
Object.entries({
...replaceValueBySearchValue,
//If updated, don't forget to change in the ftl script as well.
"PAGE_ID_xIgLsPgGId9D8e": pageId
}).map(([searchValue, replaceValue]) => (ftlCode = ftlCode.replace(searchValue, replaceValue)));
return { ftlCode };
}
return { generateFtlFilesCode };
}

View File

@ -1,33 +1,39 @@
import * as url from "url";
import * as fs from "fs";
import { join as pathJoin, dirname as pathDirname } from "path";
import { assert } from "tsafe/assert";
import { Reflect } from "tsafe/Reflect";
import type { BuildOptions } from "./BuildOptions";
export function generateJavaStackFiles(params: { version: string; themeName: string; homepage?: string; keycloakThemeBuildingDirPath: string }): {
export type BuildOptionsLike = {
themeName: string;
groupId: string;
artifactId?: string;
version: string;
};
{
const buildOptions = Reflect<BuildOptions>();
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
}
export function generateJavaStackFiles(params: {
keycloakThemeBuildingDirPath: string;
doBundlesEmailTemplate: boolean;
buildOptions: BuildOptionsLike;
}): {
jarFilePath: string;
} {
const { themeName, version, homepage, keycloakThemeBuildingDirPath } = params;
const {
buildOptions: { groupId, themeName, version, artifactId },
keycloakThemeBuildingDirPath,
doBundlesEmailTemplate
} = params;
{
const { pomFileCode } = (function generatePomFileCode(): {
pomFileCode: string;
} {
const groupId = (() => {
const fallbackGroupId = `there.was.no.homepage.field.in.the.package.json.${themeName}`;
return (
(!homepage
? fallbackGroupId
: url
.parse(homepage)
.host?.replace(/:[0-9]+$/, "")
?.split(".")
.reverse()
.join(".") ?? fallbackGroupId) + ".keycloak"
);
})();
const artefactId = `${themeName}-keycloak-theme`;
const pomFileCode = [
`<?xml version="1.0"?>`,
`<project xmlns="http://maven.apache.org/POM/4.0.0"`,
@ -35,11 +41,11 @@ export function generateJavaStackFiles(params: { version: string; themeName: str
` xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">`,
` <modelVersion>4.0.0</modelVersion>`,
` <groupId>${groupId}</groupId>`,
` <artifactId>${artefactId}</artifactId>`,
` <artifactId>${artifactId}</artifactId>`,
` <version>${version}</version>`,
` <name>${artefactId}</name>`,
` <name>${artifactId}</name>`,
` <description />`,
`</project>`,
`</project>`
].join("\n");
return { pomFileCode };
@ -63,19 +69,19 @@ export function generateJavaStackFiles(params: { version: string; themeName: str
"themes": [
{
"name": themeName,
"types": ["login"],
},
],
"types": ["login", ...(doBundlesEmailTemplate ? ["email"] : [])]
}
]
},
null,
2,
2
),
"utf8",
),
"utf8"
)
);
}
return {
"jarFilePath": pathJoin(keycloakThemeBuildingDirPath, "target", `${themeName}-${version}.jar`),
"jarFilePath": pathJoin(keycloakThemeBuildingDirPath, "target", `${artifactId}-${version}.jar`)
};
}

View File

@ -0,0 +1,201 @@
import { transformCodebase } from "../tools/transformCodebase";
import * as fs from "fs";
import { join as pathJoin, basename as pathBasename } from "path";
import { replaceImportsFromStaticInJsCode } from "./replacers/replaceImportsFromStaticInJsCode";
import { replaceImportsInCssCode } from "./replacers/replaceImportsInCssCode";
import { generateFtlFilesCodeFactory, pageIds } from "./generateFtl";
import { downloadBuiltinKeycloakTheme } from "../download-builtin-keycloak-theme";
import { mockTestingResourcesCommonPath, mockTestingResourcesPath, mockTestingSubDirOfPublicDirBasename } from "../mockTestingResourcesPath";
import { isInside } from "../tools/isInside";
import type { BuildOptions } from "./BuildOptions";
import { assert } from "tsafe/assert";
import { Reflect } from "tsafe/Reflect";
import { getLogger } from "../tools/logger";
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
export namespace BuildOptionsLike {
export type Common = {
themeName: string;
extraPages?: string[];
extraThemeProperties?: string[];
isSilent: boolean;
};
export type Standalone = Common & {
isStandalone: true;
urlPathname: string | undefined;
};
export type ExternalAssets = ExternalAssets.SameDomain | ExternalAssets.DifferentDomains;
export namespace ExternalAssets {
export type CommonExternalAssets = Common & {
isStandalone: false;
};
export type SameDomain = CommonExternalAssets & {
areAppAndKeycloakServerSharingSameDomain: true;
};
export type DifferentDomains = CommonExternalAssets & {
areAppAndKeycloakServerSharingSameDomain: false;
urlOrigin: string;
urlPathname: string | undefined;
};
}
}
{
const buildOptions = Reflect<BuildOptions>();
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
}
export async function generateKeycloakThemeResources(params: {
reactAppBuildDirPath: string;
keycloakThemeBuildingDirPath: string;
keycloakThemeEmailDirPath: string;
keycloakVersion: string;
buildOptions: BuildOptionsLike;
}): Promise<{ doBundlesEmailTemplate: boolean }> {
const { reactAppBuildDirPath, keycloakThemeBuildingDirPath, keycloakThemeEmailDirPath, keycloakVersion, buildOptions } = params;
const logger = getLogger({ isSilent: buildOptions.isSilent });
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", buildOptions.themeName, "login");
let allCssGlobalsToDefine: Record<string, string> = {};
transformCodebase({
"destDirPath": buildOptions.isStandalone ? pathJoin(themeDirPath, "resources", "build") : reactAppBuildDirPath,
"srcDirPath": reactAppBuildDirPath,
"transformSourceCode": ({ filePath, sourceCode }) => {
//NOTE: Prevent cycles, excludes the folder we generated for debug in public/
if (
buildOptions.isStandalone &&
isInside({
"dirPath": pathJoin(reactAppBuildDirPath, mockTestingSubDirOfPublicDirBasename),
filePath
})
) {
return undefined;
}
if (/\.css?$/i.test(filePath)) {
if (!buildOptions.isStandalone) {
return undefined;
}
const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode({
"cssCode": sourceCode.toString("utf8")
});
allCssGlobalsToDefine = {
...allCssGlobalsToDefine,
...cssGlobalsToDefine
};
return { "modifiedSourceCode": Buffer.from(fixedCssCode, "utf8") };
}
if (/\.js?$/i.test(filePath)) {
if (!buildOptions.isStandalone && buildOptions.areAppAndKeycloakServerSharingSameDomain) {
return undefined;
}
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"jsCode": sourceCode.toString("utf8"),
buildOptions
});
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
}
return buildOptions.isStandalone ? { "modifiedSourceCode": sourceCode } : undefined;
}
});
let doBundlesEmailTemplate: boolean;
email: {
if (!fs.existsSync(keycloakThemeEmailDirPath)) {
logger.log(
[
`Not bundling email template because ${pathBasename(keycloakThemeEmailDirPath)} does not exist`,
`To start customizing the email template, run: 👉 npx create-keycloak-email-directory 👈`
].join("\n")
);
doBundlesEmailTemplate = false;
break email;
}
doBundlesEmailTemplate = true;
transformCodebase({
"srcDirPath": keycloakThemeEmailDirPath,
"destDirPath": pathJoin(themeDirPath, "..", "email")
});
}
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
"indexHtmlCode": fs.readFileSync(pathJoin(reactAppBuildDirPath, "index.html")).toString("utf8"),
"cssGlobalsToDefine": allCssGlobalsToDefine,
"buildOptions": buildOptions
});
[...pageIds, ...(buildOptions.extraPages ?? [])].forEach(pageId => {
const { ftlCode } = generateFtlFilesCode({ pageId });
fs.mkdirSync(themeDirPath, { "recursive": true });
fs.writeFileSync(pathJoin(themeDirPath, pageId), Buffer.from(ftlCode, "utf8"));
});
{
const tmpDirPath = pathJoin(themeDirPath, "..", "tmp_xxKdLpdIdLd");
await downloadBuiltinKeycloakTheme({
keycloakVersion,
"destDirPath": tmpDirPath,
isSilent: buildOptions.isSilent
});
const themeResourcesDirPath = pathJoin(themeDirPath, "resources");
transformCodebase({
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "login", "resources"),
"destDirPath": themeResourcesDirPath
});
const reactAppPublicDirPath = pathJoin(reactAppBuildDirPath, "..", "public");
transformCodebase({
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "common", "resources"),
"destDirPath": pathJoin(themeResourcesDirPath, pathBasename(mockTestingResourcesCommonPath))
});
transformCodebase({
"srcDirPath": themeResourcesDirPath,
"destDirPath": pathJoin(reactAppPublicDirPath, mockTestingResourcesPath)
});
const keycloakResourcesWithinPublicDirPath = pathJoin(reactAppPublicDirPath, mockTestingSubDirOfPublicDirBasename);
fs.writeFileSync(
pathJoin(keycloakResourcesWithinPublicDirPath, "README.txt"),
Buffer.from(
["This is just a test folder that helps develop", "the login and register page without having to run a Keycloak container"].join(" ")
)
);
fs.writeFileSync(pathJoin(keycloakResourcesWithinPublicDirPath, ".gitignore"), Buffer.from("*", "utf8"));
fs.rmSync(tmpDirPath, { recursive: true, force: true });
}
fs.writeFileSync(
pathJoin(themeDirPath, "theme.properties"),
Buffer.from(["parent=keycloak", ...(buildOptions.extraThemeProperties ?? [])].join("\n\n"), "utf8")
);
return { doBundlesEmailTemplate };
}

View File

@ -0,0 +1,61 @@
import * as fs from "fs";
import { join as pathJoin } from "path";
import { assert } from "tsafe/assert";
import { Reflect } from "tsafe/Reflect";
import type { BuildOptions } from "./BuildOptions";
export type BuildOptionsLike = {
themeName: string;
};
{
const buildOptions = Reflect<BuildOptions>();
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
}
generateStartKeycloakTestingContainer.basename = "start_keycloak_testing_container.sh";
const containerName = "keycloak-testing-container";
/** Files for being able to run a hot reload keycloak container */
export function generateStartKeycloakTestingContainer(params: {
keycloakVersion: string;
keycloakThemeBuildingDirPath: string;
buildOptions: BuildOptionsLike;
}) {
const {
keycloakThemeBuildingDirPath,
keycloakVersion,
buildOptions: { themeName }
} = params;
const keycloakThemePath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName).replace(/\\/g, "/");
fs.writeFileSync(
pathJoin(keycloakThemeBuildingDirPath, generateStartKeycloakTestingContainer.basename),
Buffer.from(
[
"#!/usr/bin/env bash",
"",
`docker rm ${containerName} || true`,
"",
`cd "${keycloakThemeBuildingDirPath.replace(/\\/g, "/")}"`,
"",
"docker run \\",
" -p 8080:8080 \\",
` --name ${containerName} \\`,
" -e KEYCLOAK_ADMIN=admin \\",
" -e KEYCLOAK_ADMIN_PASSWORD=admin \\",
" -e JAVA_OPTS=-Dkeycloak.profile=preview \\",
` -v "${keycloakThemePath}":"/opt/keycloak/themes/${themeName}":rw \\`,
` -it quay.io/keycloak/keycloak:${keycloakVersion} \\`,
` start-dev`,
""
].join("\n"),
"utf8"
),
{ "mode": 0o755 }
);
}

View File

@ -0,0 +1,8 @@
#!/usr/bin/env node
export * from "./keycloakify";
import { main } from "./keycloakify";
if (require.main === module) {
main().catch(e => console.error(e));
}

View File

@ -0,0 +1,139 @@
import { generateKeycloakThemeResources } from "./generateKeycloakThemeResources";
import { generateJavaStackFiles } from "./generateJavaStackFiles";
import { join as pathJoin, relative as pathRelative, basename as pathBasename } from "path";
import * as child_process from "child_process";
import { generateStartKeycloakTestingContainer } from "./generateStartKeycloakTestingContainer";
import * as fs from "fs";
import { readBuildOptions } from "./BuildOptions";
import { getLogger } from "../tools/logger";
import { getCliOptions } from "../tools/cliOptions";
import jar from "../tools/jar";
import { assert } from "tsafe/assert";
import type { Equals } from "tsafe";
const reactProjectDirPath = process.cwd();
export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak");
export const keycloakThemeEmailDirPath = pathJoin(keycloakThemeBuildingDirPath, "..", "keycloak_email");
export async function main() {
const { isSilent, hasExternalAssets } = getCliOptions(process.argv.slice(2));
const logger = getLogger({ isSilent });
logger.log("🔏 Building the keycloak theme...⌚");
const buildOptions = readBuildOptions({
"packageJson": fs.readFileSync(pathJoin(reactProjectDirPath, "package.json")).toString("utf8"),
"CNAME": (() => {
const cnameFilePath = pathJoin(reactProjectDirPath, "public", "CNAME");
if (!fs.existsSync(cnameFilePath)) {
return undefined;
}
return fs.readFileSync(cnameFilePath).toString("utf8");
})(),
"isExternalAssetsCliParamProvided": hasExternalAssets,
"isSilent": isSilent
});
const { doBundlesEmailTemplate } = await generateKeycloakThemeResources({
keycloakThemeBuildingDirPath,
keycloakThemeEmailDirPath,
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
buildOptions,
//We have to leave it at that otherwise we break our default theme.
//Problem is that we can't guarantee that the the old resources
//will still be available on the newer keycloak version.
"keycloakVersion": "11.0.3"
});
const { jarFilePath } = generateJavaStackFiles({
keycloakThemeBuildingDirPath,
doBundlesEmailTemplate,
buildOptions
});
switch (buildOptions.bundler) {
case "none":
logger.log("😱 Skipping bundling step, there will be no jar");
break;
case "keycloakify":
logger.log("🫶 Let keycloakify do its thang");
await jar({
"rootPath": pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources"),
"version": buildOptions.version,
"groupId": buildOptions.groupId,
"artifactId": buildOptions.artifactId,
"targetPath": jarFilePath
});
break;
case "mvn":
logger.log("🫙 Run maven to deliver a jar");
child_process.execSync("mvn package", { "cwd": keycloakThemeBuildingDirPath });
break;
default:
assert<Equals<typeof buildOptions.bundler, never>>(false);
}
// We want, however, to test in a container running the latest Keycloak version
const containerKeycloakVersion = "20.0.1";
generateStartKeycloakTestingContainer({
keycloakThemeBuildingDirPath,
"keycloakVersion": containerKeycloakVersion,
buildOptions
});
logger.log(
[
"",
`✅ Your keycloak theme has been generated and bundled into ./${pathRelative(reactProjectDirPath, jarFilePath)} 🚀`,
`It is to be placed in "/opt/keycloak/providers" in the container running a quay.io/keycloak/keycloak Docker image.`,
"",
//TODO: Restore when we find a good Helm chart for Keycloak.
//"Using Helm (https://github.com/codecentric/helm-charts), edit to reflect:",
"",
"value.yaml: ",
" extraInitContainers: |",
" - name: realm-ext-provider",
" image: curlimages/curl",
" imagePullPolicy: IfNotPresent",
" command:",
" - sh",
" args:",
" - -c",
` - curl -L -f -S -o /extensions/${pathBasename(jarFilePath)} https://AN.URL.FOR/${pathBasename(jarFilePath)}`,
" volumeMounts:",
" - name: extensions",
" mountPath: /extensions",
" ",
" extraVolumeMounts: |",
" - name: extensions",
" mountPath: /opt/keycloak/providers",
" extraEnv: |",
" - name: KEYCLOAK_USER",
" value: admin",
" - name: KEYCLOAK_PASSWORD",
" value: xxxxxxxxx",
" - name: JAVA_OPTS",
" value: -Dkeycloak.profile=preview",
"",
"",
`To test your theme locally you can spin up a Keycloak ${containerKeycloakVersion} container image with the theme pre loaded by running:`,
"",
`👉 $ ./${pathRelative(reactProjectDirPath, pathJoin(keycloakThemeBuildingDirPath, generateStartKeycloakTestingContainer.basename))} 👈`,
"",
"Test with different Keycloak versions by editing the .sh file. see available versions here: https://quay.io/repository/keycloak/keycloak?tab=tags",
"",
"Once your container is up and running: ",
"- Log into the admin console 👉 http://localhost:8080/admin username: admin, password: admin 👈",
'- Create a realm named "myrealm"',
'- Create a client with ID: "myclient", "Root URL": "https://www.keycloak.org/app/" and "Valid redirect URIs": "https://www.keycloak.org/app/*"',
`- Select Login Theme: ${buildOptions.themeName} (don't forget to save at the bottom of the page)`,
`- Go to 👉 https://www.keycloak.org/app/ 👈 Click "Save" then "Sign in". You should see your login page`,
"",
"Video demoing this process: https://youtu.be/N3wlBoH4hKg",
""
].join("\n")
);
}

View File

@ -0,0 +1,86 @@
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
import type { BuildOptions } from "../BuildOptions";
import { assert } from "tsafe/assert";
import { is } from "tsafe/is";
import { Reflect } from "tsafe/Reflect";
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
export namespace BuildOptionsLike {
export type Standalone = {
isStandalone: true;
};
export type ExternalAssets = {
isStandalone: false;
urlOrigin: string;
};
}
{
const buildOptions = Reflect<BuildOptions>();
assert(!is<BuildOptions.ExternalAssets.CommonExternalAssets>(buildOptions));
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
}
export function replaceImportsFromStaticInJsCode(params: { jsCode: string; buildOptions: BuildOptionsLike }): { fixedJsCode: string } {
/*
NOTE:
When we have urlOrigin defined it means that
we are building with --external-assets
so we have to make sur that the fixed js code will run
inside and outside keycloak.
When urlOrigin isn't defined we can assume the fixedJsCode
will always run in keycloak context.
*/
const { jsCode, buildOptions } = params;
const getReplaceArgs = (language: "js" | "css"): Parameters<typeof String.prototype.replace> => [
new RegExp(`([a-zA-Z_]+)\\.([a-zA-Z]+)=function\\(([a-zA-Z]+)\\){return"static\\/${language}\\/"`, "g"),
(...[, n, u, e]) => `
${n}[(function(){
var pd= Object.getOwnPropertyDescriptor(${n}, "p");
if( pd === undefined || pd.configurable ){
${
buildOptions.isStandalone
? `
Object.defineProperty(${n}, "p", {
get: function() { return window.${ftlValuesGlobalName}.url.resourcesPath; },
set: function (){}
});
`
: `
var p= "";
Object.defineProperty(${n}, "p", {
get: function() { return "${ftlValuesGlobalName}" in window ? "${buildOptions.urlOrigin}/" : p; },
set: function (value){ p = value;}
});
`
}
}
return "${u}";
})()] = function(${e}) { return "${buildOptions.isStandalone ? "/build/" : ""}static/${language}/"`
];
const fixedJsCode = jsCode
.replace(...getReplaceArgs("js"))
.replace(...getReplaceArgs("css"))
.replace(/([a-zA-Z]+\.[a-zA-Z]+)\+"static\//g, (...[, group]) =>
buildOptions.isStandalone
? `window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`
: `("${ftlValuesGlobalName}" in window ? "${buildOptions.urlOrigin}/" : ${group}) + "static/`
)
//TODO: Write a test case for this
.replace(/".chunk.css",([a-zA-Z])+=([a-zA-Z]+\.[a-zA-Z]+)\+([a-zA-Z]+),/, (...[, group1, group2, group3]) =>
buildOptions.isStandalone
? `".chunk.css",${group1} = window.${ftlValuesGlobalName}.url.resourcesPath + "/build/" + ${group3},`
: `".chunk.css",${group1} = ("${ftlValuesGlobalName}" in window ? "${buildOptions.urlOrigin}/" : ${group2}) + ${group3},`
);
return { fixedJsCode };
}

View File

@ -0,0 +1,64 @@
import * as crypto from "crypto";
import type { BuildOptions } from "../BuildOptions";
import { assert } from "tsafe/assert";
import { is } from "tsafe/is";
import { Reflect } from "tsafe/Reflect";
export type BuildOptionsLike = {
urlPathname: string | undefined;
};
{
const buildOptions = Reflect<BuildOptions>();
assert(!is<BuildOptions.ExternalAssets.CommonExternalAssets>(buildOptions));
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
}
export function replaceImportsInCssCode(params: { cssCode: string }): {
fixedCssCode: string;
cssGlobalsToDefine: Record<string, string>;
} {
const { cssCode } = params;
const cssGlobalsToDefine: Record<string, string> = {};
new Set(cssCode.match(/url\(["']?\/[^/][^)"']+["']?\)[^;}]*/g) ?? []).forEach(
match => (cssGlobalsToDefine["url" + crypto.createHash("sha256").update(match).digest("hex").substring(0, 15)] = match)
);
let fixedCssCode = cssCode;
Object.keys(cssGlobalsToDefine).forEach(
cssVariableName =>
//NOTE: split/join pattern ~ replace all
(fixedCssCode = fixedCssCode.split(cssGlobalsToDefine[cssVariableName]).join(`var(--${cssVariableName})`))
);
return { fixedCssCode, cssGlobalsToDefine };
}
export function generateCssCodeToDefineGlobals(params: { cssGlobalsToDefine: Record<string, string>; buildOptions: BuildOptionsLike }): {
cssCodeToPrependInHead: string;
} {
const { cssGlobalsToDefine, buildOptions } = params;
return {
"cssCodeToPrependInHead": [
":root {",
...Object.keys(cssGlobalsToDefine)
.map(cssVariableName =>
[
`--${cssVariableName}:`,
cssGlobalsToDefine[cssVariableName].replace(
new RegExp(`url\\(${(buildOptions.urlPathname ?? "/").replace(/\//g, "\\/")}`, "g"),
"url(${url.resourcesPath}/build/"
)
].join(" ")
)
.map(line => ` ${line};`),
"}"
].join("\n")
};
}

View File

@ -0,0 +1,47 @@
import type { BuildOptions } from "../BuildOptions";
import { assert } from "tsafe/assert";
import { is } from "tsafe/is";
import { Reflect } from "tsafe/Reflect";
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
export namespace BuildOptionsLike {
export type Common = {
urlPathname: string | undefined;
};
export type Standalone = Common & {
isStandalone: true;
};
export type ExternalAssets = Common & {
isStandalone: false;
urlOrigin: string;
};
}
{
const buildOptions = Reflect<BuildOptions>();
assert(!is<BuildOptions.ExternalAssets.CommonExternalAssets>(buildOptions));
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
}
export function replaceImportsInInlineCssCode(params: { cssCode: string; buildOptions: BuildOptionsLike }): {
fixedCssCode: string;
} {
const { cssCode, buildOptions } = params;
const fixedCssCode = cssCode.replace(
buildOptions.urlPathname === undefined
? /url\(["']?\/([^/][^)"']+)["']?\)/g
: new RegExp(`url\\(["']?${buildOptions.urlPathname}([^)"']+)["']?\\)`, "g"),
(...[, group]) =>
`url(${
buildOptions.isStandalone ? "${url.resourcesPath}/build/" + group : buildOptions.urlOrigin + (buildOptions.urlPathname ?? "/") + group
})`
);
return { fixedCssCode };
}

View File

@ -1,102 +0,0 @@
import { execSync } from "child_process";
import { join as pathJoin, relative as pathRelative } from "path";
import * as fs from "fs";
const keycloakifyDirPath = pathJoin(__dirname, "..", "..");
fs.writeFileSync(
pathJoin(keycloakifyDirPath, "dist", "package.json"),
Buffer.from(
JSON.stringify(
(() => {
const packageJsonParsed = JSON.parse(fs.readFileSync(pathJoin(keycloakifyDirPath, "package.json")).toString("utf8"));
return {
...packageJsonParsed,
"main": packageJsonParsed["main"].replace(/^dist\//, ""),
"types": packageJsonParsed["types"].replace(/^dist\//, ""),
};
})(),
null,
2,
),
"utf8",
),
);
const commonThirdPartyDeps = (() => {
const namespaceModuleNames = ["@emotion"];
const standaloneModuleNames = ["react", "@types/react", "powerhooks", "tss-react", "evt"];
return [
...namespaceModuleNames
.map(namespaceModuleName =>
fs
.readdirSync(pathJoin(keycloakifyDirPath, "node_modules", namespaceModuleName))
.map(submoduleName => `${namespaceModuleName}/${submoduleName}`),
)
.reduce((prev, curr) => [...prev, ...curr], []),
...standaloneModuleNames,
];
})();
const yarnHomeDirPath = pathJoin(keycloakifyDirPath, ".yarn_home");
execSync(["rm -rf", "mkdir"].map(cmd => `${cmd} ${yarnHomeDirPath}`).join(" && "));
const execYarnLink = (params: { targetModuleName?: string; cwd: string }) => {
const { targetModuleName, cwd } = params;
const cmd = ["yarn", "link", ...(targetModuleName !== undefined ? [targetModuleName] : [])].join(" ");
console.log(`$ cd ${pathRelative(keycloakifyDirPath, cwd) || "."} && ${cmd}`);
execSync(cmd, {
cwd,
"env": {
...process.env,
"HOME": yarnHomeDirPath,
},
});
};
const testAppNames = ["keycloakify-demo-app"] as const;
const getTestAppPath = (testAppName: typeof testAppNames[number]) => pathJoin(keycloakifyDirPath, "..", testAppName);
testAppNames.forEach(testAppName => execSync("yarn install", { "cwd": getTestAppPath(testAppName) }));
console.log("=== Linking common dependencies ===");
const total = commonThirdPartyDeps.length;
let current = 0;
commonThirdPartyDeps.forEach(commonThirdPartyDep => {
current++;
console.log(`${current}/${total} ${commonThirdPartyDep}`);
const localInstallPath = pathJoin(
...[keycloakifyDirPath, "node_modules", ...(commonThirdPartyDep.startsWith("@") ? commonThirdPartyDep.split("/") : [commonThirdPartyDep])],
);
execYarnLink({ "cwd": localInstallPath });
testAppNames.forEach(testAppName =>
execYarnLink({
"cwd": getTestAppPath(testAppName),
"targetModuleName": commonThirdPartyDep,
}),
);
});
console.log("=== Linking in house dependencies ===");
execYarnLink({ "cwd": pathJoin(keycloakifyDirPath, "dist") });
testAppNames.forEach(testAppName =>
execYarnLink({
"cwd": getTestAppPath(testAppName),
"targetModuleName": "keycloakify",
}),
);

View File

@ -0,0 +1,5 @@
import { pathJoin } from "./tools/pathJoin";
export const mockTestingSubDirOfPublicDirBasename = "keycloak_static";
export const mockTestingResourcesPath = pathJoin(mockTestingSubDirOfPublicDirBasename, "resources");
export const mockTestingResourcesCommonPath = pathJoin(mockTestingResourcesPath, "resources_common");

View File

@ -0,0 +1,47 @@
import { getLatestsSemVersionedTagFactory } from "./tools/octokit-addons/getLatestsSemVersionedTag";
import { Octokit } from "@octokit/rest";
import cliSelect from "cli-select";
export async function promptKeycloakVersion() {
const { getLatestsSemVersionedTag } = (() => {
const { octokit } = (() => {
const githubToken = process.env.GITHUB_TOKEN;
const octokit = new Octokit(githubToken === undefined ? undefined : { "auth": githubToken });
return { octokit };
})();
const { getLatestsSemVersionedTag } = getLatestsSemVersionedTagFactory({ octokit });
return { getLatestsSemVersionedTag };
})();
console.log("Initialize the directory with email template from which keycloak version?");
const tags = [
...(await getLatestsSemVersionedTag({
"count": 10,
"doIgnoreBeta": true,
"owner": "keycloak",
"repo": "keycloak"
}).then(arr => arr.map(({ tag }) => tag))),
"11.0.3"
];
if (process.env["GITHUB_ACTIONS"] === "true") {
return { "keycloakVersion": tags[0] };
}
const { value: keycloakVersion } = await cliSelect<string>({
"values": tags
}).catch(() => {
console.log("Aborting");
process.exit(-1);
});
console.log(keycloakVersion);
return { keycloakVersion };
}

View File

@ -0,0 +1,73 @@
export type NpmModuleVersion = {
major: number;
minor: number;
patch: number;
betaPreRelease?: number;
};
export namespace NpmModuleVersion {
export function parse(versionStr: string): NpmModuleVersion {
const match = versionStr.match(/^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-beta.([0-9]+))?/);
if (!match) {
throw new Error(`${versionStr} is not a valid NPM version`);
}
return {
"major": parseInt(match[1]),
"minor": parseInt(match[2]),
"patch": parseInt(match[3]),
...(() => {
const str = match[4];
return str === undefined ? {} : { "betaPreRelease": parseInt(str) };
})()
};
}
export function stringify(v: NpmModuleVersion) {
return `${v.major}.${v.minor}.${v.patch}${v.betaPreRelease === undefined ? "" : `-beta.${v.betaPreRelease}`}`;
}
/**
*
* v1 < v2 => -1
* v1 === v2 => 0
* v1 > v2 => 1
*
*/
export function compare(v1: NpmModuleVersion, v2: NpmModuleVersion): -1 | 0 | 1 {
const sign = (diff: number): -1 | 0 | 1 => (diff === 0 ? 0 : diff < 0 ? -1 : 1);
const noUndefined = (n: number | undefined) => n ?? Infinity;
for (const level of ["major", "minor", "patch", "betaPreRelease"] as const) {
if (noUndefined(v1[level]) !== noUndefined(v2[level])) {
return sign(noUndefined(v1[level]) - noUndefined(v2[level]));
}
}
return 0;
}
/*
console.log(compare(parse("3.0.0-beta.3"), parse("3.0.0")) === -1 )
console.log(compare(parse("3.0.0-beta.3"), parse("3.0.0-beta.4")) === -1 )
console.log(compare(parse("3.0.0-beta.3"), parse("4.0.0")) === -1 )
*/
export function bumpType(params: { versionBehindStr: string; versionAheadStr: string }): "major" | "minor" | "patch" | "betaPreRelease" | "same" {
const versionAhead = parse(params.versionAheadStr);
const versionBehind = parse(params.versionBehindStr);
if (compare(versionBehind, versionAhead) === 1) {
throw new Error(`Version regression ${versionBehind} -> ${versionAhead}`);
}
for (const level of ["major", "minor", "patch", "betaPreRelease"] as const) {
if (versionBehind[level] !== versionAhead[level]) {
return level;
}
}
return "same";
}
}

View File

@ -0,0 +1,15 @@
import parseArgv from "minimist";
export type CliOptions = {
isSilent: boolean;
hasExternalAssets: boolean;
};
export const getCliOptions = (processArgv: string[]): CliOptions => {
const argv = parseArgv(processArgv);
return {
isSilent: typeof argv["silent"] === "boolean" ? argv["silent"] : false,
hasExternalAssets: typeof argv["external-assets"] === "boolean" ? argv["external-assets"] : false
};
};

54
src/bin/tools/crc32.ts Normal file
View File

@ -0,0 +1,54 @@
import { Readable } from "stream";
const crc32tab = [
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
];
/**
*
* @param input either a byte stream, a string or a buffer, you want to have the checksum for
* @returns a promise for a checksum (uint32)
*/
export function crc32(input: Readable | String | Buffer): Promise<number> {
if (typeof input === "string") {
let crc = ~0;
for (let i = 0; i < input.length; i++) crc = (crc >>> 8) ^ crc32tab[(crc ^ input.charCodeAt(i)) & 0xff];
return Promise.resolve((crc ^ -1) >>> 0);
} else if (input instanceof Buffer) {
let crc = ~0;
for (let i = 0; i < input.length; i++) crc = (crc >>> 8) ^ crc32tab[(crc ^ input[i]) & 0xff];
return Promise.resolve((crc ^ -1) >>> 0);
} else if (input instanceof Readable) {
return new Promise<number>((resolve, reject) => {
let crc = ~0;
input.on("end", () => resolve((crc ^ -1) >>> 0));
input.on("error", e => reject(e));
input.on("data", (chunk: Buffer) => {
for (let i = 0; i < chunk.length; i++) crc = (crc >>> 8) ^ crc32tab[(crc ^ chunk[i]) & 0xff];
});
});
} else {
throw new Error("Unsupported input " + typeof input);
}
}

61
src/bin/tools/deflate.ts Normal file
View File

@ -0,0 +1,61 @@
import { PassThrough, Readable, TransformCallback, Writable } from "stream";
import { pipeline } from "stream/promises";
import { deflateRaw as deflateRawCb, createDeflateRaw } from "zlib";
import { promisify } from "util";
import { crc32 } from "./crc32";
import tee from "./tee";
const deflateRaw = promisify(deflateRawCb);
/**
* A stream transformer that records the number of bytes
* passed in its `size` property.
*/
class ByteCounter extends PassThrough {
size: number = 0;
_transform(chunk: any, encoding: BufferEncoding, callback: TransformCallback) {
if ("length" in chunk) this.size += chunk.length;
super._transform(chunk, encoding, callback);
}
}
/**
* @param data buffer containing the data to be compressed
* @returns a buffer containing the compressed/deflated data and the crc32 checksum
* of the source data
*/
export async function deflateBuffer(data: Buffer) {
const [deflated, checksum] = await Promise.all([deflateRaw(data), crc32(data)]);
return { deflated, crc32: checksum };
}
/**
* @param input a byte stream, containing data to be compressed
* @param sink a method that will accept chunks of compressed data; We don't pass
* a writable here, since we don't want the writablestream to be closed after
* a single file
* @returns a promise, which will resolve with the crc32 checksum and the
* compressed size
*/
export async function deflateStream(input: Readable, sink: (chunk: Buffer) => void) {
const deflateWriter = new Writable({
write(chunk, _, callback) {
sink(chunk);
callback();
}
});
// tee the input stream, so we can compress and calc crc32 in parallel
const [rs1, rs2] = tee(input);
const byteCounter = new ByteCounter();
const [_, crc] = await Promise.all([
// pipe input into zip compressor, count the bytes
// returned and pass compressed data to the sink
pipeline(rs1, createDeflateRaw(), byteCounter, deflateWriter),
// calc checksum
crc32(rs2)
]);
return { crc32: crc, compressedSize: byteCounter.size };
}

View File

@ -1,32 +1,275 @@
import { basename as pathBasename, join as pathJoin } from "path";
import { execSync } from "child_process";
import fs from "fs";
import { dirname as pathDirname, basename as pathBasename, join as pathJoin, join } from "path";
import { createReadStream, createWriteStream } from "fs";
import { stat, mkdir, unlink, writeFile } from "fs/promises";
import { transformCodebase } from "./transformCodebase";
import { rm_rf, rm, rm_r } from "./rm";
import { createHash } from "crypto";
import fetch from "make-fetch-happen";
import { createInflateRaw } from "zlib";
import type { Readable } from "stream";
import { homedir } from "os";
import { FetchOptions } from "make-fetch-happen";
import { exec as execCallback } from "child_process";
import { promisify } from "util";
/** assert url ends with .zip */
export function downloadAndUnzip(params: { url: string; destDirPath: string; pathOfDirToExtractInArchive?: string }) {
const { url, destDirPath, pathOfDirToExtractInArchive } = params;
const exec = promisify(execCallback);
const tmpDirPath = pathJoin(destDirPath, "..", "tmp_xxKdOxnEdx");
const zipFilePath = pathBasename(url);
rm_rf(tmpDirPath);
fs.mkdirSync(tmpDirPath, { "recursive": true });
execSync(`curl -L ${url} -o ${zipFilePath}`, { "cwd": tmpDirPath });
execSync(`unzip ${zipFilePath}${pathOfDirToExtractInArchive === undefined ? "" : ` "${pathOfDirToExtractInArchive}/*"`}`, {
"cwd": tmpDirPath,
});
rm(pathBasename(url), { "cwd": tmpDirPath });
transformCodebase({
"srcDirPath": pathOfDirToExtractInArchive === undefined ? tmpDirPath : pathJoin(tmpDirPath, pathOfDirToExtractInArchive),
destDirPath,
});
rm_r(tmpDirPath);
function hash(s: string) {
return createHash("sha256").update(s).digest("hex");
}
async function maybeStat(path: string) {
try {
return await stat(path);
} catch (error) {
if ((error as Error & { code: string }).code === "ENOENT") return undefined;
throw error;
}
}
/**
* Get an npm configuration value as string, undefined if not set.
*
* @param key
* @returns string or undefined
*/
async function getNmpConfig(key: string): Promise<string | undefined> {
const { stdout } = await exec(`npm config get ${key}`);
const value = stdout.trim();
return value && value !== "null" ? value : undefined;
}
/**
* Get proxy configuration from npm config files. Note that we don't care about
* proxy config in env vars, because make-fetch-happen will do that for us.
*
* @returns proxy configuration
*/
async function getNpmProxyConfig(): Promise<Pick<FetchOptions, "proxy" | "noProxy">> {
const proxy = (await getNmpConfig("https-proxy")) ?? (await getNmpConfig("proxy"));
const noProxy = (await getNmpConfig("noproxy")) ?? (await getNmpConfig("no-proxy"));
return { proxy, noProxy };
}
/**
* Download a file from `url` to `dir`. Will try to avoid downloading existing
* files by using the cache directory ~/.keycloakify/cache
*
* If the target directory does not exist, it will be created.
*
* If the target file exists, it will be overwritten.
*
* We use make-fetch-happen's internal file cache here, so we don't need to
* worry about redownloading the same file over and over. Unfortunately, that
* cache does not have a single file per entry, but bundles and indexes them,
* so we still need to write the contents to the target directory (possibly
* over and over), cause the current unzip implementation wants random access.
*
* @param url download url
* @param dir target directory
* @param filename target filename
* @returns promise for the full path of the downloaded file
*/
async function download(url: string, dir: string, filename: string): Promise<string> {
const proxyOpts = await getNpmProxyConfig();
const cacheRoot = process.env.XDG_CACHE_HOME ?? homedir();
const cachePath = join(cacheRoot, ".keycloakify/cache");
const opts: FetchOptions = { cachePath, ...proxyOpts };
const response = await fetch(url, opts);
const filepath = pathJoin(dir, filename);
await mkdir(dir, { recursive: true });
await writeFile(filepath, response.body);
return filepath;
}
/**
* @typedef
* @type MultiError = Error & { cause: Error[] }
*/
/**
* Extract the archive `zipFile` into the directory `dir`. If `archiveDir` is given,
* only that directory will be extracted, stripping the given path components.
*
* If dir does not exist, it will be created.
*
* If any archive file exists, it will be overwritten.
*
* Will unzip using all available nodejs worker threads.
*
* Will try to clean up extracted files on failure.
*
* If unpacking fails, will either throw an regular error, or
* possibly an `MultiError`, which contains a `cause` field with
* a number of root cause errors.
*
* Warning this method is not optimized for continuous reading of the zip
* archive, but is a trade-off between simplicity and allowing extraction
* of a single directory from the archive.
*
* @param zipFile the file to unzip
* @param dir the target directory
* @param archiveDir if given, unpack only files from this archive directory
* @throws {MultiError} error
* @returns Promise for a list of full file paths pointing to actually extracted files
*/
async function unzip(zipFile: string, dir: string, archiveDir?: string): Promise<string[]> {
await mkdir(dir, { recursive: true });
const promises: Promise<string>[] = [];
// Iterate over all files in the zip, skip files which are not in archiveDir,
// if given.
for await (const record of iterateZipArchive(zipFile)) {
const { path: recordPath, createReadStream: createRecordReadStream } = record;
const filePath = pathJoin(dir, recordPath);
const parent = pathDirname(filePath);
if (archiveDir && !recordPath.startsWith(archiveDir)) continue;
promises.push(
new Promise<string>(async (resolve, reject) => {
await mkdir(parent, { recursive: true });
// Pull the file out of the archive, write it to the target directory
const input = createRecordReadStream();
const output = createWriteStream(filePath);
output.on("error", e => reject(Object.assign(e, { filePath })));
output.on("finish", () => resolve(filePath));
input.pipe(output);
})
);
}
// Wait until _all_ files are either extracted or failed
const results = await Promise.allSettled(promises);
const success = results.filter(r => r.status === "fulfilled").map(r => (r as PromiseFulfilledResult<string>).value);
const failure = results.filter(r => r.status === "rejected").map(r => (r as PromiseRejectedResult).reason);
// If any extraction failed, try to clean up, then throw a MultiError,
// which has a `cause` field, containing a list of root cause errors.
if (failure.length) {
await Promise.all(success.map(path => unlink(path)));
await Promise.all(failure.map(e => e && e.path && unlink(e.path as string)));
const e = new Error("Failed to extract: " + failure.map(e => e.message).join(";"));
(e as any).cause = failure;
throw e;
}
return success;
}
/**
*
* @param file file to read
* @param start first byte to read
* @param end last byte to read
* @returns Promise of a buffer of read bytes
*/
async function readFileChunk(file: string, start: number, end: number): Promise<Buffer> {
const chunks: Buffer[] = [];
return new Promise((resolve, reject) => {
const stream = createReadStream(file, { start, end });
stream.on("error", e => reject(e));
stream.on("end", () => resolve(Buffer.concat(chunks)));
stream.on("data", chunk => chunks.push(chunk as Buffer));
});
}
type ZipRecord = {
path: string;
createReadStream: () => Readable;
compressionMethod: "deflate" | undefined;
};
type ZipRecordGenerator = AsyncGenerator<ZipRecord, void, unknown>;
/**
* Iterate over all records of a zipfile, and yield a ZipRecord.
* Use `record.createReadStream()` to actually read the file.
*
* Warning this method will only work with single-disk zip files.
* Warning this method may fail if the zip archive has an crazy amount
* of files and the central directory is not fully contained within the
* last 65k bytes of the zip file.
*
* @param zipFile
* @returns AsyncGenerator which will yield ZipRecords
*/
async function* iterateZipArchive(zipFile: string): ZipRecordGenerator {
// Need to know zip file size before we can do anything else
const { size } = await stat(zipFile);
const chunkSize = 65_535 + 22 + 1; // max comment size + end header size + wiggle
// Read last ~65k bytes. Zip files have an comment up to 65_535 bytes at the very end,
// before that comes the zip central directory end header.
let chunk = await readFileChunk(zipFile, size - chunkSize, size);
const unread = size - chunk.length;
let i = chunk.length - 4;
let found = false;
// Find central directory end header, reading backwards from the end
while (!found && i-- > 0) if (chunk[i] === 0x50 && chunk.readUInt32LE(i) === 0x06054b50) found = true;
if (!found) throw new Error("Not a zip file");
// This method will fail on a multi-disk zip, so bail early.
if (chunk.readUInt16LE(i + 4) !== 0) throw new Error("Multi-disk zip not supported");
let nFiles = chunk.readUint16LE(i + 10);
// Get the position of the central directory
const directorySize = chunk.readUint32LE(i + 12);
const directoryOffset = chunk.readUint32LE(i + 16);
if (directoryOffset === 0xffff_ffff) throw new Error("zip64 not supported");
if (directoryOffset > size) throw new Error(`Central directory offset ${directoryOffset} is outside file`);
i = directoryOffset - unread;
// If i < 0, it means that the central directory is not contained within `chunk`
if (i < 0) {
chunk = await readFileChunk(zipFile, directoryOffset, directoryOffset + directorySize);
i = 0;
}
// Now iterate the central directory records, yield an `ZipRecord` for every entry
while (nFiles-- > 0) {
// Check for marker bytes
if (chunk.readUInt32LE(i) !== 0x02014b50) throw new Error("No central directory record at position " + (unread + i));
const compressionMethod = ({ 8: "deflate" } as const)[chunk.readUint16LE(i + 10)];
const compressedFileSize = chunk.readUint32LE(i + 20);
const filenameLength = chunk.readUint16LE(i + 28);
const extraLength = chunk.readUint16LE(i + 30);
const commentLength = chunk.readUint16LE(i + 32);
// Start of the actual content byte stream is after the 'local' record header,
// which is 30 bytes long plus filename and extra field
const start = chunk.readUint32LE(i + 42) + 30 + filenameLength + extraLength;
const end = start + compressedFileSize;
const filename = chunk.slice(i + 46, i + 46 + filenameLength).toString("utf-8");
const createRecordReadStream = () => {
const input = createReadStream(zipFile, { start, end });
if (compressionMethod === "deflate") {
const inflate = createInflateRaw();
input.pipe(inflate);
return inflate;
}
return input;
};
if (end > start) yield { path: filename, createReadStream: createRecordReadStream, compressionMethod };
// advance pointer to next central directory entry
i += 46 + filenameLength + extraLength + commentLength;
}
}
export async function downloadAndUnzip({
url,
destDirPath,
pathOfDirToExtractInArchive,
cacheDirPath
}: {
isSilent: boolean;
url: string;
destDirPath: string;
pathOfDirToExtractInArchive?: string;
cacheDirPath: string;
}) {
const downloadHash = hash(JSON.stringify({ url, pathOfDirToExtractInArchive })).substring(0, 15);
const extractDirPath = pathJoin(cacheDirPath, `_${downloadHash}`);
const filename = pathBasename(url);
const zipFilepath = await download(url, cacheDirPath, filename);
const zipMtime = (await stat(zipFilepath)).mtimeMs;
const unzipMtime = (await maybeStat(extractDirPath))?.mtimeMs;
if (!unzipMtime || zipMtime > unzipMtime) await unzip(zipFilepath, extractDirPath, pathOfDirToExtractInArchive);
const srcDirPath = pathOfDirToExtractInArchive === undefined ? extractDirPath : pathJoin(extractDirPath, pathOfDirToExtractInArchive);
transformCodebase({ srcDirPath, destDirPath });
}

View File

@ -2,7 +2,7 @@ import * as fs from "fs";
import * as path from "path";
function getProjectRootRec(dirPath: string): string {
if (fs.existsSync(path.join(dirPath, "tsconfig.json"))) {
if (fs.existsSync(path.join(dirPath, "package.json"))) {
return dirPath;
}
return getProjectRootRec(path.join(dirPath, ".."));

View File

@ -1,9 +1,17 @@
import { getProjectRoot } from "./getProjectRoot";
import { join as pathJoin } from "path";
import child_process from "child_process";
import { constants } from "fs";
import { chmod, stat } from "fs/promises";
Object.entries<string>(require(pathJoin(getProjectRoot(), "package.json"))["bin"]).forEach(([, scriptPath]) =>
child_process.execSync(`chmod +x ${scriptPath}`, {
"cwd": getProjectRoot(),
}),
);
(async () => {
const { bin } = await import(pathJoin(getProjectRoot(), "package.json"));
const promises = Object.values<string>(bin).map(async scriptPath => {
const fullPath = pathJoin(getProjectRoot(), scriptPath);
const oldMode = (await stat(fullPath)).mode;
const newMode = oldMode | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH;
await chmod(fullPath, newMode);
});
await Promise.all(promises);
})();

102
src/bin/tools/jar.ts Normal file
View File

@ -0,0 +1,102 @@
import { Readable, Transform } from "stream";
import { dirname, relative, sep } from "path";
import { createWriteStream } from "fs";
import walk from "./walk";
import type { ZipSource } from "./zip";
import zip from "./zip";
import { mkdir } from "fs/promises";
/** Trim leading whitespace from every line */
const trimIndent = (s: string) => s.replace(/(\n)\s+/g, "$1");
type JarArgs = {
rootPath: string;
targetPath: string;
groupId: string;
artifactId: string;
version: string;
};
/**
* Create a jar archive, using the resources found at `rootPath` (a directory) and write the
* archive to `targetPath` (a file). Use `groupId`, `artifactId` and `version` to define
* the contents of the pom.properties file which is going to be added to the archive.
*/
export default async function jar({ groupId, artifactId, version, rootPath, targetPath }: JarArgs) {
const manifest: ZipSource = {
path: "META-INF/MANIFEST.MF",
data: Buffer.from(
trimIndent(
`Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Keycloakify
Built-By: unknown
Build-Jdk: 19.0.0`
)
)
};
const pomProps: ZipSource = {
path: `META-INF/maven/${groupId}/${artifactId}/pom.properties`,
data: Buffer.from(
trimIndent(
`# Generated by keycloakify
# ${new Date()}
artifactId=${artifactId}
groupId=${groupId}
version=${version}`
)
)
};
/**
* Convert every path entry to a ZipSource record, and when all records are
* processed, append records for MANIFEST.mf and pom.properties
*/
const pathToRecord = () =>
new Transform({
objectMode: true,
transform: function (fsPath, _, cb) {
const path = relative(rootPath, fsPath).split(sep).join("/");
this.push({ path, fsPath });
cb();
},
final: function () {
this.push(manifest);
this.push(pomProps);
this.push(null);
}
});
await mkdir(dirname(targetPath), { recursive: true });
// Create an async pipeline, wait until everything is fully processed
await new Promise<void>((resolve, reject) => {
// walk all files in `rootPath` recursively
Readable.from(walk(rootPath))
// transform every path into a ZipSource object
.pipe(pathToRecord())
// let the zip lib convert all ZipSource objects into a byte stream
.pipe(zip())
// write that byte stream to targetPath
.pipe(createWriteStream(targetPath, { encoding: "binary" }))
.on("finish", () => resolve())
.on("error", e => reject(e));
});
}
/**
* Standalone usage, call e.g. `ts-node jar.ts dirWithSources some-jar.jar`
*/
if (require.main === module) {
const main = () =>
jar({
rootPath: process.argv[2],
targetPath: process.argv[3],
artifactId: process.env.ARTIFACT_ID ?? "artifact",
groupId: process.env.GROUP_ID ?? "group",
version: process.env.VERSION ?? "1.0.0"
});
main().catch(e => console.error(e));
}

27
src/bin/tools/logger.ts Normal file
View File

@ -0,0 +1,27 @@
type LoggerOpts = {
force?: boolean;
};
type Logger = {
log: (message: string, opts?: LoggerOpts) => void;
warn: (message: string) => void;
error: (message: string) => void;
};
export const getLogger = ({ isSilent }: { isSilent?: boolean } = {}): Logger => {
return {
log: (message, { force } = {}) => {
if (isSilent && !force) {
return;
}
console.log(message);
},
warn: message => {
console.warn(message);
},
error: message => {
console.error(message);
}
};
};

View File

@ -0,0 +1,40 @@
import { listTagsFactory } from "./listTags";
import type { Octokit } from "@octokit/rest";
import { NpmModuleVersion } from "../NpmModuleVersion";
export function getLatestsSemVersionedTagFactory(params: { octokit: Octokit }) {
const { octokit } = params;
async function getLatestsSemVersionedTag(params: { owner: string; repo: string; doIgnoreBeta: boolean; count: number }): Promise<
{
tag: string;
version: NpmModuleVersion;
}[]
> {
const { owner, repo, doIgnoreBeta, count } = params;
const semVersionedTags: { tag: string; version: NpmModuleVersion }[] = [];
const { listTags } = listTagsFactory({ octokit });
for await (const tag of listTags({ owner, repo })) {
let version: NpmModuleVersion;
try {
version = NpmModuleVersion.parse(tag.replace(/^[vV]?/, ""));
} catch {
continue;
}
if (doIgnoreBeta && version.betaPreRelease !== undefined) {
continue;
}
semVersionedTags.push({ tag, version });
}
return semVersionedTags.sort(({ version: vX }, { version: vY }) => NpmModuleVersion.compare(vY, vX)).slice(0, count);
}
return { getLatestsSemVersionedTag };
}

View File

@ -0,0 +1,49 @@
import type { Octokit } from "@octokit/rest";
const per_page = 99;
export function listTagsFactory(params: { octokit: Octokit }) {
const { octokit } = params;
const octokit_repo_listTags = async (params: { owner: string; repo: string; per_page: number; page: number }) => {
return octokit.repos.listTags(params);
};
async function* listTags(params: { owner: string; repo: string }): AsyncGenerator<string> {
const { owner, repo } = params;
let page = 1;
while (true) {
const resp = await octokit_repo_listTags({
owner,
repo,
per_page,
"page": page++
});
for (const branch of resp.data.map(({ name }) => name)) {
yield branch;
}
if (resp.data.length < 99) {
break;
}
}
}
/** Returns the same "latest" tag as deno.land/x, not actually the latest though */
async function getLatestTag(params: { owner: string; repo: string }): Promise<string | undefined> {
const { owner, repo } = params;
const itRes = await listTags({ owner, repo }).next();
if (itRes.done) {
return undefined;
}
return itRes.value;
}
return { listTags, getLatestTag };
}

View File

@ -1,31 +0,0 @@
import { execSync } from "child_process";
function rmInternal(params: { pathToRemove: string; args: string | undefined; cwd: string | undefined }) {
const { pathToRemove, args, cwd } = params;
execSync(`rm ${args ? `-${args} ` : ""}${pathToRemove.replace(/ /g, "\\ ")}`, cwd !== undefined ? { cwd } : undefined);
}
export function rm(pathToRemove: string, options?: { cwd: string }) {
rmInternal({
pathToRemove,
"args": undefined,
"cwd": options?.cwd,
});
}
export function rm_r(pathToRemove: string, options?: { cwd: string }) {
rmInternal({
pathToRemove,
"args": "r",
"cwd": options?.cwd,
});
}
export function rm_rf(pathToRemove: string, options?: { cwd: string }) {
rmInternal({
pathToRemove,
"args": "rf",
"cwd": options?.cwd,
});
}

37
src/bin/tools/tee.ts Normal file
View File

@ -0,0 +1,37 @@
import { PassThrough, Readable } from "stream";
export default function tee(input: Readable) {
const a = new PassThrough();
const b = new PassThrough();
let aFull = false;
let bFull = false;
a.on("drain", () => {
aFull = false;
if (!aFull && !bFull) input.resume();
});
b.on("drain", () => {
bFull = false;
if (!aFull && !bFull) input.resume();
});
input.on("error", e => {
a.emit("error", e);
b.emit("error", e);
});
input.on("data", chunk => {
aFull = !a.write(chunk);
bFull = !b.write(chunk);
if (aFull || bFull) input.pause();
});
input.on("end", () => {
a.end();
b.end();
});
return [a, b] as const;
}

View File

@ -16,8 +16,8 @@ export function transformCodebase(params: { srcDirPath: string; destDirPath: str
srcDirPath,
destDirPath,
transformSourceCode = id<TransformSourceCode>(({ sourceCode }) => ({
"modifiedSourceCode": sourceCode,
})),
"modifiedSourceCode": sourceCode
}))
} = params;
for (const file_relative_path of crawl(srcDirPath)) {
@ -25,7 +25,7 @@ export function transformCodebase(params: { srcDirPath: string; destDirPath: str
const transformSourceCodeResult = transformSourceCode({
"sourceCode": fs.readFileSync(filePath),
"filePath": path.join(srcDirPath, file_relative_path),
"filePath": path.join(srcDirPath, file_relative_path)
});
if (transformSourceCodeResult === undefined) {
@ -33,14 +33,14 @@ export function transformCodebase(params: { srcDirPath: string; destDirPath: str
}
fs.mkdirSync(path.dirname(path.join(destDirPath, file_relative_path)), {
"recursive": true,
"recursive": true
});
const { newFileName, modifiedSourceCode } = transformSourceCodeResult;
fs.writeFileSync(
path.join(path.dirname(path.join(destDirPath, file_relative_path)), newFileName ?? path.basename(file_relative_path)),
modifiedSourceCode,
modifiedSourceCode
);
}
}

19
src/bin/tools/walk.ts Normal file
View File

@ -0,0 +1,19 @@
import { readdir } from "fs/promises";
import { resolve } from "path";
/**
* Asynchronously and recursively walk a directory tree, yielding every file and directory
* found
*
* @param root the starting directory
* @returns AsyncGenerator
*/
export default async function* walk(root: string): AsyncGenerator<string, void, void> {
for (const entry of await readdir(root, { withFileTypes: true })) {
const absolutePath = resolve(root, entry.name);
if (entry.isDirectory()) {
yield absolutePath;
yield* walk(absolutePath);
} else yield absolutePath;
}
}

246
src/bin/tools/zip.ts Normal file
View File

@ -0,0 +1,246 @@
import { Transform, TransformOptions } from "stream";
import { createReadStream } from "fs";
import { stat } from "fs/promises";
import { Blob } from "buffer";
import { deflateBuffer, deflateStream } from "./deflate";
/**
* Zip source
* @property filename the name of the entry in the archie
* @property path of the source file, if the source is an actual file
* @property data the actual data buffer, if the source is constructed in-memory
*/
export type ZipSource = { path: string } & ({ fsPath: string } | { data: Buffer });
export type ZipRecord = {
path: string;
compression: "deflate" | undefined;
uncompressedSize: number;
compressedSize?: number;
crc32?: number;
offset?: number;
};
/**
* @returns the actual byte size of an string
*/
function utf8size(s: string) {
return new Blob([s]).size;
}
/**
* @param record
* @returns a buffer representing a Zip local header
* @link https://en.wikipedia.org/wiki/ZIP_(file_format)#Local_file_header
*/
function localHeader(record: ZipRecord) {
const { path, compression, uncompressedSize } = record;
const filenameSize = utf8size(path);
const buf = Buffer.alloc(30 + filenameSize);
buf.writeUInt32LE(0x04_03_4b_50, 0); // local header signature
buf.writeUInt16LE(10, 4); // min version
// we write 0x08 because crc and compressed size are unknown at
buf.writeUInt16LE(0x08, 6); // general purpose bit flag
buf.writeUInt16LE(compression ? ({ "deflate": 8 } as const)[compression] : 0, 8);
buf.writeUInt16LE(0, 10); // modified time
buf.writeUInt16LE(0, 12); // modified date
buf.writeUInt32LE(0, 14); // crc unknown
buf.writeUInt32LE(0, 18); // compressed size unknown
buf.writeUInt32LE(uncompressedSize, 22);
buf.writeUInt16LE(filenameSize, 26);
buf.writeUInt16LE(0, 28); // extra field length
buf.write(path, 30, "utf-8");
return buf;
}
/**
* @param record
* @returns a buffer representing a Zip central header
* @link https://en.wikipedia.org/wiki/ZIP_(file_format)#Central_directory_file_header
*/
function centralHeader(record: ZipRecord) {
const { path, compression, crc32, compressedSize, uncompressedSize, offset } = record;
const filenameSize = utf8size(path);
const buf = Buffer.alloc(46 + filenameSize);
const isFile = !path.endsWith("/");
if (typeof offset === "undefined") throw new Error("Illegal argument");
// we don't want to deal with possibly messed up file or directory
// permissions, so we ignore the original permissions
const externalAttr = isFile ? 0x81a40000 : 0x41ed0000;
buf.writeUInt32LE(0x0201_4b50, 0); // central header signature
buf.writeUInt16LE(10, 4); // version
buf.writeUInt16LE(10, 6); // min version
buf.writeUInt16LE(0, 8); // general purpose bit flag
buf.writeUInt16LE(compression ? ({ "deflate": 8 } as const)[compression] : 0, 10);
buf.writeUInt16LE(0, 12); // modified time
buf.writeUInt16LE(0, 14); // modified date
buf.writeUInt32LE(crc32 || 0, 16);
buf.writeUInt32LE(compressedSize || 0, 20);
buf.writeUInt32LE(uncompressedSize, 24);
buf.writeUInt16LE(filenameSize, 28);
buf.writeUInt16LE(0, 30); // extra field length
buf.writeUInt16LE(0, 32); // comment field length
buf.writeUInt16LE(0, 34); // disk number
buf.writeUInt16LE(0, 36); // internal
buf.writeUInt32LE(externalAttr, 38); // external
buf.writeUInt32LE(offset, 42); // offset where file starts
buf.write(path, 46, "utf-8");
return buf;
}
/**
* @returns a buffer representing an Zip End-Of-Central-Directory block
* @link https://en.wikipedia.org/wiki/ZIP_(file_format)#End_of_central_directory_record_(EOCD)
*/
function eocd({ offset, cdSize, nRecords }: { offset: number; cdSize: number; nRecords: number }) {
const buf = Buffer.alloc(22);
buf.writeUint32LE(0x06054b50, 0); // eocd signature
buf.writeUInt16LE(0, 4); // disc number
buf.writeUint16LE(0, 6); // disc where central directory starts
buf.writeUint16LE(nRecords, 8); // records on this disc
buf.writeUInt16LE(nRecords, 10); // records total
buf.writeUInt32LE(cdSize, 12); // byte size of cd
buf.writeUInt32LE(offset, 16); // cd offset
buf.writeUint16LE(0, 20); // comment length
return buf;
}
/**
* @returns a stream Transform, which reads a stream of ZipRecords and
* writes a bytestream
*/
export default function zip() {
/**
* This is called when the input stream of ZipSource items is finished.
* Will write central directory and end-of-central-direcotry blocks.
*/
const final = () => {
// write central directory
let cdSize = 0;
for (const record of records) {
const head = centralHeader(record);
zipTransform.push(head);
cdSize += head.length;
}
// write end-of-central-directory
zipTransform.push(eocd({ offset, cdSize, nRecords: records.length }));
// signal stream end
zipTransform.push(null);
};
/**
* Write a directory entry to the archive
* @param path
*/
const writeDir = async (path: string) => {
const record: ZipRecord = {
path: path + "/",
offset,
compression: undefined,
uncompressedSize: 0
};
const head = localHeader(record);
zipTransform.push(head);
records.push(record);
offset += head.length;
};
/**
* Write a file entry to the archive
* @param archivePath path of the file in archive
* @param fsPath path to file on filesystem
* @param size of the actual, uncompressed, file
*/
const writeFile = async (archivePath: string, fsPath: string, size: number) => {
const record: ZipRecord = {
path: archivePath,
offset,
compression: "deflate",
uncompressedSize: size
};
const head = localHeader(record);
zipTransform.push(head);
const { crc32, compressedSize } = await deflateStream(createReadStream(fsPath), chunk => zipTransform.push(chunk));
record.crc32 = crc32;
record.compressedSize = compressedSize;
records.push(record);
offset += head.length + compressedSize;
};
/**
* Write archive record based on filesystem file or directory
* @param archivePath path of item in archive
* @param fsPath path to item on filesystem
*/
const writeFromPath = async (archivePath: string, fsPath: string) => {
const fileStats = await stat(fsPath);
fileStats.isDirectory() ? await writeDir(archivePath) /**/ : await writeFile(archivePath, fsPath, fileStats.size) /**/;
};
/**
* Write archive record based on data in a buffer
* @param path
* @param data
*/
const writeFromBuffer = async (path: string, data: Buffer) => {
const { deflated, crc32 } = await deflateBuffer(data);
const record: ZipRecord = {
path,
compression: "deflate",
crc32,
uncompressedSize: data.length,
compressedSize: deflated.length,
offset
};
const head = localHeader(record);
zipTransform.push(head);
zipTransform.push(deflated);
records.push(record);
offset += head.length + deflated.length;
};
/**
* Write an archive record
* @param source
*/
const writeRecord = async (source: ZipSource) => {
if ("fsPath" in source) await writeFromPath(source.path, source.fsPath);
else if ("data" in source) await writeFromBuffer(source.path, source.data);
else throw new Error("Illegal argument " + typeof source + " " + JSON.stringify(source));
};
/**
* The actual stream transform function
* @param source
* @param _ encoding, ignored
* @param cb
*/
const transform: TransformOptions["transform"] = async (source: ZipSource, _, cb) => {
await writeRecord(source);
cb();
};
/** offset and records keep local state during processing */
let offset = 0;
const records: ZipRecord[] = [];
const zipTransform = new Transform({
readableObjectMode: false,
writableObjectMode: true,
transform,
final
});
return zipTransform;
}

11
src/bin/tsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"extends": "../../tsproject.json",
"compilerOptions": {
"module": "CommonJS",
"target": "ES5",
"esModuleInterop": true,
"lib": ["es2015", "DOM", "ES2019.Object"],
"outDir": "../../dist/bin",
"rootDir": "."
}
}

100
src/lib/KcApp.tsx Normal file
View File

@ -0,0 +1,100 @@
import React, { lazy, Suspense } from "react";
import { __unsafe_useI18n as useI18n } from "./i18n";
import DefaultTemplate from "./Template";
import type { KcContextBase } from "./getKcContext/KcContextBase";
import type { PageProps } from "./KcProps";
import type { I18nBase } from "./i18n";
import type { SetOptional } from "./tools/SetOptional";
const Login = lazy(() => import("./pages/Login"));
const Register = lazy(() => import("./pages/Register"));
const RegisterUserProfile = lazy(() => import("./pages/RegisterUserProfile"));
const Info = lazy(() => import("./pages/Info"));
const Error = lazy(() => import("./pages/Error"));
const LoginResetPassword = lazy(() => import("./pages/LoginResetPassword"));
const LoginVerifyEmail = lazy(() => import("./pages/LoginVerifyEmail"));
const Terms = lazy(() => import("./pages/Terms"));
const LoginOtp = lazy(() => import("./pages/LoginOtp"));
const LoginPassword = lazy(() => import("./pages/LoginPassword"));
const LoginUsername = lazy(() => import("./pages/LoginUsername"));
const WebauthnAuthenticate = lazy(() => import("./pages/WebauthnAuthenticate"));
const LoginUpdatePassword = lazy(() => import("./pages/LoginUpdatePassword"));
const LoginUpdateProfile = lazy(() => import("./pages/LoginUpdateProfile"));
const LoginIdpLinkConfirm = lazy(() => import("./pages/LoginIdpLinkConfirm"));
const LoginPageExpired = lazy(() => import("./pages/LoginPageExpired"));
const LoginIdpLinkEmail = lazy(() => import("./pages/LoginIdpLinkEmail"));
const LoginConfigTotp = lazy(() => import("./pages/LoginConfigTotp"));
const LogoutConfirm = lazy(() => import("./pages/LogoutConfirm"));
const UpdateUserProfile = lazy(() => import("./pages/UpdateUserProfile"));
const IdpReviewUserProfile = lazy(() => import("./pages/IdpReviewUserProfile"));
export default function KcApp(props_: SetOptional<PageProps<KcContextBase, I18nBase>, "Template">) {
const { kcContext, i18n: userProvidedI18n, Template = DefaultTemplate, ...kcProps } = props_;
const i18n = (function useClosure() {
const i18n = useI18n({
kcContext,
"extraMessages": {},
"doSkip": userProvidedI18n !== undefined
});
return userProvidedI18n ?? i18n;
})();
if (i18n === null) {
return null;
}
const commonProps = { i18n, Template, ...kcProps };
return (
<Suspense>
{(() => {
switch (kcContext.pageId) {
case "login.ftl":
return <Login {...{ kcContext, ...commonProps }} />;
case "register.ftl":
return <Register {...{ kcContext, ...commonProps }} />;
case "register-user-profile.ftl":
return <RegisterUserProfile {...{ kcContext, ...commonProps }} />;
case "info.ftl":
return <Info {...{ kcContext, ...commonProps }} />;
case "error.ftl":
return <Error {...{ kcContext, ...commonProps }} />;
case "login-reset-password.ftl":
return <LoginResetPassword {...{ kcContext, ...commonProps }} />;
case "login-verify-email.ftl":
return <LoginVerifyEmail {...{ kcContext, ...commonProps }} />;
case "terms.ftl":
return <Terms {...{ kcContext, ...commonProps }} />;
case "login-otp.ftl":
return <LoginOtp {...{ kcContext, ...commonProps }} />;
case "login-username.ftl":
return <LoginUsername {...{ kcContext, ...commonProps }} />;
case "login-password.ftl":
return <LoginPassword {...{ kcContext, ...commonProps }} />;
case "webauthn-authenticate.ftl":
return <WebauthnAuthenticate {...{ kcContext, ...commonProps }} />;
case "login-update-password.ftl":
return <LoginUpdatePassword {...{ kcContext, ...commonProps }} />;
case "login-update-profile.ftl":
return <LoginUpdateProfile {...{ kcContext, ...commonProps }} />;
case "login-idp-link-confirm.ftl":
return <LoginIdpLinkConfirm {...{ kcContext, ...commonProps }} />;
case "login-idp-link-email.ftl":
return <LoginIdpLinkEmail {...{ kcContext, ...commonProps }} />;
case "login-page-expired.ftl":
return <LoginPageExpired {...{ kcContext, ...commonProps }} />;
case "login-config-totp.ftl":
return <LoginConfigTotp {...{ kcContext, ...commonProps }} />;
case "logout-confirm.ftl":
return <LogoutConfirm {...{ kcContext, ...commonProps }} />;
case "update-user-profile.ftl":
return <UpdateUserProfile {...{ kcContext, ...commonProps }} />;
case "idp-review-user-profile.ftl":
return <IdpReviewUserProfile {...{ kcContext, ...commonProps }} />;
}
})()}
</Suspense>
);
}

View File

@ -1,5 +1,8 @@
import { allPropertiesValuesToUndefined } from "../tools/allPropertiesValuesToUndefined";
import { allPropertiesValuesToUndefined } from "./tools/allPropertiesValuesToUndefined";
import { assert } from "tsafe/assert";
import type { KcContextBase } from "./getKcContext";
import type { ReactNode } from "react";
import { I18nBase } from "./i18n";
/** Class names can be provided as an array or separated by whitespace */
export type KcPropsGeneric<CssClasses extends string> = {
@ -37,7 +40,7 @@ export const defaultKcTemplateProps = {
"stylesCommon": [
"node_modules/patternfly/dist/css/patternfly.min.css",
"node_modules/patternfly/dist/css/patternfly-additions.min.css",
"lib/zocial/zocial.css",
"lib/zocial/zocial.css"
],
"styles": ["css/login.css"],
"scripts": [],
@ -60,7 +63,7 @@ export const defaultKcTemplateProps = {
"kcFormGroupClass": ["form-group"],
"kcLabelWrapperClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"],
"kcSignUpClass": ["login-pf-signup"],
"kcInfoAreaWrapperClass": [],
"kcInfoAreaWrapperClass": []
} as const;
assert<typeof defaultKcTemplateProps extends KcTemplateProps ? true : false>();
@ -84,6 +87,7 @@ export type KcProps = KcPropsGeneric<
| "kcFormSocialAccountDoubleListClass"
| "kcFormSocialAccountListLinkClass"
| "kcWebAuthnKeyIcon"
| "kcWebAuthnDefaultIcon"
| "kcFormClass"
| "kcFormGroupErrorClass"
| "kcLabelClass"
@ -105,12 +109,16 @@ export type KcProps = KcPropsGeneric<
| "kcSrOnlyClass"
| "kcSelectAuthListClass"
| "kcSelectAuthListItemClass"
| "kcSelectAuthListItemFillClass"
| "kcSelectAuthListItemInfoClass"
| "kcSelectAuthListItemLeftClass"
| "kcSelectAuthListItemBodyClass"
| "kcSelectAuthListItemDescriptionClass"
| "kcSelectAuthListItemHeadingClass"
| "kcSelectAuthListItemHelpTextClass"
| "kcSelectAuthListItemIconPropertyClass"
| "kcSelectAuthListItemIconClass"
| "kcSelectAuthListItemTitle"
| "kcAuthenticatorDefaultClass"
| "kcAuthenticatorPasswordClass"
| "kcAuthenticatorOTPClass"
@ -138,6 +146,7 @@ export const defaultKcProps = {
"kcFormSocialAccountDoubleListClass": ["login-pf-social-double-col"],
"kcFormSocialAccountListLinkClass": ["login-pf-social-link"],
"kcWebAuthnKeyIcon": ["pficon", "pficon-key"],
"kcWebAuthnDefaultIcon": ["pficon", "pficon-key"],
"kcFormClass": ["form-horizontal"],
"kcFormGroupErrorClass": ["has-error"],
@ -173,6 +182,10 @@ export const defaultKcProps = {
// css classes for select-authenticator form
"kcSelectAuthListClass": ["list-group", "list-view-pf"],
"kcSelectAuthListItemClass": ["list-group-item", "list-view-pf-stacked"],
"kcSelectAuthListItemFillClass": ["pf-l-split__item", "pf-m-fill"],
"kcSelectAuthListItemIconPropertyClass": ["fa-2x", "select-auth-box-icon-properties"],
"kcSelectAuthListItemIconClass": ["pf-l-split__item", "select-auth-box-icon"],
"kcSelectAuthListItemTitle": ["select-auth-box-paragraph"],
"kcSelectAuthListItemInfoClass": ["list-view-pf-main-info"],
"kcSelectAuthListItemLeftClass": ["list-view-pf-left"],
"kcSelectAuthListItemBodyClass": ["list-view-pf-body"],
@ -192,9 +205,32 @@ export const defaultKcProps = {
"kcSelectOTPListItemClass": ["card-pf-body", "card-pf-top-element"],
"kcAuthenticatorOtpCircleClass": ["fa", "fa-mobile", "card-pf-icon-circle"],
"kcSelectOTPItemHeadingClass": ["card-pf-title", "text-center"],
"kcFormOptionsWrapperClass": [],
"kcFormOptionsWrapperClass": []
} as const;
export type TemplateProps<KcContext extends KcContextBase.Common, I18n extends I18nBase> = {
kcContext: KcContext;
i18n: I18n;
doFetchDefaultThemeResources: boolean;
} & {
displayInfo?: boolean;
displayMessage?: boolean;
displayRequiredFields?: boolean;
displayWide?: boolean;
showAnotherWayIfPresent?: boolean;
headerNode: ReactNode;
showUsernameNode?: ReactNode;
formNode: ReactNode;
infoNode?: ReactNode;
} & KcTemplateProps;
export type PageProps<KcContext, I18n extends I18nBase> = {
kcContext: KcContext;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template: (props: TemplateProps<any, any>) => JSX.Element | null;
} & KcProps;
assert<typeof defaultKcProps extends KcProps ? true : false>();
/** Tu use if you don't want any default */

View File

@ -1,36 +1,13 @@
import { useReducer, useEffect, memo } from "react";
import type { ReactNode } from "react";
import { useKcMessage } from "../i18n/useKcMessage";
import { useKcLanguageTag } from "../i18n/useKcLanguageTag";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { assert } from "../tools/assert";
import type { KcLanguageTag } from "../i18n/KcLanguageTag";
import { getBestMatchAmongKcLanguageTag } from "../i18n/KcLanguageTag";
import { getKcLanguageTagLabel } from "../i18n/KcLanguageTag";
import { useCallbackFactory } from "powerhooks/useCallbackFactory";
import { headInsert } from "../tools/headInsert";
import { pathJoin } from "../tools/pathJoin";
import { useConstCallback } from "powerhooks/useConstCallback";
import type { KcTemplateProps } from "./KcProps";
import { useCssAndCx } from "tss-react";
import React, { useReducer, useEffect } from "react";
import { assert } from "./tools/assert";
import { headInsert } from "./tools/headInsert";
import { pathJoin } from "../bin/tools/pathJoin";
import { clsx } from "./tools/clsx";
import type { TemplateProps } from "./KcProps";
import type { KcContextBase } from "./getKcContext/KcContextBase";
import type { I18nBase } from "./i18n";
export type TemplateProps = {
displayInfo?: boolean;
displayMessage?: boolean;
displayRequiredFields?: boolean;
displayWide?: boolean;
showAnotherWayIfPresent?: boolean;
headerNode: ReactNode;
showUsernameNode?: ReactNode;
formNode: ReactNode;
infoNode?: ReactNode;
/** If you write your own page you probably want
* to avoid pulling the default theme assets.
*/
doFetchDefaultThemeResources: boolean;
} & { kcContext: KcContextBase } & KcTemplateProps;
export const Template = memo((props: TemplateProps) => {
export default function Template(props: TemplateProps<KcContextBase.Common, I18nBase>) {
const {
displayInfo = false,
displayMessage = true,
@ -42,123 +19,55 @@ export const Template = memo((props: TemplateProps) => {
formNode,
infoNode = null,
kcContext,
i18n,
doFetchDefaultThemeResources,
stylesCommon,
styles,
scripts,
kcHtmlClass
} = props;
const { cx } = useCssAndCx();
useEffect(() => {
console.log("Rendering this page with react using keycloakify");
}, []);
const { msg } = useKcMessage();
const { kcLanguageTag, setKcLanguageTag } = useKcLanguageTag();
const onChangeLanguageClickFactory = useCallbackFactory(([languageTag]: [KcLanguageTag]) => setKcLanguageTag(languageTag));
const onTryAnotherWayClick = useConstCallback(() => (document.forms["kc-select-try-another-way-form" as never].submit(), false));
const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n;
const { realm, locale, auth, url, message, isAppInitiatedAction } = kcContext;
useEffect(() => {
if (!realm.internationalizationEnabled || kcContext.pageId === "error.ftl") {
return;
}
const { isReady } = usePrepareTemplate({
doFetchDefaultThemeResources,
stylesCommon,
styles,
scripts,
url,
kcHtmlClass
});
assert(locale !== undefined);
if (kcLanguageTag === getBestMatchAmongKcLanguageTag(locale.current)) {
return;
}
window.location.href = locale.supported.find(({ languageTag }) => languageTag === kcLanguageTag)!.url;
}, [kcLanguageTag]);
const [isExtraCssLoaded, setExtraCssLoaded] = useReducer(() => true, false);
useEffect(() => {
if (!doFetchDefaultThemeResources) {
setExtraCssLoaded();
return;
}
let isUnmounted = false;
const cleanups: (() => void)[] = [];
const toArr = (x: string | readonly string[] | undefined) => (typeof x === "string" ? x.split(" ") : x ?? []);
Promise.all(
[
...toArr(props.stylesCommon).map(relativePath => pathJoin(url.resourcesCommonPath, relativePath)),
...toArr(props.styles).map(relativePath => pathJoin(url.resourcesPath, relativePath)),
]
.reverse()
.map(href =>
headInsert({
"type": "css",
href,
"position": "prepend",
}),
),
).then(() => {
if (isUnmounted) {
return;
}
setExtraCssLoaded();
});
toArr(props.scripts).forEach(relativePath =>
headInsert({
"type": "javascript",
"src": pathJoin(url.resourcesPath, relativePath),
}),
);
if (props.kcHtmlClass !== undefined) {
const htmlClassList = document.getElementsByTagName("html")[0].classList;
const tokens = cx(props.kcHtmlClass).split(" ");
htmlClassList.add(...tokens);
cleanups.push(() => htmlClassList.remove(...tokens));
}
return () => {
isUnmounted = true;
cleanups.forEach(f => f());
};
}, [props.kcHtmlClass]);
if (!isExtraCssLoaded) {
if (!isReady) {
return null;
}
return (
<div className={cx(props.kcLoginClass)}>
<div id="kc-header" className={cx(props.kcHeaderClass)}>
<div id="kc-header-wrapper" className={cx(props.kcHeaderWrapperClass)}>
<div className={clsx(props.kcLoginClass)}>
<div id="kc-header" className={clsx(props.kcHeaderClass)}>
<div id="kc-header-wrapper" className={clsx(props.kcHeaderWrapperClass)}>
{msg("loginTitleHtml", realm.displayNameHtml)}
</div>
</div>
<div className={cx(props.kcFormCardClass, displayWide && props.kcFormCardAccountClass)}>
<header className={cx(props.kcFormHeaderClass)}>
<div className={clsx(props.kcFormCardClass, displayWide && props.kcFormCardAccountClass)}>
<header className={clsx(props.kcFormHeaderClass)}>
{realm.internationalizationEnabled && (assert(locale !== undefined), true) && locale.supported.length > 1 && (
<div id="kc-locale">
<div id="kc-locale-wrapper" className={cx(props.kcLocaleWrapperClass)}>
<div id="kc-locale-wrapper" className={clsx(props.kcLocaleWrapperClass)}>
<div className="kc-dropdown" id="kc-locale-dropdown">
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a href="#" id="kc-current-locale-link">
{getKcLanguageTagLabel(kcLanguageTag)}
{labelBySupportedLanguageTag[currentLanguageTag]}
</a>
<ul>
{locale.supported.map(({ languageTag }) => (
<li key={languageTag} className="kc-dropdown-item">
<a href="#" onClick={onChangeLanguageClickFactory(languageTag)}>
{getKcLanguageTagLabel(languageTag)}
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a href="#" onClick={() => changeLocale(languageTag)}>
{labelBySupportedLanguageTag[languageTag]}
</a>
</li>
))}
@ -169,8 +78,8 @@ export const Template = memo((props: TemplateProps) => {
)}
{!(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? (
displayRequiredFields ? (
<div className={cx(props.kcContentWrapperClass)}>
<div className={cx(props.kcLabelWrapperClass, "subtitle")}>
<div className={clsx(props.kcContentWrapperClass)}>
<div className={clsx(props.kcLabelWrapperClass, "subtitle")}>
<span className="subtitle">
<span className="required">*</span>
{msg("requiredFields")}
@ -184,20 +93,20 @@ export const Template = memo((props: TemplateProps) => {
<h1 id="kc-page-title">{headerNode}</h1>
)
) : displayRequiredFields ? (
<div className={cx(props.kcContentWrapperClass)}>
<div className={cx(props.kcLabelWrapperClass, "subtitle")}>
<div className={clsx(props.kcContentWrapperClass)}>
<div className={clsx(props.kcLabelWrapperClass, "subtitle")}>
<span className="subtitle">
<span className="required">*</span> {msg("requiredFields")}
</span>
</div>
<div className="col-md-10">
{showUsernameNode}
<div className={cx(props.kcFormGroupClass)}>
<div className={clsx(props.kcFormGroupClass)}>
<div id="kc-username">
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
<a id="reset-login" href={url.loginRestartFlowUrl}>
<div className="kc-login-tooltip">
<i className={cx(props.kcResetFlowIcon)}></i>
<i className={clsx(props.kcResetFlowIcon)}></i>
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
</div>
</a>
@ -208,12 +117,12 @@ export const Template = memo((props: TemplateProps) => {
) : (
<>
{showUsernameNode}
<div className={cx(props.kcFormGroupClass)}>
<div className={clsx(props.kcFormGroupClass)}>
<div id="kc-username">
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
<a id="reset-login" href={url.loginRestartFlowUrl}>
<div className="kc-login-tooltip">
<i className={cx(props.kcResetFlowIcon)}></i>
<i className={clsx(props.kcResetFlowIcon)}></i>
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
</div>
</a>
@ -226,15 +135,15 @@ export const Template = memo((props: TemplateProps) => {
<div id="kc-content-wrapper">
{/* App-initiated actions should not see warning messages about the need to complete the action during login. */}
{displayMessage && message !== undefined && (message.type !== "warning" || !isAppInitiatedAction) && (
<div className={cx("alert", `alert-${message.type}`)}>
{message.type === "success" && <span className={cx(props.kcFeedbackSuccessIcon)}></span>}
{message.type === "warning" && <span className={cx(props.kcFeedbackWarningIcon)}></span>}
{message.type === "error" && <span className={cx(props.kcFeedbackErrorIcon)}></span>}
{message.type === "info" && <span className={cx(props.kcFeedbackInfoIcon)}></span>}
<div className={clsx("alert", `alert-${message.type}`)}>
{message.type === "success" && <span className={clsx(props.kcFeedbackSuccessIcon)}></span>}
{message.type === "warning" && <span className={clsx(props.kcFeedbackWarningIcon)}></span>}
{message.type === "error" && <span className={clsx(props.kcFeedbackErrorIcon)}></span>}
{message.type === "info" && <span className={clsx(props.kcFeedbackInfoIcon)}></span>}
<span
className="kc-feedback-text"
dangerouslySetInnerHTML={{
"__html": message.summary,
"__html": message.summary
}}
/>
</div>
@ -245,12 +154,20 @@ export const Template = memo((props: TemplateProps) => {
id="kc-select-try-another-way-form"
action={url.loginAction}
method="post"
className={cx(displayWide && props.kcContentWrapperClass)}
className={clsx(displayWide && props.kcContentWrapperClass)}
>
<div className={cx(displayWide && [props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass])}>
<div className={cx(props.kcFormGroupClass)}>
<div className={clsx(displayWide && [props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass])}>
<div className={clsx(props.kcFormGroupClass)}>
<input type="hidden" name="tryAnotherWay" value="on" />
<a href="#" id="try-another-way" onClick={onTryAnotherWayClick}>
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a
href="#"
id="try-another-way"
onClick={() => {
document.forms["kc-select-try-another-way-form" as never].submit();
return false;
}}
>
{msg("doTryAnotherWay")}
</a>
</div>
@ -258,8 +175,8 @@ export const Template = memo((props: TemplateProps) => {
</form>
)}
{displayInfo && (
<div id="kc-info" className={cx(props.kcSignUpClass)}>
<div id="kc-info-wrapper" className={cx(props.kcInfoAreaWrapperClass)}>
<div id="kc-info" className={clsx(props.kcSignUpClass)}>
<div id="kc-info-wrapper" className={clsx(props.kcInfoAreaWrapperClass)}>
{infoNode}
</div>
</div>
@ -269,4 +186,80 @@ export const Template = memo((props: TemplateProps) => {
</div>
</div>
);
});
}
export function usePrepareTemplate(params: {
doFetchDefaultThemeResources: boolean;
stylesCommon: string | readonly string[] | undefined;
styles: string | readonly string[] | undefined;
scripts: string | readonly string[] | undefined;
url: {
resourcesCommonPath: string;
resourcesPath: string;
};
kcHtmlClass: string | readonly string[] | undefined;
}) {
const { doFetchDefaultThemeResources, stylesCommon, styles, url, scripts, kcHtmlClass } = params;
const [isReady, setReady] = useReducer(() => true, !doFetchDefaultThemeResources);
useEffect(() => {
if (!doFetchDefaultThemeResources) {
return;
}
let isUnmounted = false;
const toArr = (x: string | readonly string[] | undefined) => (typeof x === "string" ? x.split(" ") : x ?? []);
Promise.all(
[
...toArr(stylesCommon).map(relativePath => pathJoin(url.resourcesCommonPath, relativePath)),
...toArr(styles).map(relativePath => pathJoin(url.resourcesPath, relativePath))
]
.reverse()
.map(href =>
headInsert({
"type": "css",
href,
"position": "prepend"
})
)
).then(() => {
if (isUnmounted) {
return;
}
setReady();
});
toArr(scripts).forEach(relativePath =>
headInsert({
"type": "javascript",
"src": pathJoin(url.resourcesPath, relativePath)
})
);
return () => {
isUnmounted = true;
};
}, [kcHtmlClass]);
useEffect(() => {
if (kcHtmlClass === undefined) {
return;
}
const htmlClassList = document.getElementsByTagName("html")[0].classList;
const tokens = clsx(kcHtmlClass).split(" ");
htmlClassList.add(...tokens);
return () => {
htmlClassList.remove(...tokens);
};
}, [kcHtmlClass]);
return { isReady };
}

View File

@ -1,47 +0,0 @@
import { memo } from "react";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import type { KcProps } from "./KcProps";
import { Login } from "./Login";
import { Register } from "./Register";
import { RegisterUserProfile } from "./RegisterUserProfile";
import { Info } from "./Info";
import { Error } from "./Error";
import { LoginResetPassword } from "./LoginResetPassword";
import { LoginVerifyEmail } from "./LoginVerifyEmail";
import { Terms } from "./Terms";
import { LoginOtp } from "./LoginOtp";
import { LoginUpdatePassword } from "./LoginUpdatePassword";
import { LoginUpdateProfile } from "./LoginUpdateProfile";
import { LoginIdpLinkConfirm } from "./LoginIdpLinkConfirm";
import { LoginPageExpired } from "./LoginPageExpired";
export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContextBase } & KcProps) => {
switch (kcContext.pageId) {
case "login.ftl":
return <Login {...{ kcContext, ...props }} />;
case "register.ftl":
return <Register {...{ kcContext, ...props }} />;
case "register-user-profile.ftl":
return <RegisterUserProfile {...{ kcContext, ...props }} />;
case "info.ftl":
return <Info {...{ kcContext, ...props }} />;
case "error.ftl":
return <Error {...{ kcContext, ...props }} />;
case "login-reset-password.ftl":
return <LoginResetPassword {...{ kcContext, ...props }} />;
case "login-verify-email.ftl":
return <LoginVerifyEmail {...{ kcContext, ...props }} />;
case "terms.ftl":
return <Terms {...{ kcContext, ...props }} />;
case "login-otp.ftl":
return <LoginOtp {...{ kcContext, ...props }} />;
case "login-update-password.ftl":
return <LoginUpdatePassword {...{ kcContext, ...props }} />;
case "login-update-profile.ftl":
return <LoginUpdateProfile {...{ kcContext, ...props }} />;
case "login-idp-link-confirm.ftl":
return <LoginIdpLinkConfirm {...{ kcContext, ...props }} />;
case "login-page-expired.ftl":
return <LoginPageExpired {...{ kcContext, ...props }} />;
}
});

View File

@ -1,46 +0,0 @@
import { memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useKcMessage } from "../i18n/useKcMessage";
import { useCssAndCx } from "tss-react";
export const LoginIdpLinkConfirm = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginIdpLinkConfirm } & KcProps) => {
const { url, idpAlias } = kcContext;
const { msg } = useKcMessage();
const { cx } = useCssAndCx();
return (
<Template
{...{ kcContext, ...props }}
doFetchDefaultThemeResources={true}
headerNode={msg("confirmLinkIdpTitle")}
formNode={
<form id="kc-register-form" action={url.loginAction} method="post">
<div className={cx(props.kcFormGroupClass)}>
<button
type="submit"
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
name="submitAction"
id="updateProfile"
value="updateProfile"
>
{msg("confirmLinkIdpReviewProfile")}
</button>
<button
type="submit"
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
name="submitAction"
id="linkAccount"
value="linkAccount"
>
{msg("confirmLinkIdpContinue", idpAlias)}
</button>
</div>
</form>
}
/>
);
});

View File

@ -1,66 +0,0 @@
import { memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useKcMessage } from "../i18n/useKcMessage";
import { useCssAndCx } from "tss-react";
export const LoginResetPassword = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginResetPassword } & KcProps) => {
const { url, realm, auth } = kcContext;
const { msg, msgStr } = useKcMessage();
const { cx } = useCssAndCx();
return (
<Template
{...{ kcContext, ...props }}
doFetchDefaultThemeResources={true}
displayMessage={false}
headerNode={msg("emailForgotTitle")}
formNode={
<form id="kc-reset-password-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
<div className={cx(props.kcFormGroupClass)}>
<div className={cx(props.kcLabelWrapperClass)}>
<label htmlFor="username" className={cx(props.kcLabelClass)}>
{!realm.loginWithEmailAllowed
? msg("username")
: !realm.registrationEmailAsUsername
? msg("usernameOrEmail")
: msg("email")}
</label>
</div>
<div className={cx(props.kcInputWrapperClass)}>
<input
type="text"
id="username"
name="username"
className={cx(props.kcInputClass)}
autoFocus
defaultValue={auth !== undefined && auth.showUsername ? auth.attemptedUsername : undefined}
/>
</div>
</div>
<div className={cx(props.kcFormGroupClass, props.kcFormSettingClass)}>
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
<div className={cx(props.kcFormOptionsWrapperClass)}>
<span>
<a href={url.loginUrl}>{msg("backToLogin")}</a>
</span>
</div>
</div>
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
<input
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
type="submit"
value={msgStr("doSubmit")}
/>
</div>
</div>
</form>
}
infoNode={msg("emailInstruction")}
/>
);
});

View File

@ -1,120 +0,0 @@
import { memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useKcMessage } from "../i18n/useKcMessage";
import { useCssAndCx } from "tss-react";
export const LoginUpdateProfile = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginUpdateProfile } & KcProps) => {
const { cx } = useCssAndCx();
const { msg, msgStr } = useKcMessage();
const { url, user, messagesPerField, isAppInitiatedAction } = kcContext;
return (
<Template
{...{ kcContext, ...props }}
doFetchDefaultThemeResources={true}
headerNode={msg("loginProfileTitle")}
formNode={
<form id="kc-update-profile-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
{user.editUsernameAllowed && (
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("username", props.kcFormGroupErrorClass))}>
<div className={cx(props.kcLabelWrapperClass)}>
<label htmlFor="username" className={cx(props.kcLabelClass)}>
{msg("username")}
</label>
</div>
<div className={cx(props.kcInputWrapperClass)}>
<input
type="text"
id="username"
name="username"
defaultValue={user.username ?? ""}
className={cx(props.kcInputClass)}
/>
</div>
</div>
)}
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("email", props.kcFormGroupErrorClass))}>
<div className={cx(props.kcLabelWrapperClass)}>
<label htmlFor="email" className={cx(props.kcLabelClass)}>
{msg("email")}
</label>
</div>
<div className={cx(props.kcInputWrapperClass)}>
<input type="text" id="email" name="email" defaultValue={user.email ?? ""} className={cx(props.kcInputClass)} />
</div>
</div>
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("firstName", props.kcFormGroupErrorClass))}>
<div className={cx(props.kcLabelWrapperClass)}>
<label htmlFor="firstName" className={cx(props.kcLabelClass)}>
{msg("firstName")}
</label>
</div>
<div className={cx(props.kcInputWrapperClass)}>
<input
type="text"
id="firstName"
name="firstName"
defaultValue={user.firstName ?? ""}
className={cx(props.kcInputClass)}
/>
</div>
</div>
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("lastName", props.kcFormGroupErrorClass))}>
<div className={cx(props.kcLabelWrapperClass)}>
<label htmlFor="lastName" className={cx(props.kcLabelClass)}>
{msg("lastName")}
</label>
</div>
<div className={cx(props.kcInputWrapperClass)}>
<input type="text" id="lastName" name="lastName" defaultValue={user.lastName ?? ""} className={cx(props.kcInputClass)} />
</div>
</div>
<div className={cx(props.kcFormGroupClass)}>
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
<div className={cx(props.kcFormOptionsWrapperClass)} />
</div>
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
{isAppInitiatedAction ? (
<>
<input
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
type="submit"
defaultValue={msgStr("doSubmit")}
/>
<button
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonLargeClass)}
type="submit"
name="cancel-aia"
value="true"
>
{msg("doCancel")}
</button>
</>
) : (
<input
className={cx(
props.kcButtonClass,
props.kcButtonPrimaryClass,
props.kcButtonBlockClass,
props.kcButtonLargeClass,
)}
type="submit"
defaultValue={msgStr("doSubmit")}
/>
)}
</div>
</div>
</form>
}
/>
);
});

View File

@ -1,30 +0,0 @@
import { memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useKcMessage } from "../i18n/useKcMessage";
export const LoginVerifyEmail = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginVerifyEmail } & KcProps) => {
const { msg } = useKcMessage();
const { url } = kcContext;
return (
<Template
{...{ kcContext, ...props }}
doFetchDefaultThemeResources={true}
displayMessage={false}
headerNode={msg("emailVerifyTitle")}
formNode={
<>
<p className="instruction">{msg("emailVerifyInstruction1")}</p>
<p className="instruction">
{msg("emailVerifyInstruction2")}
<a href={url.loginAction}>{msg("doClickHere")}</a>
{msg("emailVerifyInstruction3")}
</p>
</>
}
/>
);
});

View File

@ -1,217 +0,0 @@
import { useMemo, memo, useEffect, useState, Fragment } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase, Attribute } from "../getKcContext/KcContextBase";
import { useKcMessage } from "../i18n/useKcMessage";
import { useCssAndCx } from "tss-react";
import type { ReactComponent } from "../tools/ReactComponent";
import { useCallbackFactory } from "powerhooks/useCallbackFactory";
import { useFormValidationSlice } from "../useFormValidationSlice";
export const RegisterUserProfile = memo(({ kcContext, ...props_ }: { kcContext: KcContextBase.RegisterUserProfile } & KcProps) => {
const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
const { msg, msgStr } = useKcMessage();
const { cx, css } = useCssAndCx();
const props = useMemo(
() => ({
...props_,
"kcFormGroupClass": cx(props_.kcFormGroupClass, css({ "marginBottom": 20 })),
}),
[cx, css],
);
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
return (
<Template
{...{ kcContext, ...props }}
displayMessage={messagesPerField.exists("global")}
displayRequiredFields={true}
doFetchDefaultThemeResources={true}
headerNode={msg("registerTitle")}
formNode={
<form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} {...props} />
{recaptchaRequired && (
<div className="form-group">
<div className={cx(props.kcInputWrapperClass)}>
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey} />
</div>
</div>
)}
<div className={cx(props.kcFormGroupClass)}>
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
<div className={cx(props.kcFormOptionsWrapperClass)}>
<span>
<a href={url.loginUrl}>{msg("backToLogin")}</a>
</span>
</div>
</div>
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
<input
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
type="submit"
value={msgStr("doRegister")}
disabled={!isFomSubmittable}
/>
</div>
</div>
</form>
}
/>
);
});
type UserProfileFormFieldsProps = { kcContext: KcContextBase.RegisterUserProfile } & KcProps &
Partial<Record<"BeforeField" | "AfterField", ReactComponent<{ attribute: Attribute }>>> & {
onIsFormSubmittableValueChange: (isFormSubmittable: boolean) => void;
};
const UserProfileFormFields = memo(({ kcContext, onIsFormSubmittableValueChange, ...props }: UserProfileFormFieldsProps) => {
const { cx, css } = useCssAndCx();
const { advancedMsg } = useKcMessage();
const {
formValidationState: { fieldStateByAttributeName, isFormSubmittable },
formValidationReducer,
attributesWithPassword,
} = useFormValidationSlice({
kcContext,
});
useEffect(() => {
onIsFormSubmittableValueChange(isFormSubmittable);
}, [isFormSubmittable]);
const onChangeFactory = useCallbackFactory(
(
[name]: [string],
[
{
target: { value },
},
]: [React.ChangeEvent<HTMLInputElement | HTMLSelectElement>],
) =>
formValidationReducer({
"action": "update value",
name,
"newValue": value,
}),
);
const onBlurFactory = useCallbackFactory(([name]: [string]) =>
formValidationReducer({
"action": "focus lost",
name,
}),
);
let currentGroup = "";
return (
<>
{attributesWithPassword.map((attribute, i) => {
const { group = "", groupDisplayHeader = "", groupDisplayDescription = "" } = attribute;
const { value, displayableErrors } = fieldStateByAttributeName[attribute.name];
const formGroupClassName = cx(props.kcFormGroupClass, displayableErrors.length !== 0 && props.kcFormGroupErrorClass);
return (
<Fragment key={i}>
{group !== currentGroup && (currentGroup = group) !== "" && (
<div className={formGroupClassName}>
<div className={cx(props.kcContentWrapperClass)}>
<label id={`header-${group}`} className={cx(props.kcFormGroupHeader)}>
{advancedMsg(groupDisplayHeader) || currentGroup}
</label>
</div>
{groupDisplayDescription !== "" && (
<div className={cx(props.kcLabelWrapperClass)}>
<label id={`description-${group}`} className={`${cx(props.kcLabelClass)}`}>
{advancedMsg(groupDisplayDescription)}
</label>
</div>
)}
</div>
)}
<div className={formGroupClassName}>
<div className={cx(props.kcLabelWrapperClass)}>
<label htmlFor={attribute.name} className={cx(props.kcLabelClass)}>
{advancedMsg(attribute.displayName ?? "")}
</label>
{attribute.required && <>*</>}
</div>
<div className={cx(props.kcInputWrapperClass)}>
{(() => {
const { options } = attribute.validators;
if (options !== undefined) {
return (
<select
id={attribute.name}
name={attribute.name}
onChange={onChangeFactory(attribute.name)}
onBlur={onBlurFactory(attribute.name)}
value={value}
>
{options.options.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
);
}
return (
<input
type={(() => {
switch (attribute.name) {
case "password-confirm":
case "password":
return "password";
default:
return "text";
}
})()}
id={attribute.name}
name={attribute.name}
value={value}
onChange={onChangeFactory(attribute.name)}
className={cx(props.kcInputClass)}
aria-invalid={displayableErrors.length !== 0}
disabled={attribute.readOnly}
autoComplete={attribute.autocomplete}
onBlur={onBlurFactory(attribute.name)}
/>
);
})()}
{displayableErrors.length !== 0 && (
<span
id={`input-error-${attribute.name}`}
className={cx(
props.kcInputErrorMessageClass,
css({
"position": displayableErrors.length === 1 ? "absolute" : undefined,
"& > span": { "display": "block" },
}),
)}
aria-live="polite"
>
{displayableErrors.map(({ errorMessage }) => errorMessage)}
</span>
)}
</div>
</div>
</Fragment>
);
})}
</>
);
});

View File

@ -1,51 +0,0 @@
import { memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useKcMessage } from "../i18n/useKcMessage";
import { useCssAndCx } from "tss-react";
export const Terms = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Terms } & KcProps) => {
const { msg, msgStr } = useKcMessage();
const { cx } = useCssAndCx();
const { url } = kcContext;
return (
<Template
{...{ kcContext, ...props }}
doFetchDefaultThemeResources={true}
displayMessage={false}
headerNode={msg("termsTitle")}
formNode={
<>
<div id="kc-terms-text">{msg("termsText")}</div>
<form className="form-actions" action={url.loginAction} method="POST">
<input
className={cx(
props.kcButtonClass,
props.kcButtonClass,
props.kcButtonClass,
props.kcButtonPrimaryClass,
props.kcButtonLargeClass,
)}
name="accept"
id="kc-accept"
type="submit"
value={msgStr("doAccept")}
/>
<input
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonLargeClass)}
name="cancel"
id="kc-decline"
type="submit"
value={msgStr("doDecline")}
/>
</form>
<div className="clearfix" />
</>
}
/>
);
});

View File

@ -1,9 +1,8 @@
import type { PageId } from "../../bin/build-keycloak-theme/generateFtl";
import type { KcLanguageTag } from "../i18n/KcLanguageTag";
import type { PageId } from "../../bin/keycloakify/generateFtl";
import { assert } from "tsafe/assert";
import type { Equals } from "tsafe";
import type { MessageKey } from "../i18n/useKcMessage";
import type { LanguageLabel } from "../i18n/KcLanguageTag";
import type { MessageKeyBase } from "../i18n";
import type { KcTemplateClassKey } from "../KcProps";
type ExtractAfterStartingWith<Prefix extends string, StrEnum> = StrEnum extends `${Prefix}${infer U}` ? U : never;
@ -21,10 +20,28 @@ export type KcContextBase =
| KcContextBase.LoginVerifyEmail
| KcContextBase.Terms
| KcContextBase.LoginOtp
| KcContextBase.LoginUsername
| KcContextBase.WebauthnAuthenticate
| KcContextBase.LoginPassword
| KcContextBase.LoginUpdatePassword
| KcContextBase.LoginUpdateProfile
| KcContextBase.LoginIdpLinkConfirm
| KcContextBase.LoginPageExpired;
| KcContextBase.LoginIdpLinkEmail
| KcContextBase.LoginPageExpired
| KcContextBase.LoginConfigTotp
| KcContextBase.LogoutConfirm
| KcContextBase.UpdateUserProfile
| KcContextBase.IdpReviewUserProfile;
export type WebauthnAuthenticator = {
credentialId: string;
transports: {
iconClass: KcTemplateClassKey;
displayNameProperties: MessageKeyBase[];
};
label: string;
createdAt: string;
};
export declare namespace KcContextBase {
export type Common = {
@ -46,18 +63,15 @@ export declare namespace KcContextBase {
locale?: {
supported: {
url: string;
languageTag: KcLanguageTag;
/** Is determined by languageTag. Ex: languageTag === "en" => label === "English"
* or getLanguageLabel(languageTag) === label
*/
//label: LanguageLabel;
label: string;
languageTag: string;
}[];
current: LanguageLabel;
currentLanguageTag: string;
};
auth?: {
showUsername: boolean;
showResetCredentials: boolean;
showTryAnotherWayLink: boolean;
showUsername?: boolean;
showResetCredentials?: boolean;
showTryAnotherWayLink?: boolean;
attemptedUsername?: string;
};
scripts: string[];
@ -155,7 +169,7 @@ export declare namespace KcContextBase {
export type Info = Common & {
pageId: "info.ftl";
messageHeader?: string;
requiredActions?: ExtractAfterStartingWith<"requiredAction.", MessageKey>[];
requiredActions?: ExtractAfterStartingWith<"requiredAction.", MessageKeyBase>[];
skipLink: boolean;
pageRedirectUri?: string;
actionUri?: string;
@ -181,6 +195,10 @@ export declare namespace KcContextBase {
export type LoginVerifyEmail = Common & {
pageId: "login-verify-email.ftl";
//NOTE: Optional because maybe it wasn't defined in older keycloak versions.
user?: {
email: string;
};
};
export type Terms = Common & {
@ -194,6 +212,77 @@ export declare namespace KcContextBase {
};
};
export type LoginUsername = Common & {
pageId: "login-username.ftl";
url: {
loginResetCredentialsUrl: string;
registrationUrl: string;
};
realm: {
loginWithEmailAllowed: boolean;
rememberMe: boolean;
password: boolean;
resetPasswordAllowed: boolean;
registrationAllowed: boolean;
};
registrationDisabled: boolean;
login: {
username?: string;
rememberMe?: boolean;
};
usernameHidden?: boolean;
social: {
displayInfo: boolean;
providers?: {
loginUrl: string;
alias: string;
providerId: string;
displayName: string;
}[];
};
};
export type LoginPassword = Common & {
pageId: "login-password.ftl";
url: {
loginResetCredentialsUrl: string;
registrationUrl: string;
};
realm: {
resetPasswordAllowed: boolean;
};
auth?: {
showUsername?: boolean;
showResetCredentials?: boolean;
showTryAnotherWayLink?: boolean;
attemptedUsername?: string;
};
social: {
displayInfo: boolean;
};
login: {
password?: string;
};
};
export type WebauthnAuthenticate = Common & {
pageId: "webauthn-authenticate.ftl";
authenticators: {
authenticators: WebauthnAuthenticator[];
};
challenge: string;
// I hate this:
userVerification: UserVerificationRequirement | "not specified";
rpId: string;
createTimeout: string;
isUserIdentified: "true" | "false";
shouldDisplayAuthenticators: boolean;
social: {
displayInfo: boolean;
};
login: {};
};
export type LoginUpdatePassword = Common & {
pageId: "login-update-password.ftl";
username: string;
@ -215,9 +304,76 @@ export declare namespace KcContextBase {
idpAlias: string;
};
export type LoginIdpLinkEmail = Common & {
pageId: "login-idp-link-email.ftl";
brokerContext: {
username: string;
};
idpAlias: string;
};
export type LoginPageExpired = Common & {
pageId: "login-page-expired.ftl";
};
export type LoginConfigTotp = Common & {
pageId: "login-config-totp.ftl";
mode?: "qr" | "manual" | undefined | null;
totp: {
totpSecretEncoded: string;
qrUrl: string;
policy: {
supportedApplications: string[];
algorithm: "HmacSHA1" | "HmacSHA256" | "HmacSHA512";
digits: number;
lookAheadWindow: number;
} & (
| {
type: "totp";
period: number;
}
| {
type: "hotp";
initialCounter: number;
}
);
totpSecretQrCode: string;
manualUrl: string;
totpSecret: string;
otpCredentials: { id: string; userLabel: string }[];
};
};
export type LogoutConfirm = Common & {
pageId: "logout-confirm.ftl";
url: {
logoutConfirmAction: string;
};
client: {
baseUrl?: string;
};
logoutConfirm: {
code: string;
skipLink?: boolean;
};
};
export type UpdateUserProfile = Common & {
pageId: "update-user-profile.ftl";
profile: {
attributes: Attribute[];
attributesByName: Record<string, Attribute>;
};
};
export type IdpReviewUserProfile = Common & {
pageId: "idp-review-user-profile.ftl";
profile: {
context: "IDP_REVIEW";
attributes: Attribute[];
attributesByName: Record<string, Attribute>;
};
};
}
export type Attribute = {

View File

@ -7,9 +7,10 @@ import { exclude } from "tsafe/exclude";
import { assert } from "tsafe/assert";
import type { ExtendsKcContextBase } from "./getKcContextFromWindow";
import { getKcContextFromWindow } from "./getKcContextFromWindow";
import { pathJoin } from "../tools/pathJoin";
import { pathJoin } from "../../bin/tools/pathJoin";
import { pathBasename } from "../tools/pathBasename";
import { resourcesCommonPath } from "./kcContextMocks/urlResourcesPath";
import { mockTestingResourcesCommonPath } from "../../bin/mockTestingResourcesPath";
import { symToStr } from "tsafe/symToStr";
export function getKcContext<KcContextExtended extends { pageId: string } = never>(params?: {
mockPageId?: ExtendsKcContextBase<KcContextExtended>["pageId"];
@ -17,9 +18,19 @@ export function getKcContext<KcContextExtended extends { pageId: string } = neve
}): { kcContext: ExtendsKcContextBase<KcContextExtended> | undefined } {
const { mockPageId, mockData } = params ?? {};
if (mockPageId !== undefined) {
const realKcContext = getKcContextFromWindow<KcContextExtended>();
if (mockPageId !== undefined && realKcContext === undefined) {
//TODO maybe trow if no mock fo custom page
console.log(
[
`%cKeycloakify: ${symToStr({ mockPageId })} set to ${mockPageId}.`,
`If assets are missing make sure you have built your Keycloak theme at least once.`
].join(" "),
"background: red; color: yellow; font-size: medium"
);
const kcContextDefaultMock = kcContextMocks.find(({ pageId }) => pageId === mockPageId);
const partialKcContextCustomMock = mockData?.find(({ pageId }) => pageId === mockPageId);
@ -29,8 +40,8 @@ export function getKcContext<KcContextExtended extends { pageId: string } = neve
[
`WARNING: You declared the non build in page ${mockPageId} but you didn't `,
`provide mock data needed to debug the page outside of Keycloak as you are trying to do now.`,
`Please check the documentation of the getKcContext function`,
].join("\n"),
`Please check the documentation of the getKcContext function`
].join("\n")
);
}
@ -38,17 +49,25 @@ export function getKcContext<KcContextExtended extends { pageId: string } = neve
deepAssign({
"target": kcContext,
"source": kcContextDefaultMock !== undefined ? kcContextDefaultMock : { "pageId": mockPageId, ...kcContextCommonMock },
"source": kcContextDefaultMock !== undefined ? kcContextDefaultMock : { "pageId": mockPageId, ...kcContextCommonMock }
});
if (partialKcContextCustomMock !== undefined) {
deepAssign({
"target": kcContext,
"source": partialKcContextCustomMock,
"source": partialKcContextCustomMock
});
if (partialKcContextCustomMock.pageId === "register-user-profile.ftl") {
assert(kcContextDefaultMock?.pageId === "register-user-profile.ftl");
if (
partialKcContextCustomMock.pageId === "register-user-profile.ftl" ||
partialKcContextCustomMock.pageId === "update-user-profile.ftl" ||
partialKcContextCustomMock.pageId === "idp-review-user-profile.ftl"
) {
assert(
kcContextDefaultMock?.pageId === "register-user-profile.ftl" ||
kcContextDefaultMock?.pageId === "update-user-profile.ftl" ||
kcContextDefaultMock?.pageId === "idp-review-user-profile.ftl"
);
const { attributes } = kcContextDefaultMock.profile;
@ -56,7 +75,7 @@ export function getKcContext<KcContextExtended extends { pageId: string } = neve
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributesByName = {};
const partialAttributes = [
...((partialKcContextCustomMock as DeepPartial<KcContextBase.RegisterUserProfile>).profile?.attributes ?? []),
...((partialKcContextCustomMock as DeepPartial<KcContextBase.RegisterUserProfile>).profile?.attributes ?? [])
].filter(exclude(undefined));
attributes.forEach(attribute => {
@ -66,7 +85,7 @@ export function getKcContext<KcContextExtended extends { pageId: string } = neve
deepAssign({
"target": augmentedAttribute,
"source": attribute,
"source": attribute
});
if (partialAttribute !== undefined) {
@ -74,7 +93,7 @@ export function getKcContext<KcContextExtended extends { pageId: string } = neve
deepAssign({
"target": augmentedAttribute,
"source": partialAttribute,
"source": partialAttribute
});
}
@ -82,27 +101,31 @@ export function getKcContext<KcContextExtended extends { pageId: string } = neve
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributesByName[augmentedAttribute.name] = augmentedAttribute;
});
partialAttributes.forEach(partialAttribute => {
const { name } = partialAttribute;
partialAttributes
.map(partialAttribute => ({ "validators": {}, ...partialAttribute }))
.forEach(partialAttribute => {
const { name } = partialAttribute;
assert(name !== undefined, "If you define a mock attribute it must have at least a name");
assert(name !== undefined, "If you define a mock attribute it must have at least a name");
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributes.push(partialAttribute as any);
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributesByName[name] = partialAttribute as any;
});
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributes.push(partialAttribute as any);
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributesByName[name] = partialAttribute as any;
});
}
}
return { kcContext };
}
const kcContext = getKcContextFromWindow<KcContextExtended>();
if (kcContext !== undefined) {
const { url } = kcContext;
url.resourcesCommonPath = pathJoin(url.resourcesPath, pathBasename(resourcesCommonPath));
if (realKcContext === undefined) {
return { "kcContext": undefined };
}
return { kcContext };
{
const { url } = realKcContext;
url.resourcesCommonPath = pathJoin(url.resourcesPath, pathBasename(mockTestingResourcesCommonPath));
}
return { "kcContext": realKcContext };
}

View File

@ -1,6 +1,6 @@
import type { KcContextBase } from "./KcContextBase";
import type { AndByDiscriminatingKey } from "../tools/AndByDiscriminatingKey";
import { ftlValuesGlobalName } from "../../bin/build-keycloak-theme/ftlValuesGlobalName";
import { ftlValuesGlobalName } from "../../bin/keycloakify/ftlValuesGlobalName";
export type ExtendsKcContextBase<KcContextExtended extends { pageId: string }> = [KcContextExtended] extends [never]
? KcContextBase

View File

@ -0,0 +1,500 @@
import "minimal-polyfills/Object.fromEntries";
import type { KcContextBase, Attribute } from "./KcContextBase";
//NOTE: Aside because we want to be able to import them from node
import { mockTestingResourcesCommonPath, mockTestingResourcesPath } from "../../bin/mockTestingResourcesPath";
import { id } from "tsafe/id";
import { pathJoin } from "../../bin/tools/pathJoin";
const PUBLIC_URL = process.env["PUBLIC_URL"] ?? "/";
const attributes: Attribute[] = [
{
"validators": {
"username-prohibited-characters": {
"ignore.empty.value": true
},
"up-username-has-value": {},
"length": {
"ignore.empty.value": true,
"min": "3",
"max": "255"
},
"up-duplicate-username": {},
"up-username-mutation": {}
},
"displayName": "${username}",
"annotations": {},
"required": true,
"groupAnnotations": {},
"autocomplete": "username",
"readOnly": false,
"name": "username",
"value": "xxxx"
},
{
"validators": {
"up-email-exists-as-username": {},
"length": {
"max": "255",
"ignore.empty.value": true
},
"up-blank-attribute-value": {
"error-message": "missingEmailMessage",
"fail-on-null": false
},
"up-duplicate-email": {},
"email": {
"ignore.empty.value": true
},
"pattern": {
"ignore.empty.value": true,
"pattern": "gmail\\.com$"
}
},
"displayName": "${email}",
"annotations": {},
"required": true,
"groupAnnotations": {},
"autocomplete": "email",
"readOnly": false,
"name": "email"
},
{
"validators": {
"length": {
"max": "255",
"ignore.empty.value": true
},
"person-name-prohibited-characters": {
"ignore.empty.value": true
},
"up-immutable-attribute": {},
"up-attribute-required-by-metadata-value": {}
},
"displayName": "${firstName}",
"annotations": {},
"required": true,
"groupAnnotations": {},
"readOnly": false,
"name": "firstName"
},
{
"validators": {
"length": {
"max": "255",
"ignore.empty.value": true
},
"person-name-prohibited-characters": {
"ignore.empty.value": true
},
"up-immutable-attribute": {},
"up-attribute-required-by-metadata-value": {}
},
"displayName": "${lastName}",
"annotations": {},
"required": true,
"groupAnnotations": {},
"readOnly": false,
"name": "lastName"
}
];
const attributesByName = Object.fromEntries(attributes.map(attribute => [attribute.name, attribute])) as any;
export const kcContextCommonMock: KcContextBase.Common = {
"url": {
"loginAction": "#",
"resourcesPath": pathJoin(PUBLIC_URL, mockTestingResourcesPath),
"resourcesCommonPath": pathJoin(PUBLIC_URL, mockTestingResourcesCommonPath),
"loginRestartFlowUrl": "/auth/realms/myrealm/login-actions/restart?client_id=account&tab_id=HoAx28ja4xg",
"loginUrl": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg"
},
"realm": {
"name": "myrealm",
"displayName": "myrealm",
"displayNameHtml": "myrealm",
"internationalizationEnabled": true,
"registrationEmailAsUsername": false
},
"messagesPerField": {
"printIfExists": () => {
console.log("coucou");
return undefined;
},
"existsError": () => false,
"get": key => `Fake error for ${key}`,
"exists": () => false
},
"locale": {
"supported": [
/* spell-checker: disable */
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=de",
"label": "Deutsch",
"languageTag": "de"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=no",
"label": "Norsk",
"languageTag": "no"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ru",
"label": "Русский",
"languageTag": "ru"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sv",
"label": "Svenska",
"languageTag": "sv"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pt-BR",
"label": "Português (Brasil)",
"languageTag": "pt-BR"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=lt",
"label": "Lietuvių",
"languageTag": "lt"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=en",
"label": "English",
"languageTag": "en"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=it",
"label": "Italiano",
"languageTag": "it"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=fr",
"label": "Français",
"languageTag": "fr"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=zh-CN",
"label": "中文简体",
"languageTag": "zh-CN"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=es",
"label": "Español",
"languageTag": "es"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=cs",
"label": "Čeština",
"languageTag": "cs"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ja",
"label": "日本語",
"languageTag": "ja"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sk",
"label": "Slovenčina",
"languageTag": "sk"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pl",
"label": "Polski",
"languageTag": "pl"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ca",
"label": "Català",
"languageTag": "ca"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=nl",
"label": "Nederlands",
"languageTag": "nl"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=tr",
"label": "Türkçe",
"languageTag": "tr"
}
/* spell-checker: enable */
],
"currentLanguageTag": "en"
},
"auth": {
"showUsername": false,
"showResetCredentials": false,
"showTryAnotherWayLink": false
},
"client": {
"clientId": "myApp"
},
"scripts": [],
"message": {
"type": "success",
"summary": "This is a test message"
},
"isAppInitiatedAction": false
};
const loginUrl = {
...kcContextCommonMock.url,
"loginResetCredentialsUrl": "/auth/realms/myrealm/login-actions/reset-credentials?client_id=account&tab_id=HoAx28ja4xg",
"registrationUrl": "/auth/realms/myrealm/login-actions/registration?client_id=account&tab_id=HoAx28ja4xg"
};
export const kcContextMocks: KcContextBase[] = [
id<KcContextBase.Login>({
...kcContextCommonMock,
"pageId": "login.ftl",
"url": loginUrl,
"realm": {
...kcContextCommonMock.realm,
"loginWithEmailAllowed": true,
"rememberMe": true,
"password": true,
"resetPasswordAllowed": true,
"registrationAllowed": true
},
"auth": kcContextCommonMock.auth!,
"social": {
"displayInfo": true
},
"usernameEditDisabled": false,
"login": {
"rememberMe": false
},
"registrationDisabled": false
}),
...(() => {
const registerCommon: KcContextBase.RegisterCommon = {
...kcContextCommonMock,
"url": {
...loginUrl,
"registrationAction":
"http://localhost:8080/auth/realms/myrealm/login-actions/registration?session_code=gwZdUeO7pbYpFTRxiIxRg_QtzMbtFTKrNu6XW_f8asM&execution=12146ce0-b139-4bbd-b25b-0eccfee6577e&client_id=account&tab_id=uS8lYfebLa0"
},
"scripts": [],
"isAppInitiatedAction": false,
"passwordRequired": true,
"recaptchaRequired": false,
"social": {
"displayInfo": true
}
};
return [
id<KcContextBase.Register>({
"pageId": "register.ftl",
...registerCommon,
"register": {
"formData": {}
}
}),
id<KcContextBase.RegisterUserProfile>({
"pageId": "register-user-profile.ftl",
...registerCommon,
"profile": {
"context": "REGISTRATION_PROFILE" as const,
attributes,
attributesByName
}
})
];
})(),
id<KcContextBase.Info>({
...kcContextCommonMock,
"pageId": "info.ftl",
"messageHeader": "<Message header>",
"requiredActions": undefined,
"skipLink": false,
"actionUri": "#",
"client": {
"clientId": "myApp",
"baseUrl": "#"
}
}),
id<KcContextBase.Error>({
...kcContextCommonMock,
"pageId": "error.ftl",
"client": {
"clientId": "myApp",
"baseUrl": "#"
},
"message": {
"type": "error",
"summary": "This is the error message"
}
}),
id<KcContextBase.LoginResetPassword>({
...kcContextCommonMock,
"pageId": "login-reset-password.ftl",
"realm": {
...kcContextCommonMock.realm,
"loginWithEmailAllowed": false
}
}),
id<KcContextBase.LoginVerifyEmail>({
...kcContextCommonMock,
"pageId": "login-verify-email.ftl",
"user": {
"email": "john.doe@gmail.com"
}
}),
id<KcContextBase.Terms>({
...kcContextCommonMock,
"pageId": "terms.ftl"
}),
id<KcContextBase.LoginOtp>({
...kcContextCommonMock,
"pageId": "login-otp.ftl",
"otpLogin": {
"userOtpCredentials": [
{
"id": "id1",
"userLabel": "label1"
},
{
"id": "id2",
"userLabel": "label2"
}
]
}
}),
id<KcContextBase.LoginUsername>({
...kcContextCommonMock,
"pageId": "login-username.ftl",
"url": loginUrl,
"realm": {
...kcContextCommonMock.realm,
"loginWithEmailAllowed": true,
"rememberMe": true,
"password": true,
"resetPasswordAllowed": true,
"registrationAllowed": true
},
"social": {
"displayInfo": true
},
"usernameHidden": false,
"login": {
"rememberMe": false
},
"registrationDisabled": false
}),
id<KcContextBase.LoginPassword>({
...kcContextCommonMock,
"pageId": "login-password.ftl",
"url": loginUrl,
"realm": {
...kcContextCommonMock.realm,
"resetPasswordAllowed": true
},
"social": {
"displayInfo": false
},
"login": {}
}),
id<KcContextBase.WebauthnAuthenticate>({
...kcContextCommonMock,
"pageId": "webauthn-authenticate.ftl",
"url": loginUrl,
"authenticators": {
"authenticators": []
},
"realm": {
...kcContextCommonMock.realm
},
"challenge": "",
"userVerification": "not specified",
"rpId": "",
"createTimeout": "0",
"isUserIdentified": "false",
"shouldDisplayAuthenticators": false,
"social": {
"displayInfo": false
},
"login": {}
}),
id<KcContextBase.LoginUpdatePassword>({
...kcContextCommonMock,
"pageId": "login-update-password.ftl",
"username": "anUsername"
}),
id<KcContextBase.LoginUpdateProfile>({
...kcContextCommonMock,
"pageId": "login-update-profile.ftl",
"user": {
"editUsernameAllowed": true,
"username": "anUsername",
"email": "foo@example.com",
"firstName": "aFirstName",
"lastName": "aLastName"
}
}),
id<KcContextBase.LoginIdpLinkConfirm>({
...kcContextCommonMock,
"pageId": "login-idp-link-confirm.ftl",
"idpAlias": "FranceConnect"
}),
id<KcContextBase.LoginIdpLinkEmail>({
...kcContextCommonMock,
"pageId": "login-idp-link-email.ftl",
"idpAlias": "FranceConnect",
"brokerContext": {
"username": "anUsername"
}
}),
id<KcContextBase.LoginConfigTotp>({
...kcContextCommonMock,
"pageId": "login-config-totp.ftl",
totp: {
totpSecretEncoded: "KVVF G2BY N4YX S6LB IUYT K2LH IFYE 4SBV",
qrUrl: "#",
totpSecretQrCode:
"iVBORw0KGgoAAAANSUhEUgAAAPYAAAD2AQAAAADNaUdlAAACM0lEQVR4Xu3OIZJgOQwDUDFd2UxiurLAVnnbHw4YGDKtSiWOn4Gxf81//7r/+q8b4HfLGBZDK9d85NmNR+sB42sXvOYrN5P1DcgYYFTGfOlbzE8gzwy3euweGizw7cfdl34/GRhlkxjKNV+5AebPXPORX1JuB9x8ZfbyyD2y1krWAKsbMq1HnqQDaLfa77p4+MqvzEGSqvSAD/2IHW2yHaigR9tX3m8dDIYGcNf3f+gDpVBZbZU77zyJ6Rlcy+qoTMG887KAPD9hsh6a1Sv3gJUHGHUAxSMzj7zqDDe7Phmt2eG+8UsMxjRGm816MAO+8VMl1R1jGHOrZB/5Zo/WXAPgxixm9Mo96vDGrM1eOto8c4Ax4wF437mifOXlpiPzCnN7Y9l95NnEMxgMY9AAGA8fucH14Y1aVb6N/cqrmyh0BVht7k1e+bU8LK0Cg5vmVq9c5vHIjOfqxDIfeTraNVTwewa4wVe+SW5N+uP1qACeudUZbqGOfA6VZV750Noq2Xx3kpveV44ZelSV1V7KFHzkWyVrrlUwG0Pl9pWnoy3vsQoME6vKI69i5osVgwWzHT7zjmJtMcNUSVn1oYMd7ZodbgowZl45VG0uVuLPUr1yc79uaQBag/mqR34xhlWyHm1prplHboCWdZ4TeZjsK8+dI+jbz1C5hl65mcpgB5dhcj8+dGO+0Ko68+lD37JDD83dpDLzzK+TrQyaVwGj6pUboGV+7+AyN8An/pf84/7rv/4/1l4OCc/1BYMAAAAASUVORK5CYII=",
manualUrl: "#",
totpSecret: "G4nsI8lQagRMUchH8jEG",
otpCredentials: [],
policy: {
supportedApplications: ["FreeOTP", "Google Authenticator"],
algorithm: "HmacSHA1",
digits: 6,
lookAheadWindow: 1,
type: "totp",
period: 30
}
}
}),
id<KcContextBase.LogoutConfirm>({
...kcContextCommonMock,
"pageId": "logout-confirm.ftl",
"url": {
...kcContextCommonMock.url,
"logoutConfirmAction": "Continuer?"
},
"client": {
"clientId": "myApp",
"baseUrl": "#"
},
"logoutConfirm": { "code": "123", skipLink: false }
}),
id<KcContextBase.UpdateUserProfile>({
...kcContextCommonMock,
"pageId": "update-user-profile.ftl",
"profile": {
attributes,
attributesByName
}
}),
id<KcContextBase.IdpReviewUserProfile>({
...kcContextCommonMock,
"pageId": "idp-review-user-profile.ftl",
"profile": {
context: "IDP_REVIEW",
attributes,
attributesByName
}
})
];

View File

@ -1 +0,0 @@
export * from "./kcContextMocks";

View File

@ -1,370 +0,0 @@
import "minimal-polyfills/Object.fromEntries";
import type { KcContextBase, Attribute } from "../KcContextBase";
import { getEvtKcLanguage } from "../../i18n/useKcLanguageTag";
import { getKcLanguageTagLabel } from "../../i18n/KcLanguageTag";
//NOTE: Aside because we want to be able to import them from node
import { resourcesCommonPath, resourcesPath } from "./urlResourcesPath";
import { id } from "tsafe/id";
import { pathJoin } from "../../tools/pathJoin";
const PUBLIC_URL = process.env["PUBLIC_URL"] ?? "/";
export const kcContextCommonMock: KcContextBase.Common = {
"url": {
"loginAction": "#",
"resourcesPath": pathJoin(PUBLIC_URL, resourcesPath),
"resourcesCommonPath": pathJoin(PUBLIC_URL, resourcesCommonPath),
"loginRestartFlowUrl": "/auth/realms/myrealm/login-actions/restart?client_id=account&tab_id=HoAx28ja4xg",
"loginUrl": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg",
},
"realm": {
"name": "myrealm",
"displayName": "myrealm",
"displayNameHtml": "myrealm",
"internationalizationEnabled": true,
"registrationEmailAsUsername": false,
},
"messagesPerField": {
"printIfExists": (...[, x]) => x,
"existsError": () => true,
"get": key => `Fake error for ${key}`,
"exists": () => true,
},
"locale": {
"supported": [
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=de",
"languageTag": "de",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=no",
"languageTag": "no",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ru",
"languageTag": "ru",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sv",
"languageTag": "sv",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pt-BR",
"languageTag": "pt-BR",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=lt",
"languageTag": "lt",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=en",
"languageTag": "en",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=it",
"languageTag": "it",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=fr",
"languageTag": "fr",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=zh-CN",
"languageTag": "zh-CN",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=es",
"languageTag": "es",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=cs",
"languageTag": "cs",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ja",
"languageTag": "ja",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sk",
"languageTag": "sk",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pl",
"languageTag": "pl",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ca",
"languageTag": "ca",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=nl",
"languageTag": "nl",
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=tr",
"languageTag": "tr",
},
],
//"current": null as any
"current": "English",
},
"auth": {
"showUsername": false,
"showResetCredentials": false,
"showTryAnotherWayLink": false,
},
"client": {
"clientId": "myApp",
},
"scripts": [],
"message": {
"type": "success",
"summary": "This is a test message",
},
"isAppInitiatedAction": false,
};
Object.defineProperty(kcContextCommonMock.locale!, "current", {
"get": () => getKcLanguageTagLabel(getEvtKcLanguage().state),
"enumerable": true,
});
const loginUrl = {
...kcContextCommonMock.url,
"loginResetCredentialsUrl": "/auth/realms/myrealm/login-actions/reset-credentials?client_id=account&tab_id=HoAx28ja4xg",
"registrationUrl": "/auth/realms/myrealm/login-actions/registration?client_id=account&tab_id=HoAx28ja4xg",
};
export const kcContextMocks: KcContextBase[] = [
id<KcContextBase.Login>({
...kcContextCommonMock,
"pageId": "login.ftl",
"url": loginUrl,
"realm": {
...kcContextCommonMock.realm,
"loginWithEmailAllowed": true,
"rememberMe": true,
"password": true,
"resetPasswordAllowed": true,
"registrationAllowed": true,
},
"auth": kcContextCommonMock.auth!,
"social": {
"displayInfo": true,
},
"usernameEditDisabled": false,
"login": {
"rememberMe": false,
},
"registrationDisabled": false,
}),
...(() => {
const registerCommon: KcContextBase.RegisterCommon = {
...kcContextCommonMock,
"url": {
...loginUrl,
"registrationAction":
"http://localhost:8080/auth/realms/myrealm/login-actions/registration?session_code=gwZdUeO7pbYpFTRxiIxRg_QtzMbtFTKrNu6XW_f8asM&execution=12146ce0-b139-4bbd-b25b-0eccfee6577e&client_id=account&tab_id=uS8lYfebLa0",
},
"scripts": [],
"isAppInitiatedAction": false,
"passwordRequired": true,
"recaptchaRequired": false,
"social": {
"displayInfo": true,
},
};
return [
id<KcContextBase.Register>({
"pageId": "register.ftl",
...registerCommon,
"register": {
"formData": {},
},
}),
id<KcContextBase.RegisterUserProfile>({
"pageId": "register-user-profile.ftl",
...registerCommon,
"profile": {
"context": "REGISTRATION_PROFILE" as const,
...(() => {
const attributes: Attribute[] = [
{
"validators": {
"username-prohibited-characters": {
"ignore.empty.value": true,
},
"up-username-has-value": {},
"length": {
"ignore.empty.value": true,
"min": "3",
"max": "255",
},
"up-duplicate-username": {},
"up-username-mutation": {},
},
"displayName": "${username}",
"annotations": {},
"required": true,
"groupAnnotations": {},
"autocomplete": "username",
"readOnly": false,
"name": "username",
"value": "xxxx",
},
{
"validators": {
"up-email-exists-as-username": {},
"length": {
"max": "255",
"ignore.empty.value": true,
},
"up-blank-attribute-value": {
"error-message": "missingEmailMessage",
"fail-on-null": false,
},
"up-duplicate-email": {},
"email": {
"ignore.empty.value": true,
},
"pattern": {
"ignore.empty.value": true,
"pattern": "gmail\\.com$",
},
},
"displayName": "${email}",
"annotations": {},
"required": true,
"groupAnnotations": {},
"autocomplete": "email",
"readOnly": false,
"name": "email",
},
{
"validators": {
"length": {
"max": "255",
"ignore.empty.value": true,
},
"person-name-prohibited-characters": {
"ignore.empty.value": true,
},
"up-immutable-attribute": {},
"up-attribute-required-by-metadata-value": {},
},
"displayName": "${firstName}",
"annotations": {},
"required": true,
"groupAnnotations": {},
"readOnly": false,
"name": "firstName",
},
{
"validators": {
"length": {
"max": "255",
"ignore.empty.value": true,
},
"person-name-prohibited-characters": {
"ignore.empty.value": true,
},
"up-immutable-attribute": {},
"up-attribute-required-by-metadata-value": {},
},
"displayName": "${lastName}",
"annotations": {},
"required": true,
"groupAnnotations": {},
"readOnly": false,
"name": "lastName",
},
];
return {
attributes,
"attributesByName": Object.fromEntries(attributes.map(attribute => [attribute.name, attribute])) as any,
} as any;
})(),
},
}),
];
})(),
id<KcContextBase.Info>({
...kcContextCommonMock,
"pageId": "info.ftl",
"messageHeader": "<Message header>",
"requiredActions": undefined,
"skipLink": false,
"actionUri": "#",
"client": {
"clientId": "myApp",
"baseUrl": "#",
},
}),
id<KcContextBase.Error>({
...kcContextCommonMock,
"pageId": "error.ftl",
"client": {
"clientId": "myApp",
"baseUrl": "#",
},
"message": {
"type": "error",
"summary": "This is the error message",
},
}),
id<KcContextBase.LoginResetPassword>({
...kcContextCommonMock,
"pageId": "login-reset-password.ftl",
"realm": {
...kcContextCommonMock.realm,
"loginWithEmailAllowed": false,
},
}),
id<KcContextBase.LoginVerifyEmail>({
...kcContextCommonMock,
"pageId": "login-verify-email.ftl",
}),
id<KcContextBase.Terms>({
...kcContextCommonMock,
"pageId": "terms.ftl",
}),
id<KcContextBase.LoginOtp>({
...kcContextCommonMock,
"pageId": "login-otp.ftl",
"otpLogin": {
"userOtpCredentials": [
{
"id": "id1",
"userLabel": "label1",
},
{
"id": "id2",
"userLabel": "label2",
},
],
},
}),
id<KcContextBase.LoginUpdatePassword>({
...kcContextCommonMock,
"pageId": "login-update-password.ftl",
"username": "anUsername",
}),
id<KcContextBase.LoginUpdateProfile>({
...kcContextCommonMock,
"pageId": "login-update-profile.ftl",
"user": {
"editUsernameAllowed": true,
"username": "anUsername",
"email": "foo@example.com",
"firstName": "aFirstName",
"lastName": "aLastName",
},
}),
id<KcContextBase.LoginIdpLinkConfirm>({
...kcContextCommonMock,
"pageId": "login-idp-link-confirm.ftl",
"idpAlias": "FranceConnect",
}),
];

View File

@ -1,5 +0,0 @@
import { pathJoin } from "../../tools/pathJoin";
export const subDirOfPublicDirBasename = "keycloak_static";
export const resourcesPath = pathJoin(subDirOfPublicDirBasename, "resources");
export const resourcesCommonPath = pathJoin(resourcesPath, "resources_common");

View File

@ -1,63 +0,0 @@
import { objectKeys } from "tsafe/objectKeys";
import { kcMessages } from "./kcMessages/login";
export type KcLanguageTag = keyof typeof kcMessages;
const kcLanguageByTagLabel = {
/* spell-checker: disable */
"es": "Español",
"it": "Italiano",
"fr": "Français",
"ca": "Català",
"en": "English",
"de": "Deutsch",
"no": "Norsk",
"pt-BR": "Português (Brasil)",
"ru": "Русский",
"sk": "Slovenčina",
"ja": "日本語",
"pl": "Polski",
"zh-CN": "中文简体",
"sv": "Svenska",
"lt": "Lietuvių",
"cs": "Čeština",
"nl": "Nederlands",
"tr": "Türkçe",
"da": "Dansk",
"hu": "Magyar",
/* spell-checker: enable */
} as const;
export type LanguageLabel = typeof kcLanguageByTagLabel[keyof typeof kcLanguageByTagLabel];
export function getKcLanguageTagLabel(language: KcLanguageTag): LanguageLabel {
return kcLanguageByTagLabel[language] ?? language;
}
export const kcLanguageTags = objectKeys(kcMessages);
/**
* Pass in "fr-FR" or "français" for example, it will return the AvailableLanguage
* it corresponds to: "fr".
* If there is no reasonable match it's guessed from navigator.language.
* If still no matches "en" is returned.
*/
export function getBestMatchAmongKcLanguageTag(languageLike: string): KcLanguageTag {
const iso2LanguageLike = languageLike.split("-")[0].toLowerCase();
const kcLanguageTag = kcLanguageTags.find(
language =>
language.toLowerCase().includes(iso2LanguageLike) ||
getKcLanguageTagLabel(language).toLocaleLowerCase() === languageLike.toLocaleLowerCase(),
);
if (kcLanguageTag !== undefined) {
return kcLanguageTag;
}
if (languageLike !== navigator.language) {
return getBestMatchAmongKcLanguageTag(navigator.language);
}
return "en";
}

File diff suppressed because it is too large Load Diff

View File

@ -1,253 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
export const kcMessages = {
"ca": {
"invalidPasswordHistoryMessage": "Contrasenya incorrecta: no pot ser igual a cap de les últimes {0} contrasenyes.",
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
"invalidPasswordMinLengthMessage": "Contrasenya incorrecta: longitud mínima {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres minúscules.",
"invalidPasswordMinSpecialCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} caràcters especials.",
"invalidPasswordMinUpperCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres majúscules.",
"invalidPasswordNotUsernameMessage": "Contrasenya incorrecta: no pot ser igual al nom d'usuari.",
"invalidPasswordRegexPatternMessage": "Contrasenya incorrecta: no compleix l'expressió regular.",
},
"de": {
"invalidPasswordMinLengthMessage": "Ungültiges Passwort: muss mindestens {0} Zeichen beinhalten.",
"invalidPasswordMinLowerCaseCharsMessage": "Ungültiges Passwort: muss mindestens {0} Kleinbuchstaben beinhalten.",
"invalidPasswordMinDigitsMessage": "Ungültiges Passwort: muss mindestens {0} Ziffern beinhalten.",
"invalidPasswordMinUpperCaseCharsMessage": "Ungültiges Passwort: muss mindestens {0} Großbuchstaben beinhalten.",
"invalidPasswordMinSpecialCharsMessage": "Ungültiges Passwort: muss mindestens {0} Sonderzeichen beinhalten.",
"invalidPasswordNotUsernameMessage": "Ungültiges Passwort: darf nicht identisch mit dem Benutzernamen sein.",
"invalidPasswordRegexPatternMessage": "Ungültiges Passwort: stimmt nicht mit Regex-Muster überein.",
"invalidPasswordHistoryMessage": "Ungültiges Passwort: darf nicht identisch mit einem der letzten {0} Passwörter sein.",
"invalidPasswordBlacklistedMessage": "Ungültiges Passwort: Passwort ist zu bekannt und auf der schwarzen Liste.",
"invalidPasswordGenericMessage": "Ungültiges Passwort: neues Passwort erfüllt die Passwort-Anforderungen nicht.",
},
"en": {
"invalidPasswordMinLengthMessage": "Invalid password: minimum length {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Invalid password: must contain at least {0} lower case characters.",
"invalidPasswordMinDigitsMessage": "Invalid password: must contain at least {0} numerical digits.",
"invalidPasswordMinUpperCaseCharsMessage": "Invalid password: must contain at least {0} upper case characters.",
"invalidPasswordMinSpecialCharsMessage": "Invalid password: must contain at least {0} special characters.",
"invalidPasswordNotUsernameMessage": "Invalid password: must not be equal to the username.",
"invalidPasswordRegexPatternMessage": "Invalid password: fails to match regex pattern(s).",
"invalidPasswordHistoryMessage": "Invalid password: must not be equal to any of last {0} passwords.",
"invalidPasswordBlacklistedMessage": "Invalid password: password is blacklisted.",
"invalidPasswordGenericMessage": "Invalid password: new password does not match password policies.",
"ldapErrorInvalidCustomFilter": 'Custom configured LDAP filter does not start with "(" or does not end with ")".',
"ldapErrorConnectionTimeoutNotNumber": "Connection Timeout must be a number",
"ldapErrorReadTimeoutNotNumber": "Read Timeout must be a number",
"ldapErrorMissingClientId": "Client ID needs to be provided in config when Realm Roles Mapping is not used.",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType":
"Not possible to preserve group inheritance and use UID membership type together.",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "Can not set write only when LDAP provider mode is not WRITABLE",
"ldapErrorCantWriteOnlyAndReadOnly": "Can not set write-only and read-only together",
"ldapErrorCantEnableStartTlsAndConnectionPooling": "Can not enable both StartTLS and connection pooling.",
"ldapErrorCantEnableUnsyncedAndImportOff": "Can not disable Importing users when LDAP provider mode is UNSYNCED",
"ldapErrorMissingGroupsPathGroup": "Groups path group does not exist - please create the group on specified path first",
"clientRedirectURIsFragmentError": "Redirect URIs must not contain an URI fragment",
"clientRootURLFragmentError": "Root URL must not contain an URL fragment",
"clientRootURLIllegalSchemeError": "Root URL uses an illegal scheme",
"clientBaseURLIllegalSchemeError": "Base URL uses an illegal scheme",
"clientRedirectURIsIllegalSchemeError": "A redirect URI uses an illegal scheme",
"clientBaseURLInvalid": "Base URL is not a valid URL",
"clientRootURLInvalid": "Root URL is not a valid URL",
"clientRedirectURIsInvalid": "A redirect URI is not a valid URI",
"pairwiseMalformedClientRedirectURI": "Client contained an invalid redirect URI.",
"pairwiseClientRedirectURIsMissingHost": "Client redirect URIs must contain a valid host component.",
"pairwiseClientRedirectURIsMultipleHosts":
"Without a configured Sector Identifier URI, client redirect URIs must not contain multiple host components.",
"pairwiseMalformedSectorIdentifierURI": "Malformed Sector Identifier URI.",
"pairwiseFailedToGetRedirectURIs": "Failed to get redirect URIs from the Sector Identifier URI.",
"pairwiseRedirectURIsMismatch": "Client redirect URIs does not match redirect URIs fetched from the Sector Identifier URI.",
},
"es": {
"invalidPasswordMinLengthMessage": "Contraseña incorrecta: longitud mínima {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras minúsculas.",
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
"invalidPasswordMinUpperCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras mayúsculas.",
"invalidPasswordMinSpecialCharsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres especiales.",
"invalidPasswordNotUsernameMessage": "Contraseña incorrecta: no puede ser igual al nombre de usuario.",
"invalidPasswordRegexPatternMessage": "Contraseña incorrecta: no cumple la expresión regular.",
"invalidPasswordHistoryMessage": "Contraseña incorrecta: no puede ser igual a ninguna de las últimas {0} contraseñas.",
},
"fr": {
"invalidPasswordMinLengthMessage": "Mot de passe invalide : longueur minimale requise de {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Mot de passe invalide : doit contenir au moins {0} lettre(s) en minuscule.",
"invalidPasswordMinDigitsMessage": "Mot de passe invalide : doit contenir au moins {0} chiffre(s).",
"invalidPasswordMinUpperCaseCharsMessage": "Mot de passe invalide : doit contenir au moins {0} lettre(s) en majuscule.",
"invalidPasswordMinSpecialCharsMessage": "Mot de passe invalide : doit contenir au moins {0} caractère(s) spéciaux.",
"invalidPasswordNotUsernameMessage": "Mot de passe invalide : ne doit pas être identique au nom d'utilisateur.",
"invalidPasswordRegexPatternMessage": "Mot de passe invalide : ne valide pas l'expression rationnelle.",
"invalidPasswordHistoryMessage": "Mot de passe invalide : ne doit pas être égal aux {0} derniers mot de passe.",
},
"it": {},
"ja": {
"invalidPasswordMinLengthMessage": "無効なパスワード: 最小{0}の長さが必要です。",
"invalidPasswordMinLowerCaseCharsMessage": "無効なパスワード: 少なくとも{0}文字の小文字を含む必要があります。",
"invalidPasswordMinDigitsMessage": "無効なパスワード: 少なくとも{0}文字の数字を含む必要があります。",
"invalidPasswordMinUpperCaseCharsMessage": "無効なパスワード: 少なくとも{0}文字の大文字を含む必要があります。",
"invalidPasswordMinSpecialCharsMessage": "無効なパスワード: 少なくとも{0}文字の特殊文字を含む必要があります。",
"invalidPasswordNotUsernameMessage": "無効なパスワード: ユーザー名と同じパスワードは禁止されています。",
"invalidPasswordRegexPatternMessage": "無効なパスワード: 正規表現パターンと一致しません。",
"invalidPasswordHistoryMessage": "無効なパスワード: 最近の{0}パスワードのいずれかと同じパスワードは禁止されています。",
"invalidPasswordBlacklistedMessage": "無効なパスワード: パスワードがブラックリストに含まれています。",
"invalidPasswordGenericMessage": "無効なパスワード: 新しいパスワードはパスワード・ポリシーと一致しません。",
"ldapErrorInvalidCustomFilter": "LDAPフィルターのカスタム設定が、「(」から開始または「)」で終了となっていません。",
"ldapErrorConnectionTimeoutNotNumber": "接続タイムアウトは数字でなければなりません",
"ldapErrorReadTimeoutNotNumber": "読み取りタイムアウトは数字でなければなりません",
"ldapErrorMissingClientId": "レルムロール・マッピングを使用しない場合は、クライアントIDは設定内で提供される必要があります。",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType":
"グループの継承を維持することと、UIDメンバーシップ・タイプを使用することは同時にできません。",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "LDAPプロバイダー・モードがWRITABLEではない場合は、write onlyを設定することはできません。",
"ldapErrorCantWriteOnlyAndReadOnly": "write-onlyとread-onlyを一緒に設定することはできません。",
"ldapErrorCantEnableStartTlsAndConnectionPooling": "StartTLSと接続プーリングの両方を有効にできません。",
"clientRedirectURIsFragmentError": "リダイレクトURIにURIフラグメントを含めることはできません。",
"clientRootURLFragmentError": "ルートURLにURLフラグメントを含めることはできません。",
"pairwiseMalformedClientRedirectURI": "クライアントに無効なリダイレクトURIが含まれていました。",
"pairwiseClientRedirectURIsMissingHost": "クライアントのリダイレクトURIには有効なホスト・コンポーネントが含まれている必要があります。",
"pairwiseClientRedirectURIsMultipleHosts":
"設定されたセレクター識別子URIがない場合は、クライアントのリダイレクトURIは複数のホスト・コンポーネントを含むことはできません。",
"pairwiseMalformedSectorIdentifierURI": "不正なセレクター識別子URIです。",
"pairwiseFailedToGetRedirectURIs": "セクター識別子URIからリダイレクトURIを取得できませんでした。",
"pairwiseRedirectURIsMismatch": "クライアントのリダイレクトURIは、セクター識別子URIからフェッチされたリダイレクトURIと一致しません。",
},
"lt": {
"invalidPasswordMinLengthMessage": "Per trumpas slaptažodis: mažiausias ilgis {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} mažąją raidę.",
"invalidPasswordMinDigitsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} skaitmenį.",
"invalidPasswordMinUpperCaseCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} didžiąją raidę.",
"invalidPasswordMinSpecialCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} specialų simbolį.",
"invalidPasswordNotUsernameMessage": "Neteisingas slaptažodis: slaptažodis negali sutapti su naudotojo vardu.",
"invalidPasswordRegexPatternMessage": "Neteisingas slaptažodis: slaptažodis netenkina regex taisyklės(ių).",
"invalidPasswordHistoryMessage": "Neteisingas slaptažodis: slaptažodis negali sutapti su prieš tai buvusiais {0} slaptažodžiais.",
"ldapErrorInvalidCustomFilter": 'Sukonfigūruotas LDAP filtras neprasideda "(" ir nesibaigia ")" simboliais.',
"ldapErrorMissingClientId": "Privaloma nurodyti kliento ID kai srities rolių susiejimas nėra nenaudojamas.",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType": "Grupių paveldėjimo ir UID narystės tipas kartu negali būti naudojami.",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "Negalima nustatyti rašymo rėžimo kuomet LDAP teikėjo rėžimas ne WRITABLE",
"ldapErrorCantWriteOnlyAndReadOnly": "Negalima nustatyti tik rašyti ir tik skaityti kartu",
"clientRedirectURIsFragmentError": "Nurodykite URI fragmentą, kurio negali būti peradresuojamuose URI adresuose",
"clientRootURLFragmentError": "Nurodykite URL fragmentą, kurio negali būti šakniniame URL adrese",
"pairwiseMalformedClientRedirectURI": "Klientas pateikė neteisingą nukreipimo nuorodą.",
"pairwiseClientRedirectURIsMissingHost": "Kliento nukreipimo nuorodos privalo būti nurodytos su serverio vardo komponentu.",
"pairwiseClientRedirectURIsMultipleHosts":
"Kuomet nesukonfigūruotas sektoriaus identifikatoriaus URL, kliento nukreipimo nuorodos privalo talpinti ne daugiau kaip vieną skirtingą serverio vardo komponentą.",
"pairwiseMalformedSectorIdentifierURI": "Neteisinga sektoriaus identifikatoriaus URI.",
"pairwiseFailedToGetRedirectURIs": "Nepavyko gauti nukreipimo nuorodų iš sektoriaus identifikatoriaus URI.",
"pairwiseRedirectURIsMismatch": "Kliento nukreipimo nuoroda neatitinka nukreipimo nuorodų iš sektoriaus identifikatoriaus URI.",
},
"nl": {
"invalidPasswordMinLengthMessage": "Ongeldig wachtwoord: de minimale lengte is {0} karakters.",
"invalidPasswordMinLowerCaseCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} kleine letters bevatten.",
"invalidPasswordMinDigitsMessage": "Ongeldig wachtwoord: het moet minstens {0} getallen bevatten.",
"invalidPasswordMinUpperCaseCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} hoofdletters bevatten.",
"invalidPasswordMinSpecialCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} speciale karakters bevatten.",
"invalidPasswordNotUsernameMessage": "Ongeldig wachtwoord: het mag niet overeenkomen met de gebruikersnaam.",
"invalidPasswordRegexPatternMessage": "Ongeldig wachtwoord: het voldoet niet aan het door de beheerder ingestelde patroon.",
"invalidPasswordHistoryMessage": "Ongeldig wachtwoord: het mag niet overeen komen met een van de laatste {0} wachtwoorden.",
"invalidPasswordGenericMessage": "Ongeldig wachtwoord: het nieuwe wachtwoord voldoet niet aan het wachtwoordbeleid.",
"ldapErrorInvalidCustomFilter": 'LDAP filter met aangepaste configuratie start niet met "(" of eindigt niet met ")".',
"ldapErrorConnectionTimeoutNotNumber": "Verbindingstimeout moet een getal zijn",
"ldapErrorReadTimeoutNotNumber": "Lees-timeout moet een getal zijn",
"ldapErrorMissingClientId": "Client ID moet ingesteld zijn als Realm Roles Mapping niet gebruikt wordt.",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType": "Kan groepsovererving niet behouden bij UID-lidmaatschapstype.",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "Alleen-schrijven niet mogelijk als LDAP provider mode niet WRITABLE is",
"ldapErrorCantWriteOnlyAndReadOnly": "Alleen-schrijven en alleen-lezen mogen niet tegelijk ingesteld zijn",
"clientRedirectURIsFragmentError": "Redirect URIs mogen geen URI fragment bevatten",
"clientRootURLFragmentError": "Root URL mag geen URL fragment bevatten",
"pairwiseMalformedClientRedirectURI": "Client heeft een ongeldige redirect URI.",
"pairwiseClientRedirectURIsMissingHost": "Client redirect URIs moeten een geldige host-component bevatten.",
"pairwiseClientRedirectURIsMultipleHosts":
"Zonder een geconfigureerde Sector Identifier URI mogen client redirect URIs niet meerdere host componenten hebben.",
"pairwiseMalformedSectorIdentifierURI": "Onjuist notatie in Sector Identifier URI.",
"pairwiseFailedToGetRedirectURIs": "Kon geen redirect URIs verkrijgen van de Sector Identifier URI.",
"pairwiseRedirectURIsMismatch": "Client redirect URIs komen niet overeen met redict URIs ontvangen van de Sector Identifier URI.",
},
"no": {
"invalidPasswordMinLengthMessage": "Ugyldig passord: minimum lengde {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Ugyldig passord: må inneholde minst {0} små bokstaver.",
"invalidPasswordMinDigitsMessage": "Ugyldig passord: må inneholde minst {0} sifre.",
"invalidPasswordMinUpperCaseCharsMessage": "Ugyldig passord: må inneholde minst {0} store bokstaver.",
"invalidPasswordMinSpecialCharsMessage": "Ugyldig passord: må inneholde minst {0} spesialtegn.",
"invalidPasswordNotUsernameMessage": "Ugyldig passord: kan ikke være likt brukernavn.",
"invalidPasswordRegexPatternMessage": "Ugyldig passord: tilfredsstiller ikke kravene for passord-mønster.",
"invalidPasswordHistoryMessage": "Ugyldig passord: kan ikke være likt noen av de {0} foregående passordene.",
"ldapErrorInvalidCustomFilter": 'Tilpasset konfigurasjon av LDAP-filter starter ikke med "(" eller slutter ikke med ")".',
"ldapErrorMissingClientId": "KlientID må være tilgjengelig i config når sikkerhetsdomenerollemapping ikke brukes.",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType": "Ikke mulig å bevare gruppearv og samtidig bruke UID medlemskapstype.",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "Kan ikke sette write-only når LDAP leverandør-modus ikke er WRITABLE",
"ldapErrorCantWriteOnlyAndReadOnly": "Kan ikke sette både write-only og read-only",
},
"pl": {},
"pt-BR": {
"invalidPasswordMinLengthMessage": "Senha inválida: deve conter ao menos {0} caracteres.",
"invalidPasswordMinLowerCaseCharsMessage": "Senha inválida: deve conter ao menos {0} caracteres minúsculos.",
"invalidPasswordMinDigitsMessage": "Senha inválida: deve conter ao menos {0} digitos numéricos.",
"invalidPasswordMinUpperCaseCharsMessage": "Senha inválida: deve conter ao menos {0} caracteres maiúsculos.",
"invalidPasswordMinSpecialCharsMessage": "Senha inválida: deve conter ao menos {0} caracteres especiais.",
"invalidPasswordNotUsernameMessage": "Senha inválida: não deve ser igual ao nome de usuário.",
"invalidPasswordRegexPatternMessage": "Senha inválida: falha ao passar por padrões.",
"invalidPasswordHistoryMessage": "Senha inválida: não deve ser igual às últimas {0} senhas.",
"ldapErrorInvalidCustomFilter": 'Filtro LDAP não inicia com "(" ou não termina com ")".',
"ldapErrorMissingClientId": "ID do cliente precisa ser definido na configuração quando mapeamentos de Roles do Realm não é utilizado.",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType":
"Não é possível preservar herança de grupos e usar tipo de associação de UID ao mesmo tempo.",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "Não é possível definir modo de somente escrita quando o provedor LDAP não suporta escrita",
"ldapErrorCantWriteOnlyAndReadOnly": "Não é possível definir somente escrita e somente leitura ao mesmo tempo",
"clientRedirectURIsFragmentError": "URIs de redirecionamento não podem conter fragmentos",
"clientRootURLFragmentError": "URL raiz não pode conter fragmentos",
},
"ru": {
"invalidPasswordMinLengthMessage": "Некорректный пароль: длина пароля должна быть не менее {0} символов(а).",
"invalidPasswordMinDigitsMessage": "Некорректный пароль: должен содержать не менее {0} цифр(ы).",
"invalidPasswordMinLowerCaseCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} символов(а) в нижнем регистре.",
"invalidPasswordMinUpperCaseCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} символов(а) в верхнем регистре.",
"invalidPasswordMinSpecialCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} спецсимволов(а).",
"invalidPasswordNotUsernameMessage": "Некорректный пароль: пароль не должен совпадать с именем пользователя.",
"invalidPasswordRegexPatternMessage": "Некорректный пароль: пароль не прошел проверку по регулярному выражению.",
"invalidPasswordHistoryMessage": "Некорректный пароль: пароль не должен совпадать с последним(и) {0} паролем(ями).",
"invalidPasswordGenericMessage": "Некорректный пароль: новый пароль не соответствует правилам пароля.",
"ldapErrorInvalidCustomFilter": 'Сконфигурированный пользователем фильтр LDAP не должен начинаться с "(" или заканчиваться на ")".',
"ldapErrorMissingClientId": "Client ID должен быть настроен в конфигурации, если не используется сопоставление ролей в realm.",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType": "Не удалось унаследовать группу и использовать членство UID типа вместе.",
"ldapErrorCantWriteOnlyForReadOnlyLdap": 'Невозможно установить режим "только на запись", когда LDAP провайдер не в режиме WRITABLE',
"ldapErrorCantWriteOnlyAndReadOnly": 'Невозможно одновременно установить режимы "только на чтение" и "только на запись"',
"clientRedirectURIsFragmentError": "URI перенаправления не должен содержать фрагмент URI",
"clientRootURLFragmentError": "Корневой URL не должен содержать фрагмент URL ",
"pairwiseMalformedClientRedirectURI": "Клиент содержит некорректный URI перенаправления.",
"pairwiseClientRedirectURIsMissingHost": "URI перенаправления клиента должен содержать корректный компонент хоста.",
"pairwiseClientRedirectURIsMultipleHosts":
"Без конфигурации по части идентификатора URI, URI перенаправления клиента не может содержать несколько компонентов хоста.",
"pairwiseMalformedSectorIdentifierURI": "Искаженная часть идентификатора URI.",
"pairwiseFailedToGetRedirectURIs": "Не удалось получить идентификаторы URI перенаправления из части идентификатора URI.",
"pairwiseRedirectURIsMismatch": "Клиент URI переадресации не соответствует URI переадресации, полученной из части идентификатора URI.",
},
"zh-CN": {
"invalidPasswordMinLengthMessage": "无效的密码:最短长度 {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "无效的密码:至少包含 {0} 小写字母",
"invalidPasswordMinDigitsMessage": "无效的密码:至少包含 {0} 个数字",
"invalidPasswordMinUpperCaseCharsMessage": "无效的密码:最短长度 {0} 大写字母",
"invalidPasswordMinSpecialCharsMessage": "无效的密码:最短长度 {0} 特殊字符",
"invalidPasswordNotUsernameMessage": "无效的密码: 不可以与用户名相同",
"invalidPasswordRegexPatternMessage": "无效的密码: 无法与正则表达式匹配",
"invalidPasswordHistoryMessage": "无效的密码:不能与最后使用的 {0} 个密码相同",
"ldapErrorInvalidCustomFilter": '定制的 LDAP过滤器不是以 "(" 开头或以 ")"结尾.',
"ldapErrorConnectionTimeoutNotNumber": "Connection Timeout 必须是个数字",
"ldapErrorMissingClientId": "当域角色映射未启用时,客户端 ID 需要指定。",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType": "无法在使用UID成员类型的同时维护组继承属性。",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "当LDAP提供方不是可写模式时无法设置只写",
"ldapErrorCantWriteOnlyAndReadOnly": "无法同时设置只读和只写",
"clientRedirectURIsFragmentError": "重定向URL不应包含URI片段",
"clientRootURLFragmentError": "根URL 不应包含 URL 片段",
"pairwiseMalformedClientRedirectURI": "客户端包含一个无效的重定向URL",
"pairwiseClientRedirectURIsMissingHost": "客户端重定向URL需要有一个有效的主机",
"pairwiseClientRedirectURIsMultipleHosts":
"Without a configured Sector Identifier URI, client redirect URIs must not contain multiple host components.",
"pairwiseMalformedSectorIdentifierURI": "Malformed Sector Identifier URI.",
"pairwiseFailedToGetRedirectURIs": "无法从服务器获得重定向URL",
"pairwiseRedirectURIsMismatch": "客户端的重定向URI与服务器端获取的URI配置不匹配。",
},
};
/* spell-checker: enable */

View File

@ -1,853 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
export const kcMessages = {
"ca": {
"emailVerificationSubject": "Verificació d'email",
"emailVerificationBody":
"Algú ha creat un compte de {2} amb aquesta adreça de correu electrònic. Si has estat tu, fes clic a l'enllaç següent per verificar la teva adreça de correu electrònic.\n\n{0}\n\nAquest enllaç expirarà en {1} minuts.\n\nSi tu no has creat aquest compte, simplement ignora aquest missatge.",
"emailVerificationBodyHtml":
'<p>Algú ha creat un compte de {2} amb aquesta adreça de correu electrònic. Si has estat tu, fes clic a l\'enllaç següent per verificar la teva adreça de correu electrònic.</p><p><a href="{0}">{0}</a></p><p> Aquest enllaç expirarà en {1} minuts.</p><p> Si tu no has creat aquest compte, simplement ignora aquest missatge.</p>',
"passwordResetSubject": "Reinicia contrasenya",
"passwordResetBody":
"Algú ha demanat de canviar les credencials del teu compte de {2}. Si has estat tu, fes clic a l'enllaç següent per a reiniciar-les.\n\n{0}\n\nAquest enllaç expirarà en {1} minuts.\n\nSi no vols reiniciar les teves credencials, simplement ignora aquest missatge i no es realitzarà cap canvi.",
"passwordResetBodyHtml":
'<p>Algú ha demanat de canviar les credencials del teu compte de {2}. Si has estat tu, fes clic a l\'enllaç següent per a reiniciar-les.</p><p><a href="{0}">{0}</a></p><p>Aquest enllaç expirarà en {1} minuts.</p><p>Si no vols reiniciar les teves credencials, simplement ignora aquest missatge i no es realitzarà cap canvi.</p>',
"executeActionsSubject": "Actualitza el teu compte",
"executeActionsBody":
"L'administrador ha sol·licitat que actualitzis el teu compte de {2}. Fes clic a l'enllaç inferior per iniciar aquest procés.\n\n{0}\n\nAquest enllaç expirarà en {1} minutes.\n\nSi no estàs al tant que l'administrador hagi sol·licitat això, simplement ignora aquest missatge i no es realitzarà cap canvi.",
"executeActionsBodyHtml":
"<p>L'administrador ha sol·licitat que actualitzis el teu compte de {2}. Fes clic a l'enllaç inferior per iniciar aquest procés.</p><p><a href=\"{0}\">{0}</a></p><p>Aquest enllaç expirarà en {1} minutes.</p><p>Si no estàs al tant que l'administrador hagi sol·licitat això, simplement ignora aquest missatge i no es realitzarà cap canvi.</p>",
"eventLoginErrorSubject": "Fallada en l'inici de sessió",
"eventLoginErrorBody":
"S'ha detectat un intent d'accés fallit al teu compte el {0} des de {1}. Si no has estat tu, si us plau contacta amb l'administrador.",
"eventLoginErrorBodyHtml":
"<p>S'ha detectat un intent d'accés fallit al teu compte el {0} des de {1}. Si no has estat tu, si us plau contacta amb l'administrador.</p>",
"eventRemoveTotpSubject": "Esborrat OTP",
"eventRemoveTotpBody": "OTP s'ha eliminat del teu compte el {0} des de {1}. Si no has estat tu, per favor contacta amb l'administrador.",
"eventRemoveTotpBodyHtml":
"<p>OTP s'ha eliminat del teu compte el {0} des de {1}. Si no has estat tu, si us plau contacta amb l'administrador. </ P>",
"eventUpdatePasswordSubject": "Actualització de contrasenya",
"eventUpdatePasswordBody":
"La teva contrasenya s'ha actualitzat el {0} des de {1}. Si no has estat tu, si us plau contacta amb l'administrador.",
"eventUpdatePasswordBodyHtml":
"<p>La teva contrasenya s'ha actualitzat el {0} des de {1}. Si no has estat tu, si us plau contacta amb l'administrador.</p>",
"eventUpdateTotpSubject": "Actualització de OTP",
"eventUpdateTotpBody": "OTP s'ha actualitzat al teu compte el {0} des de {1}. Si no has estat tu, si us plau contacta amb l'administrador.",
"eventUpdateTotpBodyHtml":
"<p>OTP s'ha actualitzat al teu compte el {0} des de {1}. Si no has estat tu, si us plau contacta amb l'administrador.</p>",
},
"cs": {
"emailVerificationSubject": "Ověření e-mailu",
"emailVerificationBody":
"Někdo vytvořil účet {2} s touto e-mailovou adresou. Pokud jste to vy, klikněte na níže uvedený odkaz a ověřte svou e-mailovou adresu \n\n{0}\n\nTento odkaz vyprší za {1} minuty.\n\nPokud jste tento účet nevytvořili, tuto zprávu ignorujte.",
"emailVerificationBodyHtml":
'<p>Někdo vytvořil účet {2} s touto e-mailovou adresou. Pokud jste to vy, klikněte na níže uvedený odkaz a ověřte svou e-mailovou adresu. </p><p><a href="{0}">Odkaz na ověření e-mailové adresy</a></p><p>Platnost odkazu vyprší za {1} minut.</p><p>Pokud jste tento účet nevytvořili, tuto zprávu ignorujte.</p>',
"emailTestSubject": "[KEYCLOAK] - testovací zpráva",
"emailTestBody": "Toto je testovací zpráva",
"emailTestBodyHtml": "<p>Toto je testovací zpráva </p>",
"identityProviderLinkSubject": "Odkaz {0}",
"identityProviderLinkBody":
'Někdo chce propojit váš účet "{1}" s účtem "{0}" uživatele {2}. Pokud jste to vy, klikněte na níže uvedený odkaz a propojte účty. \n\n{3}\n\nPlatnost tohoto odkazu je {5}.\n\nPokud nechcete propojit účet, tuto zprávu ignorujte. Pokud propojíte účty, budete se moci přihlásit jako {1} pomocí {0}.',
"identityProviderLinkBodyHtml":
'<p>Někdo právě požádal o změnu hesla u vašeho účtu {2}. Pokud jste to vy, pro jeho změnu klikněte na odkaz níže.</p><p><a href="{0}">Odkaz na změnu hesla.</a></p><p>Platnost tohoto odkazu je {3}.</p><p>Pokud heslo změnit nechcete, tuto zprávu ignorujte a nic se nezmění.</p>',
"passwordResetSubject": "Zapomenuté heslo",
"passwordResetBody":
"Někdo právě požádal o změnu hesla u vašeho účtu {2}. Pokud jste to vy, pro jeho změnu klikněte na odkaz níže.\n\n{0}\n\nPlatnost tohoto odkazu je {3}.\n\nPokud heslo změnit nechcete, tuto zprávu ignorujte a nic se nezmění.",
"passwordResetBodyHtml":
'<p> Někdo právě požádal o změnu pověření vašeho účtu {2}. Pokud jste to vy, klikněte na odkaz níže, abyste je resetovali.</p><p><a href="{0}">Odkaz na obnovení pověření </a></p><p> Platnost tohoto odkazu vyprší během {1} minut.</p><p> Pokud nechcete obnovit vaše pověření, ignorujte tuto zprávu a nic se nezmění.</p>',
"executeActionsSubject": "Aktualizujte svůj účet",
"executeActionsBody":
"Váš administrátor vás požádal o provedení následujících akcí u účtu {2}: {3}. Začněte kliknutím na níže uvedený odkaz.\n\n{0}\n\nPlatnost tohoto odkazu je {4}.\n\nPokud si nejste jisti, zda je tento požadavek v pořádku, ignorujte tuto zprávu.",
"executeActionsBodyHtml":
'<p>Váš administrátor vás požádal o provedení následujících akcí u účtu {2}: {3}. Začněte kliknutím na níže uvedený odkaz.</p><p><a href="{0}">Odkaz na aktualizaci účtu.</a></p><p>Platnost tohoto odkazu je {4}.</p><p>Pokud si nejste jisti, zda je tento požadavek v pořádku, ignorujte tuto zprávu.</p>',
"eventLoginErrorSubject": "Chyba přihlášení",
"eventLoginErrorBody": "Někdo se neúspěšně pokusil přihlásit k účtu {0} z {1}. Pokud jste to nebyli vy, kontaktujte administrátora.",
"eventLoginErrorBodyHtml":
"<p>Někdo se neúspěšně pokusil přihlásit k účtu {0} z {1}. Pokud jste to nebyli vy, kontaktujte administrátora.</p>",
"eventRemoveTotpSubject": "Odebrat TOTP",
"eventRemoveTotpBody": "V účtu {0} bylo odebráno nastavení OTP z {1}. Pokud jste to nebyli vy, kontaktujte administrátora.",
"eventRemoveTotpBodyHtml": "<p>V účtu {0} bylo odebráno nastavení OTP z {1}. Pokud jste to nebyli vy, kontaktujte administrátora.</p>",
"eventUpdatePasswordSubject": "Aktualizace hesla",
"eventUpdatePasswordBody": "V účtu {0} bylo změněno heslo z {1}. Pokud jste to nebyli vy, kontaktujte administrátora.",
"eventUpdatePasswordBodyHtml": "<p>V účtu {0} bylo změněno heslo z {1}. Pokud jste to nebyli vy, kontaktujte administrátora.</p>",
"eventUpdateTotpSubject": "Aktualizace OTP",
"eventUpdateTotpBody": "V účtu {0} bylo změněno nastavení OTP z {1}. Pokud jste to nebyli vy, kontaktujte administrátora.",
"eventUpdateTotpBodyHtml": "<p>V účtu {0} bylo změněno nastavení OTP z {1}. Pokud jste to nebyli vy, kontaktujte administrátora.</p>",
"requiredAction.CONFIGURE_TOTP": "Konfigurace OTP",
"requiredAction.terms_and_conditions": "Smluvní podmínky",
"requiredAction.UPDATE_PASSWORD": "Aktualizace hesla",
"requiredAction.UPDATE_PROFILE": "Aktualizace profilu",
"requiredAction.VERIFY_EMAIL": "Ověření e-mailu",
"linkExpirationFormatter.timePeriodUnit.seconds": "sekund",
"linkExpirationFormatter.timePeriodUnit.seconds.1": "sekunda",
"linkExpirationFormatter.timePeriodUnit.seconds.2": "sekundy",
"linkExpirationFormatter.timePeriodUnit.seconds.3": "sekundy",
"linkExpirationFormatter.timePeriodUnit.seconds.4": "sekundy",
"linkExpirationFormatter.timePeriodUnit.minutes": "minut",
"linkExpirationFormatter.timePeriodUnit.minutes.1": "minuta",
"linkExpirationFormatter.timePeriodUnit.minutes.2": "minuty",
"linkExpirationFormatter.timePeriodUnit.minutes.3": "minuty",
"linkExpirationFormatter.timePeriodUnit.minutes.4": "minuty",
"linkExpirationFormatter.timePeriodUnit.hours": "hodin",
"linkExpirationFormatter.timePeriodUnit.hours.1": "hodina",
"linkExpirationFormatter.timePeriodUnit.hours.2": "hodiny",
"linkExpirationFormatter.timePeriodUnit.hours.3": "hodiny",
"linkExpirationFormatter.timePeriodUnit.hours.4": "hodiny",
"linkExpirationFormatter.timePeriodUnit.days": "dní",
"linkExpirationFormatter.timePeriodUnit.days.1": "den",
"linkExpirationFormatter.timePeriodUnit.days.2": "dny",
"linkExpirationFormatter.timePeriodUnit.days.3": "dny",
"linkExpirationFormatter.timePeriodUnit.days.4": "dny",
},
"de": {
"emailVerificationSubject": "E-Mail verifizieren",
"emailVerificationBody":
"Jemand hat ein {2} Konto mit dieser E-Mail-Adresse erstellt. Falls Sie das waren, dann klicken Sie auf den Link, um die E-Mail-Adresse zu verifizieren.\n\n{0}\n\nDieser Link wird in {1} Minuten ablaufen.\n\nFalls Sie dieses Konto nicht erstellt haben, dann können sie diese Nachricht ignorieren.",
"emailVerificationBodyHtml":
'<p>Jemand hat ein {2} Konto mit dieser E-Mail-Adresse erstellt. Falls das Sie waren, klicken Sie auf den Link, um die E-Mail-Adresse zu verifizieren.</p><p><a href="{0}">Link zur Bestätigung der E-Mail-Adresse</a></p><p>Dieser Link wird in {1} Minuten ablaufen.</p><p>Falls Sie dieses Konto nicht erstellt haben, dann können sie diese Nachricht ignorieren.</p>',
"identityProviderLinkSubject": "Link {0}",
"identityProviderLinkBody":
"Es wurde beantragt Ihren Account {1} mit dem Account {0} von Benutzer {2} zu verlinken. Sollten Sie dies beantragt haben, klicken Sie auf den unten stehenden Link.\n\n{3}\n\n Die Gültigkeit des Links wird in {4} Minuten verfallen.\n\nSollten Sie Ihren Account nicht verlinken wollen, ignorieren Sie diese Nachricht. Wenn Sie die Accounts verlinken wird ein Login auf {1} über {0} ermöglicht.",
"identityProviderLinkBodyHtml":
'<p>Es wurde beantragt Ihren Account {1} mit dem Account {0} von Benutzer {2} zu verlinken. Sollten Sie dies beantragt haben, klicken Sie auf den unten stehenden Link.</p><p><a href="{3}">Link zur Bestätigung der Kontoverknüpfung</a></p><p>Die Gültigkeit des Links wird in {4} Minuten verfallen.</p><p>Sollten Sie Ihren Account nicht verlinken wollen, ignorieren Sie diese Nachricht. Wenn Sie die Accounts verlinken wird ein Login auf {1} über {0} ermöglicht.</p>',
"passwordResetSubject": "Passwort zurücksetzen",
"passwordResetBody":
"Es wurde eine Änderung der Anmeldeinformationen für Ihren Account {2} angefordert. Wenn Sie diese Änderung beantragt haben, klicken Sie auf den unten stehenden Link.\n\n{0}\n\nDie Gültigkeit des Links wird in {1} Minuten verfallen.\n\nSollten Sie keine Änderung vollziehen wollen können Sie diese Nachricht ignorieren und an Ihrem Account wird nichts geändert.",
"passwordResetBodyHtml":
'<p>Es wurde eine Änderung der Anmeldeinformationen für Ihren Account {2} angefordert. Wenn Sie diese Änderung beantragt haben, klicken Sie auf den unten stehenden Link.</p><p><a href="{0}">Link zum Zurücksetzen von Anmeldeinformationen</a></p><p>Die Gültigkeit des Links wird in {1} Minuten verfallen.</p><p>Sollten Sie keine Änderung vollziehen wollen können Sie diese Nachricht ignorieren und an Ihrem Account wird nichts geändert.</p>',
"executeActionsSubject": "Aktualisieren Sie Ihr Konto",
"executeActionsBody":
"Ihr Administrator hat Sie aufgefordert Ihren Account {2} zu aktualisieren. Klicken Sie auf den unten stehenden Link um den Prozess zu starten.\n\n{0}\n\nDie Gültigkeit des Links wird in {1} Minuten verfallen.\n\nSollten Sie sich dieser Aufforderung nicht bewusst sein, ignorieren Sie diese Nachricht und Ihr Account bleibt unverändert.",
"executeActionsBodyHtml":
'<p>Ihr Administrator hat Sie aufgefordert Ihren Account {2} zu aktualisieren. Klicken Sie auf den unten stehenden Link um den Prozess zu starten.</p><p><a href="{0}">Link zum Account-Update</a></p><p>Die Gültigkeit des Links wird in {1} Minuten verfallen.</p><p>Sollten Sie sich dieser Aufforderung nicht bewusst sein, ignorieren Sie diese Nachricht und Ihr Account bleibt unverändert.</p>',
"eventLoginErrorSubject": "Fehlgeschlagene Anmeldung",
"eventLoginErrorBody":
"Jemand hat um {0} von {1} versucht, sich mit Ihrem Konto anzumelden. Falls das nicht Sie waren, dann kontaktieren Sie bitte Ihren Admin.",
"eventLoginErrorBodyHtml":
"<p>Jemand hat um {0} von {1} versucht, sich mit Ihrem Konto anzumelden. Falls das nicht Sie waren, dann kontaktieren Sie bitte Ihren Admin.</p>",
"eventRemoveTotpSubject": "OTP Entfernt",
"eventRemoveTotpBody":
"OTP wurde von Ihrem Konto am {0} von {1} entfernt. Falls das nicht Sie waren, dann kontaktieren Sie bitte Ihren Admin.",
"eventRemoveTotpBodyHtml":
"<p>OTP wurde von Ihrem Konto am {0} von {1} entfernt. Falls das nicht Sie waren, dann kontaktieren Sie bitte Ihren Admin.</p>",
"eventUpdatePasswordSubject": "Passwort Aktualisiert",
"eventUpdatePasswordBody": "Ihr Passwort wurde am {0} von {1} geändert. Falls das nicht Sie waren, dann kontaktieren Sie bitte Ihren Admin.",
"eventUpdatePasswordBodyHtml":
"<p>Ihr Passwort wurde am {0} von {1} geändert. Falls das nicht Sie waren, dann kontaktieren Sie bitte Ihren Admin.</p>",
"eventUpdateTotpSubject": "OTP Aktualisiert",
"eventUpdateTotpBody": "OTP wurde am {0} von {1} geändert. Falls das nicht Sie waren, dann kontaktieren Sie bitte Ihren Admin.",
"eventUpdateTotpBodyHtml": "<p>OTP wurde am {0} von {1} geändert. Falls das nicht Sie waren, dann kontaktieren Sie bitte Ihren Admin.</p>",
},
"en": {
"emailVerificationSubject": "Verify email",
"emailVerificationBody":
"Someone has created a {2} account with this email address. If this was you, click the link below to verify your email address\n\n{0}\n\nThis link will expire within {3}.\n\nIf you didn't create this account, just ignore this message.",
"emailVerificationBodyHtml":
'<p>Someone has created a {2} account with this email address. If this was you, click the link below to verify your email address</p><p><a href="{0}">Link to e-mail address verification</a></p><p>This link will expire within {3}.</p><p>If you didn\'t create this account, just ignore this message.</p>',
"emailTestSubject": "[KEYCLOAK] - SMTP test message",
"emailTestBody": "This is a test message",
"emailTestBodyHtml": "<p>This is a test message</p>",
"identityProviderLinkSubject": "Link {0}",
"identityProviderLinkBody":
'Someone wants to link your "{1}" account with "{0}" account of user {2} . If this was you, click the link below to link accounts\n\n{3}\n\nThis link will expire within {5}.\n\nIf you don\'t want to link account, just ignore this message. If you link accounts, you will be able to login to {1} through {0}.',
"identityProviderLinkBodyHtml":
'<p>Someone wants to link your <b>{1}</b> account with <b>{0}</b> account of user {2} . If this was you, click the link below to link accounts</p><p><a href="{3}">Link to confirm account linking</a></p><p>This link will expire within {5}.</p><p>If you don\'t want to link account, just ignore this message. If you link accounts, you will be able to login to {1} through {0}.</p>',
"passwordResetSubject": "Reset password",
"passwordResetBody":
"Someone just requested to change your {2} account's credentials. If this was you, click on the link below to reset them.\n\n{0}\n\nThis link and code will expire within {3}.\n\nIf you don't want to reset your credentials, just ignore this message and nothing will be changed.",
"passwordResetBodyHtml":
"<p>Someone just requested to change your {2} account's credentials. If this was you, click on the link below to reset them.</p><p><a href=\"{0}\">Link to reset credentials</a></p><p>This link will expire within {3}.</p><p>If you don't want to reset your credentials, just ignore this message and nothing will be changed.</p>",
"executeActionsSubject": "Update Your Account",
"executeActionsBody":
"Your administrator has just requested that you update your {2} account by performing the following action(s): {3}. Click on the link below to start this process.\n\n{0}\n\nThis link will expire within {4}.\n\nIf you are unaware that your administrator has requested this, just ignore this message and nothing will be changed.",
"executeActionsBodyHtml":
'<p>Your administrator has just requested that you update your {2} account by performing the following action(s): {3}. Click on the link below to start this process.</p><p><a href="{0}">Link to account update</a></p><p>This link will expire within {4}.</p><p>If you are unaware that your administrator has requested this, just ignore this message and nothing will be changed.</p>',
"eventLoginErrorSubject": "Login error",
"eventLoginErrorBody":
"A failed login attempt was detected to your account on {0} from {1}. If this was not you, please contact an administrator.",
"eventLoginErrorBodyHtml":
"<p>A failed login attempt was detected to your account on {0} from {1}. If this was not you, please contact an administrator.</p>",
"eventRemoveTotpSubject": "Remove OTP",
"eventRemoveTotpBody": "OTP was removed from your account on {0} from {1}. If this was not you, please contact an administrator.",
"eventRemoveTotpBodyHtml": "<p>OTP was removed from your account on {0} from {1}. If this was not you, please contact an administrator.</p>",
"eventUpdatePasswordSubject": "Update password",
"eventUpdatePasswordBody": "Your password was changed on {0} from {1}. If this was not you, please contact an administrator.",
"eventUpdatePasswordBodyHtml": "<p>Your password was changed on {0} from {1}. If this was not you, please contact an administrator.</p>",
"eventUpdateTotpSubject": "Update OTP",
"eventUpdateTotpBody": "OTP was updated for your account on {0} from {1}. If this was not you, please contact an administrator.",
"eventUpdateTotpBodyHtml": "<p>OTP was updated for your account on {0} from {1}. If this was not you, please contact an administrator.</p>",
"requiredAction.CONFIGURE_TOTP": "Configure OTP",
"requiredAction.terms_and_conditions": "Terms and Conditions",
"requiredAction.UPDATE_PASSWORD": "Update Password",
"requiredAction.UPDATE_PROFILE": "Update Profile",
"requiredAction.VERIFY_EMAIL": "Verify Email",
"linkExpirationFormatter.timePeriodUnit.seconds": "seconds",
"linkExpirationFormatter.timePeriodUnit.seconds.1": "second",
"linkExpirationFormatter.timePeriodUnit.minutes": "minutes",
"linkExpirationFormatter.timePeriodUnit.minutes.1": "minute",
"linkExpirationFormatter.timePeriodUnit.hours": "hours",
"linkExpirationFormatter.timePeriodUnit.hours.1": "hour",
"linkExpirationFormatter.timePeriodUnit.days": "days",
"linkExpirationFormatter.timePeriodUnit.days.1": "day",
"emailVerificationBodyCode": "Please verify your email address by entering in the following code.\n\n{0}\n\n.",
"emailVerificationBodyCodeHtml": "<p>Please verify your email address by entering in the following code.</p><p><b>{0}</b></p>",
},
"es": {
"emailVerificationSubject": "Verificación de email",
"emailVerificationBody":
"Alguien ha creado una cuenta de {2} con esta dirección de email. Si has sido tú, haz click en el enlace siguiente para verificar tu dirección de email.\n\n{0}\n\nEste enlace expirará en {1} minutos.\n\nSi tú no has creado esta cuenta, simplemente ignora este mensaje.",
"emailVerificationBodyHtml":
'<p>Alguien ha creado una cuenta de {2} con esta dirección de email. Si has sido tú, haz click en el enlace siguiente para verificar tu dirección de email.</p><p><a href="{0}">{0}</a></p><p>Este enlace expirará en {1} minutos.</p><p>Si tú no has creado esta cuenta, simplemente ignora este mensaje.</p>',
"passwordResetSubject": "Reiniciar contraseña",
"passwordResetBody":
"Alguien ha solicitado cambiar las credenciales de tu cuenta de {2}. Si has sido tú, haz clic en el enlace siguiente para reiniciarlas.\n\n{0}\n\nEste enlace expirará en {1} minutos.\n\nSi no quieres reiniciar tus credenciales, simplemente ignora este mensaje y no se realizará ningún cambio.",
"passwordResetBodyHtml":
'<p>Alguien ha solicitado cambiar las credenciales de tu cuenta de {2}. Si has sido tú, haz clic en el enlace siguiente para reiniciarlas.</p><p><a href="{0}">{0}</a></p><p>Este enlace expirará en {1} minutos.</p><p>Si no quieres reiniciar tus credenciales, simplemente ignora este mensaje y no se realizará ningún cambio.</p>',
"executeActionsSubject": "Actualiza tu cuenta",
"executeActionsBody":
"El administrador ha solicitado que actualices tu cuenta de {2}. Haz clic en el enlace inferior para iniciar este proceso.\n\n{0}\n\nEste enlace expirará en {1} minutos.\n\nSi no estás al tanto de que el administrador haya solicitado esto, simplemente ignora este mensaje y no se realizará ningún cambio.",
"executeActionsBodyHtml":
'<p>El administrador ha solicitado que actualices tu cuenta de {2}. Haz clic en el enlace inferior para iniciar este proceso.</p><p><a href="{0}">{0}</a></p><p>Este enlace expirará en {1} minutos.</p><p>Si no estás al tanto de que el administrador haya solicitado esto, simplemente ignora este mensaje y no se realizará ningún cambio.</p>',
"eventLoginErrorSubject": "Fallo en el inicio de sesión",
"eventLoginErrorBody":
"Se ha detectado un intento de acceso fallido a tu cuenta el {0} desde {1}. Si no has sido tú, por favor contacta con el administrador.",
"eventLoginErrorBodyHtml":
"<p>Se ha detectado un intento de acceso fallido a tu cuenta el {0} desde {1}. Si no has sido tú, por favor contacta con el administrador.</p>",
"eventRemoveTotpSubject": "Borrado OTP",
"eventRemoveTotpBody": "OTP fue eliminado de tu cuenta el {0} desde {1}. Si no has sido tú, por favor contacta con el administrador.",
"eventRemoveTotpBodyHtml":
"<p>OTP fue eliminado de tu cuenta el {0} desde {1}. Si no has sido tú, por favor contacta con el administrador.</p>",
"eventUpdatePasswordSubject": "Actualización de contraseña",
"eventUpdatePasswordBody": "Tu contraseña se ha actualizado el {0} desde {1}. Si no has sido tú, por favor contacta con el administrador.",
"eventUpdatePasswordBodyHtml":
"<p>Tu contraseña se ha actualizado el {0} desde {1}. Si no has sido tú, por favor contacta con el administrador.</p>",
"eventUpdateTotpSubject": "Actualización de OTP",
"eventUpdateTotpBody": "OTP se ha actualizado en tu cuenta el {0} desde {1}. Si no has sido tú, por favor contacta con el administrador.",
"eventUpdateTotpBodyHtml":
"<p>OTP se ha actualizado en tu cuenta el {0} desde {1}. Si no has sido tú, por favor contacta con el administrador.</p>",
},
"fr": {
"emailVerificationSubject": "Vérification du courriel",
"emailVerificationBody":
"Quelqu'un vient de créer un compte \"{2}\" avec votre courriel. Si vous êtes à l'origine de cette requête, veuillez cliquer sur le lien ci-dessous afin de vérifier votre adresse de courriel\n\n{0}\n\nCe lien expire dans {1} minute(s).\n\nSinon, veuillez ignorer ce message.",
"emailVerificationBodyHtml":
'<p>Quelqu\'un vient de créer un compte "{2}" avec votre courriel. Si vous êtes à l\'origine de cette requête, veuillez cliquer sur le lien ci-dessous afin de vérifier votre adresse de courriel</p><p><a href="{0}">{0}</a></p><p>Ce lien expire dans {1} minute(s).</p><p>Sinon, veuillez ignorer ce message.</p>',
"passwordResetSubject": "Réinitialiser le mot de passe",
"passwordResetBody":
"Quelqu'un vient de demander une réinitialisation de mot de passe pour votre compte {2}. Si vous êtes à l'origine de cette requête, veuillez cliquer sur le lien ci-dessous pour le mettre à jour.\n\n{0}\n\nCe lien expire dans {1} minute(s).\n\nSinon, veuillez ignorer ce message ; aucun changement ne sera effectué sur votre compte.",
"passwordResetBodyHtml":
"<p>Quelqu'un vient de demander une réinitialisation de mot de passe pour votre compte {2}. Si vous êtes à l'origine de cette requête, veuillez cliquer sur le lien ci-dessous pour le mettre à jour.</p><p><a href=\"{0}\">Lien pour réinitialiser votre mot de passe</a></p><p>Ce lien expire dans {1} minute(s).</p><p>Sinon, veuillez ignorer ce message ; aucun changement ne sera effectué sur votre compte.</p>",
"executeActionsSubject": "Mettre à jour votre compte",
"executeActionsBody":
"Votre administrateur vient de demander une mise à jour de votre compte {2}. Veuillez cliquer sur le lien ci-dessous afin de commencer le processus.\n\n{0}\n\nCe lien expire dans {1} minute(s).\n\nSi vous n'êtes pas à l'origine de cette requête, veuillez ignorer ce message ; aucun changement ne sera effectué sur votre compte.",
"executeActionsBodyHtml":
"<p>Votre administrateur vient de demander une mise à jour de votre compte {2}. Veuillez cliquer sur le lien ci-dessous afin de commencer le processus.</p><p><a href=\"{0}\">{0}</a></p><p>Ce lien expire dans {1} minute(s).</p><p>Si vous n'êtes pas à l'origine de cette requête, veuillez ignorer ce message ; aucun changement ne sera effectué sur votre compte.</p>",
"eventLoginErrorSubject": "Erreur de connexion",
"eventLoginErrorBody":
"Une tentative de connexion a été détectée sur votre compte {0} depuis {1}. Si vous n'êtes pas à l'origine de cette requête, veuillez contacter votre administrateur.",
"eventLoginErrorBodyHtml":
"<p>Une tentative de connexion a été détectée sur votre compte {0} depuis {1}. Si vous n'êtes pas à l'origine de cette requête, veuillez contacter votre administrateur.</p>",
"eventRemoveTotpSubject": "Suppression du OTP",
"eventRemoveTotpBody":
"Le OTP a été supprimé de votre compte {0} depuis {1}. Si vous n'étiez pas à l'origine de cette requête, veuillez contacter votre administrateur.",
"eventRemoveTotpBodyHtml":
"<p>Le OTP a été supprimé de votre compte {0} depuis {1}. Si vous n'étiez pas à l'origine de cette requête, veuillez contacter votre administrateur.</p>",
"eventUpdatePasswordSubject": "Mise à jour du mot de passe",
"eventUpdatePasswordBody":
"Votre mot de passe pour votre compte {0} a été modifié depuis {1}. Si vous n'étiez pas à l'origine de cette requête, veuillez contacter votre administrateur.",
"eventUpdatePasswordBodyHtml":
"<p>Votre mot de passe pour votre compte {0} a été modifié depuis {1}. Si vous n'étiez pas à l'origine de cette requête, veuillez contacter votre administrateur.</p>",
"eventUpdateTotpSubject": "Mise à jour du OTP",
"eventUpdateTotpBody":
"Le OTP a été mis à jour pour votre compte {0} depuis {1}. Si vous n'étiez pas à l'origine de cette requête, veuillez contacter votre administrateur.",
"eventUpdateTotpBodyHtml":
"<p>Le OTP a été mis à jour pour votre compte {0} depuis {1}. Si vous n'étiez pas à l'origine de cette requête, veuillez contacter votre administrateur.</p>",
},
"it": {
"emailVerificationSubject": "Verifica l'email",
"emailVerificationBody":
"Qualcuno ha creato un account {2} con questo indirizzo email. Se sei stato tu, fai clic sul link seguente per verificare il tuo indirizzo email\n\n{0}\n\nQuesto link scadrà in {3}.\n\nSe non sei stato tu a creare questo account, ignora questo messaggio.",
"emailVerificationBodyHtml":
'<p>Qualcuno ha creato un account {2} con questo indirizzo email. Se sei stato tu, fai clic sul link seguente per verificare il tuo indirizzo email</p><p><a href="{0}">{0}</a></p><p>Questo link scadrà in {3}.</p><p>Se non sei stato tu a creare questo account, ignora questo messaggio.</p>',
"emailTestSubject": "[KEYCLOAK] - messaggio di test SMTP",
"emailTestBody": "Questo è un messaggio di test",
"emailTestBodyHtml": "<p>Questo è un messaggio di test</p>",
"identityProviderLinkSubject": "Link {0}",
"identityProviderLinkBody":
'Qualcuno vuole associare il tuo account "{1}" con l\'account "{0}" dell\'utente {2}. Se sei stato tu, fai clic sul link seguente per associare gli account\n\n{3}\n\nQuesto link scadrà in {5}.\n\nSe non vuoi associare l\'account, ignora questo messaggio. Se associ gli account, potrai accedere a {1} attraverso {0}.',
"identityProviderLinkBodyHtml":
"<p>Qualcuno vuole associare il tuo account <b>{1}</b> con l'account <b>{0}</b> dell'utente {2}. Se sei stato tu, fai clic sul link seguente per associare gli account</p><p><a href=\"{3}\">{3}</a></p><p>Questo link scadrà in {5}.</p><p>Se non vuoi associare l'account, ignora questo messaggio. Se associ gli account, potrai accedere a {1} attraverso {0}.</p>",
"passwordResetSubject": "Reimposta la password",
"passwordResetBody":
"Qualcuno ha appena richiesto di cambiare le credenziali di accesso al tuo account {2}. Se sei stato tu, fai clic sul link seguente per reimpostarle.\n\n{0}\n\nQuesto link e codice scadranno in {3}.\n\nSe non vuoi reimpostare le tue credenziali di accesso, ignora questo messaggio e non verrà effettuato nessun cambio.",
"passwordResetBodyHtml":
'<p>Qualcuno ha appena richiesto di cambiare le credenziali di accesso al tuo account {2}. Se sei stato tu, fai clic sul link seguente per reimpostarle.</p><p><a href="{0}">{0}</a></p><p>Questo link scadrà in {3}.</p><p>Se non vuoi reimpostare le tue credenziali di accesso, ignora questo messaggio e non verrà effettuato nessun cambio.</p>',
"executeActionsSubject": "Aggiorna il tuo account",
"executeActionsBody":
"Il tuo amministratore ha appena richiesto un aggiornamento del tuo account {2} ed è necessario che tu esegua la/le seguente/i azione/i: {3}. Fai clic sul link seguente per iniziare questo processo.\n\n{0}\n\nQuesto link scadrà in {4}.\n\nSe non sei a conoscenza della richiesta del tuo amministratore, ignora questo messaggio e non verrà effettuato nessun cambio.",
"executeActionsBodyHtml":
'<p>Il tuo amministratore ha appena richiesto un aggiornamento del tuo account {2} ed è necessario che tu esegua la/le seguente/i azione/i: {3}. Fai clic sul link seguente per iniziare questo processo.</p><p><a href="{0}">Link to account update</a></p><p>Questo link scadrà in {4}.</p><p>Se non sei a conoscenza della richiesta del tuo amministratore, ignora questo messaggio e non verrà effettuato nessun cambio.</p>',
"eventLoginErrorSubject": "Errore di accesso",
"eventLoginErrorBody":
"È stato rilevato un tentativo fallito di accesso al tuo account il {0} da {1}. Se non sei stato tu, per favore contatta l'amministratore.",
"eventLoginErrorBodyHtml":
"<p>È stato rilevato un tentativo fallito di accesso al tuo account il {0} da {1}. Se non sei stato tu, per favore contatta l'amministratore.</p>",
"eventRemoveTotpSubject": "Rimozione OTP (password temporanea valida una volta sola)",
"eventRemoveTotpBody":
"La OTP (password temporanea valida una volta sola) è stata rimossa dal tuo account il {0} da {1}. Se non sei stato tu, per favore contatta l'amministratore.",
"eventRemoveTotpBodyHtml":
"<p>La OTP (password temporanea valida una volta sola) è stata rimossa dal tuo account il {0} da {1}. Se non sei stato tu, per favore contatta l'amministratore.</p>",
"eventUpdatePasswordSubject": "Aggiornamento password",
"eventUpdatePasswordBody": "La tua password è stata cambiata il {0} da {1}. Se non sei stato tu, per favore contatta l'amministratore.",
"eventUpdatePasswordBodyHtml":
"<p>La tua password è stata cambiata il {0} da {1}. Se non sei stato tu, per favore contatta l'amministratore.</p>",
"eventUpdateTotpSubject": "Aggiornamento OTP (password temporanea valida una volta sola)",
"eventUpdateTotpBody":
"La OTP (password temporanea valida una volta sola) è stata aggiornata per il tuo account il {0} da {1}. Se non sei stato tu, per favore contatta l'amministratore.",
"eventUpdateTotpBodyHtml":
"<p>La OTP (password temporanea valida una volta sola) è stata aggiornata per il tuo account il {0} da {1}. Se non sei stato tu, per favore contatta l'amministratore.</p>",
"requiredAction.CONFIGURE_TOTP": "Configurazione OTP",
"requiredAction.terms_and_conditions": "Termini e condizioni",
"requiredAction.UPDATE_PASSWORD": "Aggiornamento password",
"requiredAction.UPDATE_PROFILE": "Aggiornamento profilo",
"requiredAction.VERIFY_EMAIL": "Verifica dell'indirizzo email",
"linkExpirationFormatter.timePeriodUnit.seconds": "secondi",
"linkExpirationFormatter.timePeriodUnit.seconds.1": "secondo",
"linkExpirationFormatter.timePeriodUnit.minutes": "minuti",
"linkExpirationFormatter.timePeriodUnit.minutes.1": "minuto",
"linkExpirationFormatter.timePeriodUnit.hours": "ore",
"linkExpirationFormatter.timePeriodUnit.hours.1": "ora",
"linkExpirationFormatter.timePeriodUnit.days": "giorni",
"linkExpirationFormatter.timePeriodUnit.days.1": "giorno",
"emailVerificationBodyCode": "Per favore verifica il tuo indirizzo email inserendo il codice seguente.\n\n{0}\n\n.",
"emailVerificationBodyCodeHtml": "<p>Per favore verifica il tuo indirizzo email inserendo il codice seguente.</p><p><b>{0}</b></p>",
},
"ja": {
"emailVerificationSubject": "Eメールの確認",
"emailVerificationBody":
"このメールアドレスで{2}アカウントが作成されました。以下のリンクをクリックしてメールアドレスの確認を完了してください。\n\n{0}\n\nこのリンクは{3}だけ有効です。\n\nもしこのアカウントの作成に心当たりがない場合は、このメールを無視してください。",
"emailVerificationBodyHtml":
'<p>このメールアドレスで{2}アカウントが作成されました。以下のリンクをクリックしてメールアドレスの確認を完了してください。</p><p><a href="{0}">メールアドレスの確認</a></p><p>このリンクは{3}だけ有効です。</p><p>もしこのアカウントの作成に心当たりがない場合は、このメールを無視してください。</p>',
"emailTestSubject": "[KEYCLOAK] - SMTPテストメッセージ",
"emailTestBody": "これはテストメッセージです",
"emailTestBodyHtml": "<p>これはテストメッセージです</p>",
"identityProviderLinkSubject": "リンク {0}",
"identityProviderLinkBody":
'あなたの"{1}"アカウントと{2}ユーザーの"{0}"アカウントのリンクが要求されました。以下のリンクをクリックしてアカウントのリンクを行ってください。\n\n{3}\n\nこのリンクは{5}だけ有効です。\n\nもしアカウントのリンクを行わない場合は、このメッセージを無視してください。アカウントのリンクを行うことで、{0}経由で{1}にログインすることができるようになります。',
"identityProviderLinkBodyHtml":
'<p>あなたの<b>{1}</b>アカウントと{2}ユーザーの<b>{0}</b>アカウントのリンクが要求されました。以下のリンクをクリックしてアカウントのリンクを行ってください。</p><p><a href="{3}">アカウントリンクの確認</a></p><p>このリンクは{5}だけ有効です。</p><p>もしアカウントのリンクを行わない場合は、このメッセージを無視してください。アカウントのリンクを行うことで、{0}経由で{1}にログインすることができるようになります。</p>',
"passwordResetSubject": "パスワードのリセット",
"passwordResetBody":
"あなたの{2}アカウントのパスワードの変更が要求されています。以下のリンクをクリックしてパスワードのリセットを行ってください。\n\n{0}\n\nこのリンクは{3}だけ有効です。\n\nもしパスワードのリセットを行わない場合は、このメッセージを無視してください。何も変更されません。",
"passwordResetBodyHtml":
'<p>あなたの{2}アカウントのパスワードの変更が要求されています。以下のリンクをクリックしてパスワードのリセットを行ってください。</p><p><a href="{0}">パスワードのリセット</a></p><p>このリンクは{3}だけ有効です。</p><p>もしパスワードのリセットを行わない場合は、このメッセージを無視してください。何も変更されません。</p>',
"executeActionsSubject": "アカウントの更新",
"executeActionsBody":
"次のアクションを実行することにより、管理者よりあなたの{2}アカウントの更新が要求されています: {3}。以下のリンクをクリックしてこのプロセスを開始してください。\n\n{0}\n\nこのリンクは{4}だけ有効です。\n\n管理者からのこの変更要求についてご存知ない場合は、このメッセージを無視してください。何も変更されません。",
"executeActionsBodyHtml":
'<p>次のアクションを実行することにより、管理者よりあなたの{2}アカウントの更新が要求されています: {3}。以下のリンクをクリックしてこのプロセスを開始してください。</p><p><a href="{0}">アカウントの更新</a></p><p>このリンクは{4}だけ有効です。</p><p>管理者からのこの変更要求についてご存知ない場合は、このメッセージを無視してください。何も変更されません。</p>',
"eventLoginErrorSubject": "ログインエラー",
"eventLoginErrorBody": "{0}に{1}からのログイン失敗があなたのアカウントで検出されました。心当たりがない場合は、管理者に連絡してください。",
"eventLoginErrorBodyHtml":
"<p>{0}に{1}からのログイン失敗があなたのアカウントで検出されました。心当たりがない場合は管理者に連絡してください。</p>",
"eventRemoveTotpSubject": "OTPの削除",
"eventRemoveTotpBody": "{0}に{1}からの操作でOTPが削除されました。心当たりがない場合は、管理者に連絡してください。",
"eventRemoveTotpBodyHtml": "<p>{0}に{1}からの操作でOTPが削除されました。心当たりがない場合は、管理者に連絡してください。</p>",
"eventUpdatePasswordSubject": "パスワードの更新",
"eventUpdatePasswordBody": "{0}に{1}からの操作であなたのパスワードが変更されました。心当たりがない場合は、管理者に連絡してください。",
"eventUpdatePasswordBodyHtml":
"<p>{0}に{1}からの操作であなたのパスワードが変更されました。心当たりがない場合は、管理者に連絡してください。</p>",
"eventUpdateTotpSubject": "OTPの更新",
"eventUpdateTotpBody": "{0}に{1}からの操作でOTPが更新されました。心当たりがない場合は、管理者に連絡してください。",
"eventUpdateTotpBodyHtml": "<p>{0}に{1}からの操作でOTPが更新されました。心当たりがない場合は、管理者に連絡してください。</p>",
"requiredAction.CONFIGURE_TOTP": "OTPの設定",
"requiredAction.terms_and_conditions": "利用規約",
"requiredAction.UPDATE_PASSWORD": "パスワードの更新",
"requiredAction.UPDATE_PROFILE": "プロファイルの更新",
"requiredAction.VERIFY_EMAIL": "Eメールの確認",
"linkExpirationFormatter.timePeriodUnit.seconds": "秒",
"linkExpirationFormatter.timePeriodUnit.seconds.1": "秒",
"linkExpirationFormatter.timePeriodUnit.minutes": "分",
"linkExpirationFormatter.timePeriodUnit.minutes.1": "分",
"linkExpirationFormatter.timePeriodUnit.hours": "時間",
"linkExpirationFormatter.timePeriodUnit.hours.1": "時間",
"linkExpirationFormatter.timePeriodUnit.days": "日",
"linkExpirationFormatter.timePeriodUnit.days.1": "日",
"emailVerificationBodyCode": "次のコードを入力してメールアドレスを確認してください。\n\n{0}\n\n.",
"emailVerificationBodyCodeHtml": "<p>次のコードを入力してメールアドレスを確認してください。</p><p><b>{0}</b></p>",
},
"lt": {
"emailVerificationSubject": "El. pašto patvirtinimas",
"emailVerificationBody":
"Paskyra {2} sukurta naudojant šį el. pašto adresą. Jei tai buvote Jūs, tuomet paspauskite žemiau esančią nuorodą\n\n{0}\n\nŠi nuoroda galioja {1} min.\n\nJei paskyros nekūrėte, tuomet ignuoruokite šį laišką. ",
"emailVerificationBodyHtml":
'<p>Paskyra {2} sukurta naudojant šį el. pašto adresą. Jei tao buvote Jūs, tuomet paspauskite žemiau esančią nuorodą</p><p><a href=LT"{0}">{0}</a></p><p>Ši nuoroda galioja {1} min.</p><p>nJei paskyros nekūrėte, tuomet ignuoruokite šį laišką.</p>',
"identityProviderLinkSubject": "Sąsaja {0}",
"identityProviderLinkBody":
'Kažas pageidauja susieti Jūsų "{1}" paskyrą su "{0}" {2} naudotojo paskyrą. Jei tai buvote Jūs, tuomet paspauskite žemiau esančią nuorodą norėdami susieti paskyras\n\n{3}\n\nŠi nuoroda galioja {4} min.\n\nJei paskyrų susieti nenorite, tuomet ignoruokite šį laišką. Jei paskyras susiesite, tuomet prie {1} galėsiste prisijungti per {0}.',
"identityProviderLinkBodyHtml":
'<p>žas pageidauja susieti Jūsų <b>{1}</b> paskyrą su <b>{0}</b> {2} naudotojo paskyrą. Jei tai buvote Jūs, tuomet paspauskite žemiau esančią nuorodą norėdami susieti paskyras</p><p><a href=LT"{3}">{3}</a></p><p>Ši nuoroda galioja {4} min.</p><p>Jei paskyrų susieti nenorite, tuomet ignoruokite šį laišką. Jei paskyras susiesite, tuomet prie {1} galėsiste prisijungti per {0}.</p>',
"passwordResetSubject": "Slaptažodžio atkūrimas",
"passwordResetBody":
"Kažkas pageidauja pakeisti Jūsų paskyros {2} slaptažodį. Jei tai buvote Jūs, tuomet paspauskite žemiau esančią nuorodą slaptažodžio pakeitimui.\n\n{0}\n\nŠi nuoroda ir kodas galioja {1} min.\n\nJei nepageidajate keisti slaptažodžio, tuomet ignoruokite šį laišką ir niekas nebus pakeista.",
"passwordResetBodyHtml":
'<p>Kažkas pageidauja pakeisti Jūsų paskyros {2} slaptažodį. Jei tai buvote Jūs, tuomet paspauskite žemiau esančią nuorodą slaptažodžio pakeitimui.</p><p><a href=LT"{0}">{0}</a></p><p>Ši nuoroda ir kodas galioja {1} min.</p><p>Jei nepageidajate keisti slaptažodžio, tuomet ignoruokite šį laišką ir niekas nebus pakeista.</p>',
"executeActionsSubject": "Atnaujinkite savo paskyrą",
"executeActionsBody":
"Sistemos administratorius pageidauja, kad Jūs atnaujintumėte savo {2} paskyrą. Paspauskite žemiau esančią nuorodą paskyros duomenų atnaujinimui.\n\n{0}\n\nŠi nuoroda galioja {1} min.\n\nJei Jūs neasate tikri, kad tai administratoriaus pageidavimas, tuomet ignoruokite šį laišką ir niekas nebus pakeista.",
"executeActionsBodyHtml":
'<p>Sistemos administratorius pageidauja, kad Jūs atnaujintumėte savo {2} paskyrą. Paspauskite žemiau esančią nuorodą paskyros duomenų atnaujinimui.</p><p><a href=LT"{0}">{0}</a></p><p>Ši nuoroda galioja {1} min.</p><p>Jei Jūs neasate tikri, kad tai administratoriaus pageidavimas, tuomet ignoruokite šį laišką ir niekas nebus pakeista.</p>',
"eventLoginErrorSubject": "Nesėkmingas bandymas prisijungti prie jūsų paskyros",
"eventLoginErrorBody":
"Bandymas prisijungti prie jūsų paskyros {0} iš {1} nesėkmingas. Jei tai nebuvote jūs, tuomet susisiekite su administratoriumi",
"eventLoginErrorBodyHtml":
"<p>Bandymas prisijungti prie jūsų paskyros {0} iš {1} nesėkmingas. Jei tai nebuvote jūs, tuomet susisiekite su administratoriumi</p>",
"eventRemoveTotpSubject": "OTP pašalinimas",
"eventRemoveTotpBody":
"Kažkas pageidauja atsieti TOPT Jūsų {1} paskyroje su {0}. Jei tai nebuvote Jūs, tuomet susisiekite su administratoriumi",
"eventRemoveTotpBodyHtml":
"<p>Kažkas pageidauja atsieti TOPT Jūsų <b>{1}</b> paskyroje su <b>{0}</b>. Jei tai nebuvote Jūs, tuomet susisiekite su administratoriumi</p>",
"eventUpdatePasswordSubject": "Slaptažodžio atnaujinimas",
"eventUpdatePasswordBody": "{1} paskyroje {0} pakeisas jūsų slaptažodis. Jei Jūs nekeitėte, tuomet susisiekite su administratoriumi",
"eventUpdatePasswordBodyHtml":
"<p>{1} paskyroje {0} pakeisas jūsų slaptažodis. Jei Jūs nekeitėte, tuomet susisiekite su administratoriumi</p>",
"eventUpdateTotpSubject": "OTP atnaujinimas",
"eventUpdateTotpBody": "OTP Jūsų {1} paskyroje su {0} buvo atnaujintas. Jei tai nebuvote Jūs, tuomet susisiekite su administratoriumi",
"eventUpdateTotpBodyHtml":
"<p>OTP Jūsų {1} paskyroje su {0} buvo atnaujintas. Jei tai nebuvote Jūs, tuomet susisiekite su administratoriumi</p>",
},
"nl": {
"emailVerificationSubject": "Bevestig e-mailadres",
"emailVerificationBody":
"Iemand heeft een {2} account aangemaakt met dit e-mailadres. Als u dit was, klikt u op de onderstaande koppeling om uw e-mailadres te bevestigen \n\n{0}\n\nDeze koppeling zal binnen {3} vervallen.\n\nU kunt dit bericht negeren indien u dit account niet heeft aangemaakt.",
"emailVerificationBodyHtml":
'<p>Iemand heeft een {2} account aangemaakt met dit e-mailadres. Als u dit was, klikt u op de onderstaande koppeling om uw e-mailadres te bevestigen</p><p><a href="{0}">Koppeling naar e-mailadres bevestiging</a></p><p>Deze koppeling zal binnen {3} vervallen.</p<p>U kunt dit bericht negeren indien u dit account niet heeft aangemaakt.</p>',
"emailTestSubject": "[KEYCLOAK] - SMTP testbericht",
"emailTestBody": "Dit is een testbericht",
"emailTestBodyHtml": "<p>Dit is een testbericht</p>",
"identityProviderLinkSubject": "Koppel {0}",
"identityProviderLinkBody":
'Iemand wil uw "{1}" account koppelen met "{0}" account van gebruiker {2}. Als u dit was, klik dan op de onderstaande link om de accounts te koppelen\n\n{3}\n\nDeze link zal over {5} vervallen.\n\nAls u de accounts niet wilt koppelen, negeer dan dit bericht. Als u accounts koppelt, dan kunt u bij {1} inloggen via {0}.',
"identityProviderLinkBodyHtml":
'<p>Iemand wil uw "{1}" account koppelen met "{0}" account van gebruiker {2}. Als u dit was, klik dan op de onderstaande link om de accounts te koppelen</p><p><a href="{3}">Link om accounts te koppelen</a></p><p>Deze link zal over {5} vervallen.</p><p>Als u de accounts niet wilt koppelen, negeer dan dit bericht. Als u accounts koppelt, dan kunt u bij {1} inloggen via {0}.</p>',
"passwordResetSubject": "Wijzig wachtwoord",
"passwordResetBody":
"Iemand verzocht de aanmeldgegevens van uw {2} account te wijzigen. Als u dit was, klik dan op de onderstaande koppeling om ze te wijzigen.\n\n{0}\n\nDe link en de code zullen binnen {3} vervallen.\n\nAls u uw aanmeldgegevens niet wilt wijzigen, negeer dan dit bericht en er zal niets gewijzigd worden.",
"passwordResetBodyHtml":
'<p>Iemand verzocht de aanmeldgegevens van uw {2} account te wijzigen. Als u dit was, klik dan op de onderstaande koppeling om ze te wijzigen.</p><p><a href="{0}">Wijzig aanmeldgegevens</a></p><p>De link en de code zullen binnen {3} vervallen.</p><p>Als u uw aanmeldgegevens niet wilt wijzigen, negeer dan dit bericht en er zal niets gewijzigd worden.</p>',
"executeActionsSubject": "Wijzig uw account",
"executeActionsBody":
"Uw beheerder heeft u verzocht uw {2} account te wijzigen. Klik op de onderstaande koppeling om dit proces te starten. \n\n{0}\n\nDeze link zal over {4} vervallen. \n\nAls u niet over dit verzoek op de hoogte was, negeer dan dit bericht om uw account ongewijzigd te laten.",
"executeActionsBodyHtml":
'<p>Uw beheerder heeft u verzocht uw {2} account te wijzigen. Klik op de onderstaande koppeling om dit proces te starten.</p><p><a href="{0}">Link naar account wijziging</a></p><p>Deze link zal over {4} vervallen.</p><p>Als u niet over dit verzoek op de hoogte was, negeer dan dit bericht om uw account ongewijzigd te laten.</p>',
"eventLoginErrorSubject": "Inlogfout",
"eventLoginErrorBody":
"Er is een foutieve inlogpoging gedetecteerd op uw account om {0} vanuit {1}. Als u dit niet was, neem dan contact op met de beheerder.",
"eventLoginErrorBodyHtml":
"<p>Er is een foutieve inlogpoging gedetecteerd op uw account om {0} vanuit {1}. Als u dit niet was, neem dan contact op met de beheerder.</p>",
"eventRemoveTotpSubject": "OTP verwijderd",
"eventRemoveTotpBody": "OTP is verwijderd van uw account om {0} vanuit {1}. Als u dit niet was, neem dan contact op met uw beheerder.",
"eventRemoveTotpBodyHtml":
"<p>OTP is verwijderd van uw account om {0} vanuit {1}. Als u dit niet was, neem dan contact op met uw beheerder.</p>",
"eventUpdatePasswordSubject": "Wachtwoord gewijzigd",
"eventUpdatePasswordBody": "Uw wachtwoord is gewijzigd om {0} door {1}. Als u dit niet was, neem dan contact op met uw beheerder.",
"eventUpdatePasswordBodyHtml": "<p>Uw wachtwoord is gewijzigd om {0} door {1}. Als u dit niet was, neem dan contact op met uw beheerder.</p>",
"eventUpdateTotpSubject": "OTP gewijzigd",
"eventUpdateTotpBody": "OTP is gewijzigd voor uw account om {0} door {1}. Als u dit niet was, neem dan contact op met uw beheerder.",
"eventUpdateTotpBodyHtml":
"<p>OTP is gewijzigd voor uw account om {0} door {1}. Als u dit niet was, neem dan contact op met uw beheerder.</p>",
"linkExpirationFormatter.timePeriodUnit.seconds": "seconden",
"linkExpirationFormatter.timePeriodUnit.seconds.1": "seconde",
"linkExpirationFormatter.timePeriodUnit.minutes": "minuten",
"linkExpirationFormatter.timePeriodUnit.minutes.1": "minuut",
"linkExpirationFormatter.timePeriodUnit.hours": "uur",
"linkExpirationFormatter.timePeriodUnit.hours.1": "uur",
"linkExpirationFormatter.timePeriodUnit.days": "dagen",
"linkExpirationFormatter.timePeriodUnit.days.1": "dag",
},
"no": {
"emailVerificationSubject": "Bekreft e-postadresse",
"emailVerificationBody":
"Noen har opprettet en {2} konto med denne e-postadressen. Hvis dette var deg, klikk på lenken nedenfor for å bekrefte e-postadressen din\n\n{0}\n\nDenne lenken vil utløpe om {1} minutter.\n\nHvis du ikke opprettet denne kontoen, vennligst ignorer denne meldingen.",
"emailVerificationBodyHtml":
'<p>Noen har opprettet en {2} konto med denne e-postadressen. Hvis dette var deg, klikk på lenken nedenfor for å bekrefte e-postadressen din</p><p><a href="{0}">{0}</a></p><p>Denne lenken vil utløpe om {1} minutter.</p><p>Hvis du ikke opprettet denne kontoen, vennligst ignorer denne meldingen.</p>',
"identityProviderLinkSubject": "Lenke {0}",
"identityProviderLinkBody":
"Noen vil koble din <b>{1}</b> konto med <b>{0}</b> konto til bruker {2}. Hvis dette var deg, klikk på lenken nedenfor for å koble kontoene\n\n{3}\n\nDenne lenken vil utløpe om {4} minutter\n\nHvis du ikke vil koble kontoene, vennligst ignorer denne meldingen. Hvis du kobler kontoene sammen vil du kunne logge inn til {1} gjennom {0}.",
"identityProviderLinkBodyHtml":
'<p>Noen vil koble din <b>{1}</b> konto med <b>{0}</b> konto til bruker {2}. Hvis dette var deg, klikk på lenken nedenfor for å koble kontoene.</p><p><a href="{3}">{3}</a></p><p>Denne lenken vil utløpe om {4} minutter.</p><p>Hvis du ikke vil koble kontoene, vennligst ignorer denne meldingen. Hvis du kobler kontoene sammen vil du kunne logge inn til {1} gjennom {0}.</p>',
"passwordResetSubject": "Tilbakestill passord",
"passwordResetBody":
"Noen har bedt om å endre innloggingsdetaljene til din konto {2}. Hvis dette var deg, klikk på lenken nedenfor for å tilbakestille dem.\n\n{0}\n\nDenne lenken vil utløpe om {1} minutter.\n\nHvis du ikke vil tilbakestille din innloggingsdata, vennligst ignorer denne meldingen og ingenting vil bli endret.",
"passwordResetBodyHtml":
'<p>Noen har bedt om å endre innloggingsdetaljene til din konto {2}. Hvis dette var deg, klikk på lenken nedenfor for å tilbakestille dem.</p><p><a href="{0}">{0}</a></p><p>Denne lenken vil utløpe om {1} minutter.</p><p>Hvis du ikke vil tilbakestille din innloggingsdata, vennligst ignorer denne meldingen og ingenting vil bli endret.</p>',
"executeActionsSubject": "Oppdater kontoen din",
"executeActionsBody":
"Administrator har anmodet at du oppdaterer din {2} konto. Klikk på lenken nedenfor for å starte denne prosessen\n\n{0}\n\nDenne lenken vil utløpe om {1} minutter.\n\nHvis du ikke var klar over at administrator har bedt om dette, vennligst ignorer denne meldingen og ingenting vil bli endret.",
"executeActionsBodyHtml":
'<p>Administrator har anmodet at du oppdaterer din {2} konto. Klikk på linken nedenfor for å starte denne prosessen.</p><p><a href="{0}">{0}</a></p><p>Denne lenken vil utløpe om {1} minutter.</p><p>Hvis du ikke var klar over at administrator har bedt om dette, ignorer denne meldingen og ingenting vil bli endret. </p>',
"eventLoginErrorSubject": "Innlogging feilet",
"eventLoginErrorBody":
"Et mislykket innloggingsforsøk ble oppdaget på din konto på {0} fra {1}. Hvis dette ikke var deg, vennligst kontakt administrator.",
"eventLoginErrorBodyHtml":
"<p>Et mislykket innloggingsforsøk ble oppdaget på din konto på {0} fra {1}. Hvis dette ikke var deg, vennligst kontakt administrator.</p>",
"eventRemoveTotpSubject": "Fjern engangskode",
"eventRemoveTotpBody": "Engangskode ble fjernet fra kontoen din på {0} fra {1}. Hvis dette ikke var deg, vennligst kontakt administrator.",
"eventRemoveTotpBodyHtml":
"<p>Engangskode ble fjernet fra kontoen din på {0} fra {1}. Hvis dette ikke var deg, vennligst kontakt administrator.</p>",
"eventUpdatePasswordSubject": "Oppdater passord",
"eventUpdatePasswordBody": "Ditt passord ble endret i {0} fra {1}. Hvis dette ikke var deg, vennligst kontakt administrator.",
"eventUpdatePasswordBodyHtml": "<p>Ditt passord ble endret i {0} fra {1}. Hvis dette ikke var deg, vennligst kontakt administrator. </p>",
"eventUpdateTotpSubject": "Oppdater engangskode",
"eventUpdateTotpBody": "Engangskode ble oppdatert for kontoen din på {0} fra {1}. Hvis dette ikke var deg, vennligst kontakt administrator.",
"eventUpdateTotpBodyHtml":
"<p>Engangskode ble oppdatert for kontoen din på {0} fra {1}. Hvis dette ikke var deg, vennligst kontakt administrator. </p>",
},
"pl": {
"emailVerificationSubject": "Zweryfikuj email",
"emailVerificationBody":
"Ktoś utworzył już konto {2} z tym adresem e-mail. Jeśli to Ty, kliknij poniższy link, aby zweryfikować swój adres e-mail \n\n{0}\n\nLink ten wygaśnie w ciągu {3}.\n\nJeśli nie utworzyłeś tego konta, po prostu zignoruj tę wiadomość.",
"emailVerificationBodyHtml":
'<p>Ktoś utworzył już konto {2} z tym adresem e-mail. Jeśli to Ty, kliknij <a href="{0}">ten link</a> aby zweryfikować swój adres e-mail</p><p>Link ten wygaśnie w ciągu {3}</p><p>Jeśli nie utworzyłeś tego konta, po prostu zignoruj tę wiadomość.</p>',
"emailTestSubject": "[KEYCLOAK] - wiadomość testowa SMTP",
"emailTestBody": "To jest wiadomość testowa",
"emailTestBodyHtml": "<p>To jest wiadomość testowa</p>",
"identityProviderLinkSubject": "Link {0}",
"identityProviderLinkBody":
'Ktoś chce połączyć Twoje konto "{1}" z kontem "{0}" użytkownika {2}. Jeśli to Ty, kliknij poniższy link by połączyć konta\n\n{3}\n\nTen link wygaśnie w ciągu {5}.\n\nJeśli nie chcesz połączyć konta to zignoruj tę wiadomość. Jeśli połączysz konta, będziesz mógł się zalogować na {1} przez {0}.',
"identityProviderLinkBodyHtml":
'<p>Ktoś chce połączyć Twoje konto <b>{1}</b> z kontem <b>{0}</b> użytkownika {2}. Jeśli to Ty, kliknij <a href="{3}">ten link</a> by połączyć konta.</p><p>Ten link wygaśnie w ciągu {5}.</p><p>Jeśli nie chcesz połączyć konta to zignoruj tę wiadomość. Jeśli połączysz konta, będziesz mógł się zalogować na {1} przez {0}.</p>',
"passwordResetSubject": "Zresetuj hasło",
"passwordResetBody":
"Ktoś właśnie poprosił o zmianę danych logowania Twojego konta {2}. Jeśli to Ty, kliknij poniższy link, aby je zresetować.\n\n{0}\n\nTen link i kod stracą ważność w ciągu {3}.\n\nJeśli nie chcesz zresetować swoich danych logowania, po prostu zignoruj tę wiadomość i nic się nie zmieni.",
"passwordResetBodyHtml":
'<p>Ktoś właśnie poprosił o zmianę poświadczeń Twojego konta {2}. Jeśli to Ty, kliknij poniższy link, aby je zresetować.</p><p><a href="{0}">Link do resetowania poświadczeń</a></p><p>Ten link wygaśnie w ciągu {3}.</p><p>Jeśli nie chcesz resetować swoich poświadczeń, po prostu zignoruj tę wiadomość i nic się nie zmieni.</p>',
"executeActionsSubject": "Zaktualizuj swoje konto",
"executeActionsBody":
"Administrator właśnie zażądał aktualizacji konta {2} poprzez wykonanie następujących działań: {3}. Kliknij poniższy link, aby rozpocząć ten proces.\n\n{0}\n\nTen link wygaśnie w ciągu {4}.\n\nJeśli nie masz pewności, że administrator tego zażądał, po prostu zignoruj tę wiadomość i nic się nie zmieni.",
"executeActionsBodyHtml":
'<p>Administrator właśnie zażądał aktualizacji konta {2} poprzez wykonanie następujących działań: {3}. Kliknij <a href="{0}">ten link</a>, aby rozpocząć proces.</p><p>Link ten wygaśnie w ciągu {4}.</p><p>Jeśli nie masz pewności, że administrator tego zażądał, po prostu zignoruj tę wiadomość i nic się nie zmieni.</p>',
"eventLoginErrorSubject": "Błąd logowania",
"eventLoginErrorBody":
"Nieudana próba logowania została wykryta na Twoim koncie {0} z {1}. Jeśli to nie Ty, skontaktuj się z administratorem.",
"eventLoginErrorBodyHtml":
"<p>Nieudana próba logowania została wykryta na Twoim koncie {0} z {1}. Jeśli to nie Ty, skontaktuj się z administratorem.</p>",
"eventRemoveTotpSubject": "Usuń hasło jednorazowe (OTP)",
"eventRemoveTotpBody":
"Hasło jednorazowe (OTP) zostało usunięte z Twojego konta w {0} z {1}. Jeśli to nie Ty, skontaktuj się z administratorem.",
"eventRemoveTotpBodyHtml":
"<p>Hasło jednorazowe (OTP) zostało usunięte z Twojego konta w {0} z {1}. Jeśli to nie Ty, skontaktuj się z administratorem.</p>",
"eventUpdatePasswordSubject": "Aktualizuj hasło",
"eventUpdatePasswordBody": "Twoje hasło zostało zmienione {0} z {1}. Jeśli to nie Ty, skontaktuj się z administratorem.",
"eventUpdatePasswordBodyHtml": "<p>Twoje hasło zostało zmienione {0} z {1}. Jeśli to nie Ty, skontaktuj się z administratorem.</p>",
"eventUpdateTotpSubject": "Aktualizuj hasło jednorazowe (OTP)",
"eventUpdateTotpBody":
"Hasło jednorazowe (OTP) zostało zaktualizowane na Twoim koncie {0} z {1}. Jeśli to nie Ty, skontaktuj się z administratorem.",
"eventUpdateTotpBodyHtml":
"<p>Hasło jednorazowe (OTP) zostało zaktualizowane na Twoim koncie {0} z {1}. Jeśli to nie Ty, skontaktuj się z administratorem.</p>",
"requiredAction.CONFIGURE_TOTP": "Konfiguracja hasła jednorazowego (OTP)",
"requiredAction.terms_and_conditions": "Regulamin",
"requiredAction.UPDATE_PASSWORD": "Aktualizacja hasła",
"requiredAction.UPDATE_PROFILE": "Aktualizacja profilu",
"requiredAction.VERIFY_EMAIL": "Weryfikacja adresu e-mail",
"linkExpirationFormatter.timePeriodUnit.seconds": "sekund",
"linkExpirationFormatter.timePeriodUnit.seconds.1": "sekunda",
"linkExpirationFormatter.timePeriodUnit.seconds.2": "sekundy",
"linkExpirationFormatter.timePeriodUnit.seconds.3": "sekundy",
"linkExpirationFormatter.timePeriodUnit.seconds.4": "sekundy",
"linkExpirationFormatter.timePeriodUnit.minutes": "minut",
"linkExpirationFormatter.timePeriodUnit.minutes.1": "minuta",
"linkExpirationFormatter.timePeriodUnit.minutes.2": "minuty",
"linkExpirationFormatter.timePeriodUnit.minutes.3": "minuty",
"linkExpirationFormatter.timePeriodUnit.minutes.4": "minuty",
"linkExpirationFormatter.timePeriodUnit.hours": "godzin",
"linkExpirationFormatter.timePeriodUnit.hours.1": "godzina",
"linkExpirationFormatter.timePeriodUnit.hours.2": "godziny",
"linkExpirationFormatter.timePeriodUnit.hours.3": "godziny",
"linkExpirationFormatter.timePeriodUnit.hours.4": "godziny",
"linkExpirationFormatter.timePeriodUnit.days": "dni",
"linkExpirationFormatter.timePeriodUnit.days.1": "dzień",
"emailVerificationBodyCode": "Potwierdź swój adres e-mail wprowadzając następujący kod.\n\n{0}\n\n.",
"emailVerificationBodyCodeHtml": "<p>Potwierdź swój adres e-mail, wprowadzając następujący kod.</p><p><b>{0}</b></p>",
},
"pt-BR": {
"emailVerificationSubject": "Verificação de e-mail",
"emailVerificationBody":
"Alguém criou uma conta {2} com este endereço de e-mail. Se foi você, clique no link abaixo para verificar o seu endereço de email\n\n{0}\n\nEste link irá expirar dentro de {3}.\n\nSe não foi você que criou esta conta, basta ignorar esta mensagem.",
"emailVerificationBodyHtml":
'<p>Alguém criou uma conta {2} com este endereço de e-mail. Se foi você, clique no link abaixo para verificar o seu endereço de email</p><p><a href="{0}">{0}</a></p><p>Este link irá expirar dentro de {3}.</p><p>Se não foi você que criou esta conta, basta ignorar esta mensagem.</p>',
"emailTestSubject": "[KEYCLOAK] - SMTP mensagem de teste",
"emailTestBody": "Esta é uma mensagem de teste",
"emailTestBodyHtml": "<p>Esta é uma mensagem de teste</p>",
"identityProviderLinkSubject": "Vincular {0}",
"identityProviderLinkBody":
'Alguém quer vincular sua conta "{1}" com a conta "{0}" do usuário {2} . Se foi você, clique no link abaixo para vincular as contas.\n\n{3}\n\nEste link irá expirar em {5}.\n\nSe você não quer vincular a conta, apenas ignore esta mensagem. Se você vincular as contas, você será capaz de logar em {1} atrávés de {0}.',
"identityProviderLinkBodyHtml":
'<p>Alguém quer vincular sua conta <b>{1}</b> com a conta <b>{0}</b> do usuário {2} . Se foi você, clique no link abaixo para vincular as contas.</p><p><a href="{3}">{3}</a></p><p>Este link irá expirar em {5}.</p><p>Se você não quer vincular a conta, apenas ignore esta mensagem. Se você vincular as contas, você será capaz de logar em {1} atrávés de {0}.</p>',
"passwordResetSubject": "Redefinição de senha",
"passwordResetBody":
"Alguém solicitou uma alteração de senha da sua conta {2}. Se foi você, clique no link abaixo para redefini-la.\n\n{0}\n\nEste link e código expiram em {3}.\n\nSe você não deseja redefinir sua senha, apenas ignore esta mensagem e nada será alterado.",
"passwordResetBodyHtml":
'<p>Alguém solicitou uma alteração de senha da sua conta {2}. Se foi você, clique no link abaixo para redefini-la.</p><p><a href="{0}">Link para redefinir a senha</a></p><p>Este link irá expirar em {3}.</p><p>Se você não deseja redefinir sua senha, apenas ignore esta mensagem e nada será alterado.</p>',
"executeActionsSubject": "Atualização de conta",
"executeActionsBody":
"O administrador solicitou que você atualize sua conta {2} executando a(s) seguinte(s) ação(ões): {3}. Clique no link abaixo para iniciar o processo.\n\n{0}\n\nEste link irá expirar em {4}.\n\nSe você não tem conhecimento de que o administrador solicitou isso, basta ignorar esta mensagem e nada será alterado.",
"executeActionsBodyHtml":
'<p>O administrador solicitou que você atualize sua conta {2} executando a(s) seguinte(s) ação(ões): {3}. Clique no link abaixo para iniciar o processo.</p><p><a href="{0}">Link to account update</a></p><p>Este link irá expirar em {4}.</p><p>Se você não tem conhecimento de que o administrador solicitou isso, basta ignorar esta mensagem e nada será alterado.</p>',
"eventLoginErrorSubject": "Erro de login",
"eventLoginErrorBody":
"Uma tentativa de login mal sucedida para a sua conta foi detectada em {0} de {1}. Se não foi você, por favor, entre em contato com um administrador.",
"eventLoginErrorBodyHtml":
"<p>Uma tentativa de login mal sucedida para a sua conta foi detectada em {0} de {1}. Se não foi você, por favor, entre em contato com um administrador.</p>",
"eventRemoveTotpSubject": "Remover OTP",
"eventRemoveTotpBody": "OTP foi removido da sua conta em {0} de {1}. Se não foi você, por favor, entre em contato com um administrador.",
"eventRemoveTotpBodyHtml":
"<p>OTP foi removido da sua conta em {0} de {1}. Se não foi você, por favor, entre em contato com um administrador.</p>",
"eventUpdatePasswordSubject": "Atualização de senha",
"eventUpdatePasswordBody": "Sua senha foi alterada em {0} de {1}. Se não foi você, por favor, entre em contato com um administrador.",
"eventUpdatePasswordBodyHtml":
"<p>Sua senha foi alterada em {0} de {1}. Se não foi você, por favor, entre em contato com um administrador.</p>",
"eventUpdateTotpSubject": "Atualização OTP",
"eventUpdateTotpBody":
"OTP foi atualizado para a sua conta em {0} de {1}. Se não foi você, por favor, entre em contato com um administrador.",
"eventUpdateTotpBodyHtml":
"<p>OTP foi atualizado para a sua conta em {0} de {1}. Se não foi você, por favor, entre em contato com um administrador.</p>",
"requiredAction.CONFIGURE_TOTP": "Configurar OTP",
"requiredAction.terms_and_conditions": "Termos e Condições",
"requiredAction.UPDATE_PASSWORD": "Atualizar Senha",
"requiredAction.UPDATE_PROFILE": "Atualizar Perfil",
"requiredAction.VERIFY_EMAIL": "Verificar E-mail",
"linkExpirationFormatter.timePeriodUnit.seconds": "segundos",
"linkExpirationFormatter.timePeriodUnit.seconds.1": "segundo",
"linkExpirationFormatter.timePeriodUnit.minutes": "minutos",
"linkExpirationFormatter.timePeriodUnit.minutes.1": "minuto",
"linkExpirationFormatter.timePeriodUnit.hours": "horas",
"linkExpirationFormatter.timePeriodUnit.hours.1": "hora",
"linkExpirationFormatter.timePeriodUnit.days": "dias",
"linkExpirationFormatter.timePeriodUnit.days.1": "dia",
"emailVerificationBodyCode": "Verifique seu endereço de e-mail digitando o seguinte código.\n\n{0}\n\n.",
"emailVerificationBodyCodeHtml": "<p>Verifique seu endereço de e-mail digitando o seguinte código.</p><p><b>{0}</b></p>",
},
"ru": {
"emailVerificationSubject": "Подтверждение E-mail",
"emailVerificationBody":
"Кто-то создал учетную запись {2} с этим E-mail. Если это были Вы, нажмите на следующую ссылку для подтверждения вашего email\n\n{0}\n\nЭта ссылка устареет через {1} минут.\n\nЕсли Вы не создавали учетную запись, просто проигнорируйте это письмо.",
"emailVerificationBodyHtml":
'<p>Кто-то создал учетную запись {2} с этим E-mail. Если это были Вы, нажмите по ссылке для подтверждения вашего E-mail</p><p><a href="{0}">{0}</a></p><p>Эта ссылка устареет через {1} минут.</p><p>Если Вы не создавали учетную запись, просто проигнорируйте это письмо.</p>',
"identityProviderLinkSubject": "Ссылка {0}",
"identityProviderLinkBody":
'Кто-то хочет связать вашу учетную запись "{1}" с "{0}" учетной записью пользователя {2} . Если это были Вы, нажмите по следующей ссылке, чтобы связать учетные записи\n\n{3}\n\nЭта ссылка устареет через {4} минут.\n\nЕсли это не хотите объединять учетные записи, просто проигнориуйте это письмо. После объединения учетных записей Вы можете войти в {1} через {0}.',
"identityProviderLinkBodyHtml":
'<p>Кто-то хочет связать вашу учетную запись <b>{1}</b> с <b>{0}</b> учетной записью пользователя {2} . Если это были Вы, нажмите по следующей ссылке, чтобы связать учетные записи</p><p><a href="{3}">{3}</a></p><p>Эта ссылка устареет через {4} минут.</p><p>Если это не хотите объединять учетные записи, просто проигнориуйте это письмо. После объединения учетных записей Вы можете войти в {1} через {0}.</p>',
"passwordResetSubject": "Сброс пароля",
"passwordResetBody":
"Кто-то только что запросил изменение пароля от Вашей учетной записи {2}. Если это были Вы, нажмите на следующую ссылку, чтобы сбросить его.\n\n{0}\n\nЭта ссылка устареет через {1} минут.\n\nЕсли Вы не хотите сбрасывать пароль, просто проигнорируйте это письмо.",
"passwordResetBodyHtml":
'<p>Кто-то только что запросил изменение пароля от Вашей учетной записи {2}. Если это были Вы, нажмите на следующую ссылку, чтобы сбросить его.</p><p><a href="{0}">{0}</a></p><p>Эта ссылка устареет через {1} минут.</p><p>Если Вы не хотите сбрасывать пароль, просто проигнорируйте это письмо и ничего не изменится.</p>',
"executeActionsSubject": "Обновление Вашей учетной записи",
"executeActionsBody":
"Администратор просит Вас обновить данные Вашей учетной записи {2}. Нажмите по следующей ссылке чтобы начать этот процесс.\n\n{0}\n\nЭта ссылка устареет через {1} минут.\n\nЕсли у вас есть подозрения, что администратор не мог сделать такой запрос, просто проигнорируйте это письмо.",
"executeActionsBodyHtml":
'<p>Администратор просит Вас обновить данные Вашей учетной записи {2}. Нажмите по следующей ссылке чтобы начать этот процесс.</p><p><a href="{0}">{0}</a></p><p>Эта ссылка устареет через {1} минут.</p><p>Если у вас есть подозрения, что администратор не мог сделать такой запрос, просто проигнорируйте это письмо.</p>',
"eventLoginErrorSubject": "Ошибка входа",
"eventLoginErrorBody":
"Была зафиксирована неудачная попытка входа в Вашу учетную запись {0} с {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.",
"eventLoginErrorBodyHtml":
"<p>Была зафиксирована неудачная попытка входа в Вашу учетную запись {0} с {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.</p>",
"eventRemoveTotpSubject": "Удалить OTP",
"eventRemoveTotpBody": "OTP был удален из вашей учетной записи {0} c {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.",
"eventRemoveTotpBodyHtml":
"<p>OTP был удален из вашей учетной записи {0} c {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.</p>",
"eventUpdatePasswordSubject": "Обновление пароля",
"eventUpdatePasswordBody": "Ваш пароль был изменен в {0} с {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.",
"eventUpdatePasswordBodyHtml": "<p>Ваш пароль был изменен в {0} с {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.</p>",
"eventUpdateTotpSubject": "Обновление OTP",
"eventUpdateTotpBody": "OTP был обновлен в вашей учетной записи {0} с {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.",
"eventUpdateTotpBodyHtml":
"<p>OTP был обновлен в вашей учетной записи {0} с {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.</p>",
},
"sk": {
"emailVerificationSubject": "Overenie e-mailu",
"emailVerificationBody":
"Niekto vytvoril účet {2} s touto e-mailovou adresou. Ak ste to vy, kliknite na nižšie uvedený odkaz a overte svoju e-mailovú adresu \n\n{0}\n\nTento odkaz uplynie do {1} minút.\n\nAk ste tento účet nevytvorili, ignorujte túto správu.",
"emailVerificationBodyHtml":
'<p>Niekto vytvoril účet {2} s touto e-mailovou adresou. Ak ste to vy, kliknite na nižšie uvedený odkaz na overenie svojej e-mailovej adresy.</p><p><a href="{0}"> Odkaz na overenie e-mailovej adresy </a></p><p>Platnosť odkazu vyprší za {1} minút.</p><p> Ak ste tento účet nevytvorili, ignorujte túto správu.</p>',
"emailTestSubject": "[KEYCLOAK] - Testovacia správa SMTP",
"emailTestBody": "Toto je skúšobná správa",
"emailTestBodyHtml": "<p>Toto je skúšobná správa</p>",
"identityProviderLinkSubject": "Odkaz {0}",
"identityProviderLinkBody":
'Niekto chce prepojiť váš účet "{1}" s účtom {0}"používateľa {2}. Ak ste to vy, kliknutím na odkaz nižšie prepojte účty. \n\n{3}\n\nTento odkaz uplynie do {4} minút.\n\nAk nechcete prepojiť účet, jednoducho ignorujte túto správu , Ak prepájate účty, budete sa môcť prihlásiť do {1} až {0}.',
"identityProviderLinkBodyHtml":
'<p>Niekto chce prepojiť váš účet <b>{1}</b> s účtom <b>{0}</b> používateľa {2}. Ak ste to vy, kliknutím na odkaz nižšie prepojte účty</p><p><a href="{3}">Odkaz na potvrdenie prepojenia účtu </a></p><p> Platnosť tohto odkazu vyprší v rámci {4} minút.</p><p>Ak nechcete prepojiť účet, ignorujte túto správu. Ak prepojujete účty, budete sa môcť prihlásiť do {1} až {0}.</p>',
"passwordResetSubject": "Obnovenie hesla",
"passwordResetBody":
"Niekto požiadal, aby ste zmenili svoje poverenia účtu {2}. Ak ste to vy, kliknite na odkaz uvedený nižšie, aby ste ich vynulovali.\n\n{0}\n\nTento odkaz a kód uplynie do {1} minút.\n\nAk nechcete obnoviť svoje poverenia , ignorujte túto správu a nič sa nezmení.",
"passwordResetBodyHtml":
'<p>Niekto požiadal, aby ste zmenili svoje poverenia účtu {2}. Ak ste to vy, kliknutím na odkaz nižšie ich resetujte.</p><p><a href="{0}">Odkaz na obnovenie poverení </a></p><p>Platnosť tohto odkazu vyprší v priebehu {1} minút.</p><p>Ak nechcete obnoviť svoje poverenia, ignorujte túto správu a nič sa nezmení.</p>',
"executeActionsSubject": "Aktualizujte svoj účet",
"executeActionsBody":
"Váš administrátor práve požiadal o aktualizáciu vášho účtu {2} vykonaním nasledujúcich akcií: {3}. Kliknutím na odkaz uvedený nižšie spustíte tento proces.\n\n{0}\n\nTento odkaz vyprší za {1} minúty.\n\nAk si nie ste vedomý, že váš adminstrátor o toto požiadal, ignorujte túto správu a nič bude zmenené.",
"executeActionsBodyHtml":
'<p>Váš správca práve požiadal o aktualizáciu vášho účtu {2} vykonaním nasledujúcich akcií: {3}. Kliknutím na odkaz uvedený nižšie spustíte tento proces.</p><p><a href="{0}"> Odkaz na aktualizáciu účtu </a></p><p> Platnosť tohto odkazu uplynie do {1} minúty.</p><p> Ak si nie ste vedomí, že váš adminstrátor o toto požiadal, ignorujte túto správu a nič sa nezmení.</p>',
"eventLoginErrorSubject": "Chyba prihlásenia",
"eventLoginErrorBody":
"Bol zistený neúspešný pokus o prihlásenie do vášho účtu v {0} z {1}. Ak ste to neboli vy, obráťte sa na administrátora.",
"eventLoginErrorBodyHtml":
"<p>Bol zistený neúspešný pokus o prihlásenie vášho účtu na {0} z {1}. Ak ste to neboli vy, kontaktujte administrátora.</p>",
"eventRemoveTotpSubject": "Odstrániť TOTP",
"eventRemoveTotpBody": "OTP bol odstránený z vášho účtu dňa {0} z {1}. Ak ste to neboli vy, obráťte sa na administrátora.",
"eventRemoveTotpBodyHtml": "<p>OTP bol odstránený z vášho účtu dňa {0} z {1}. Ak ste to neboli vy, kontaktujte administrátora.</p>",
"eventUpdatePasswordSubject": "Aktualizovať heslo",
"eventUpdatePasswordBody": "Vaše heslo bolo zmenené na {0} z {1}. Ak ste to neboli vy, obráťte sa na administrátora.",
"eventUpdatePasswordBodyHtml": "<p>Vaše heslo bolo zmenené na {0} z {1}. Ak ste to neboli vy, kontaktujte administrátora.</p>",
"eventUpdateTotpSubject": "Aktualizácia TOTP",
"eventUpdateTotpBody": "TOTP bol aktualizovaný pre váš účet na {0} z {1}. Ak ste to neboli vy, obráťte sa na administrátora.",
"eventUpdateTotpBodyHtml": "<p>TOTP bol aktualizovaný pre váš účet dňa {0} z {1}. Ak ste to neboli vy, kontaktujte administrátora.</p>",
"requiredAction.CONFIGURE_TOTP": "Konfigurácia OTP",
"requiredAction.terms_and_conditions": "Zmluvné podmienky",
"requiredAction.UPDATE_PASSWORD": "Aktualizovať heslo",
"requiredAction.UPDATE_PROFILE": "Aktualizovať profil",
"requiredAction.VERIFY_EMAIL": "Overiť e-mail",
"linkExpirationFormatter.timePeriodUnit.seconds": "sekundy",
"linkExpirationFormatter.timePeriodUnit.seconds.1": "sekunda",
"linkExpirationFormatter.timePeriodUnit.minutes": "minuty",
"linkExpirationFormatter.timePeriodUnit.minutes.1": "minúta",
"linkExpirationFormatter.timePeriodUnit.hours": "hodiny",
"linkExpirationFormatter.timePeriodUnit.hours.1": "hodina",
"linkExpirationFormatter.timePeriodUnit.days": "dni",
"linkExpirationFormatter.timePeriodUnit.days.1": "deň ",
},
"sv": {
"emailVerificationSubject": "Verifiera e-post",
"emailVerificationBody":
"Någon har skapat ett {2} konto med den här e-postadressen. Om det var du, klicka då på länken nedan för att verifiera din e-postadress\n\n{0}\n\nDen här länken kommer att upphöra inom {1} minuter.\n\nOm det inte var du som skapade det här kontot, ignorera i så fall det här meddelandet.",
"emailVerificationBodyHtml":
'<p>Någon har skapat ett {2} konto med den här e-postadressen. Om det var du, klicka då på länken nedan för att verifiera din e-postadress</p><p><a href="{0}">{0}</a></p><p>Den här länken kommer att upphöra inom {1} minuter.</p><p>Om det inte var du som skapade det här kontot, ignorera i så fall det här meddelandet.</p>',
"identityProviderLinkSubject": "Länk {0}",
"identityProviderLinkBody":
'Någon vill länka ditt "{1}" konto med "{0}" kontot tillhörande användaren {2} . Om det var du, klicka då på länken nedan för att länka kontona\n\n{3}\n\nDen här länken kommer att upphöra inom {4} minuter.\n\nOm du inte vill länka kontot, ignorera i så fall det här meddelandet. Om du länkar kontona, så kan du logga in till {1} genom {0}.',
"identityProviderLinkBodyHtml":
'<p>Någon vill länka ditt <b>{1}</b> konto med <b>{0}</b> kontot tillhörande användaren {2} . Om det var du, klicka då på länken nedan för att länka kontona</p><p><a href="{3}">{3}</a></p><p>Den här länken kommer att upphöra inom {4} minuter.</p><p>Om du inte vill länka kontot, ignorera i så fall det här meddelandet. Om du länkar kontona, så kan du logga in till {1} genom {0}.</p>',
"passwordResetSubject": "Återställ lösenord",
"passwordResetBody":
"Någon har precis bett om att ändra användaruppgifter för ditt konto {2}. Om det var du, klicka då på länken nedan för att återställa dem.\n\n{0}\n\nDen här länken och koden kommer att upphöra inom {1} minuter.\n\nOm du inte vill återställa dina kontouppgifter, ignorera i så fall det här meddelandet så kommer inget att ändras.",
"passwordResetBodyHtml":
'<p>Någon har precis bett om att ändra användaruppgifter för ditt konto {2}. Om det var du, klicka då på länken nedan för att återställa dem.</p><p><a href="{0}">{0}</a></p><p>Den här länken och koden kommer att upphöra inom {1} minuter.</p><p>Om du inte vill återställa dina kontouppgifter, ignorera i så fall det här meddelandet så kommer inget att ändras.</p>',
"executeActionsSubject": "Uppdatera ditt konto",
"executeActionsBody":
"Din administratör har precis bett om att du skall uppdatera ditt {2} konto. Klicka på länken för att påbörja processen.\n\n{0}\n\nDen här länken kommer att upphöra inom {1} minuter.\n\nOm du är omedveten om att din administratör har bett om detta, ignorera i så fall det här meddelandet så kommer inget att ändras.",
"executeActionsBodyHtml":
'<p>Din administratör har precis bett om att du skall uppdatera ditt {2} konto. Klicka på länken för att påbörja processen.</p><p><a href="{0}">{0}</a></p><p>Den här länken kommer att upphöra inom {1} minuter.</p><p>Om du är omedveten om att din administratör har bett om detta, ignorera i så fall det här meddelandet så kommer inget att ändras.</p>',
"eventLoginErrorSubject": "Inloggningsfel",
"eventLoginErrorBody":
"Ett misslyckat inloggningsförsök har upptäckts på ditt konto på {0} från {1}. Om det inte var du, vänligen kontakta i så fall en administratör.",
"eventLoginErrorBodyHtml":
"<p>Ett misslyckat inloggningsförsök har upptäckts på ditt konto den {0} från {1}. Om det inte var du, vänligen kontakta i så fall en administratör.</p>",
"eventRemoveTotpSubject": "Ta bort OTP",
"eventRemoveTotpBody": "OTP togs bort från ditt konto den {0} från {1}. Om det inte var du, vänligen kontakta i så fall en administratör.",
"eventRemoveTotpBodyHtml":
"<p>OTP togs bort från ditt konto den {0} från {1}. Om det inte var du, vänligen kontakta i så fall en administratör.</p>",
"eventUpdatePasswordSubject": "Uppdatera lösenord",
"eventUpdatePasswordBody": "Ditt lösenord ändrades den {0} från {1}. Om det inte var du, vänligen kontakta i så fall en administratör.",
"eventUpdatePasswordBodyHtml":
"<p>Ditt lösenord ändrades den {0} från {1}. Om det inte var du, vänligen kontakta i så fall en administratör.</p>",
"eventUpdateTotpSubject": "Uppdatera OTP",
"eventUpdateTotpBody": "OTP uppdaterades för ditt konto den {0} från {1}. Om det inte var du, vänligen kontakta i så fall en administratör.",
"eventUpdateTotpBodyHtml":
"<p>OTP uppdaterades för ditt konto den {0} från {1}. Om det inte var du, vänligen kontakta i så fall en administratör.</p>",
},
"tr": {
"emailVerificationSubject": "E-postayı doğrula",
"emailVerificationBody":
"Birisi bu e-posta adresiyle bir {2} hesap oluşturdu. Bu sizseniz, e-posta adresinizi doğrulamak için aşağıdaki bağlantıya tıklayın\n\n{0}\n\nBu bağlantı {3} içinde sona erecek.\n\nBu hesabı oluşturmadıysanız, sadece bu iletiyi yoksayınız.",
"emailVerificationBodyHtml":
'<p>Birisi bu e-posta adresiyle bir {2} hesap oluşturdu. Bu sizseniz, e-posta adresinizi doğrulamak için aşağıdaki bağlantıyı tıklayın.</p><p><a href="{0}">E-posta adresi doğrulama adresi</a></p><p>Bu bağlantının süresi {3} içerisinde sona erecek.</p><p>Bu hesabı siz oluşturmadıysanız, bu mesajı göz ardı edin.</p>',
"emailTestSubject": "[KEYCLOAK] - SMTP test mesajı",
"emailTestBody": "Bu bir test mesajı",
"emailTestBodyHtml": "<p>Bu bir test mesajı</p>",
"identityProviderLinkSubject": "Link {0}",
"identityProviderLinkBody":
'Birisi "{1}" hesabınızı "{0}" kullanıcı hesabı {2} ile bağlamak istiyor. Bu sizseniz, hesapları bağlamak için aşağıdaki bağlantıyı tıklayın:\n\n{3}\n\nBu bağlantı {5} içinde sona erecek.\n\nHesabınızı bağlamak istemiyorsanız bu mesajı göz ardı edin. Hesapları bağlarsanız, {1} ile {0} arasında oturum açabilirsiniz.',
"identityProviderLinkBodyHtml":
'<p>Birisi <b> {1} </ b> hesabınızı {2} kullanıcısı <b> {0} </ b> hesabına bağlamak istiyor. Bu sizseniz, bağlantı vermek için aşağıdaki bağlantıyı tıklayın</p><p><a href="{3}">Hesap bağlantısını onaylamak için bağlantı</a></p><p>Bu bağlantının süresi {5} içerisinde sona erecek.</p><p>Hesabı bağlamak istemiyorsanız, bu mesajı göz ardı edin. Hesapları bağlarsanız, {1} ile {0} arasında oturum açabilirsiniz.</p>',
"passwordResetSubject": "Şifreyi sıfırla",
"passwordResetBody":
"Birisi, {2} hesabınızın kimlik bilgilerini değiştirmeyi istedi.Bu sizseniz, sıfırlamak için aşağıdaki bağlantıyı tıklayın.\n\n{0}\n\nBu bağlantı ve kod {3} içinde sona erecek.\n\nFakat bilgilerinizi sıfırlamak istemiyorsanız, Sadece bu mesajı görmezden gelin ve hiçbir şey değişmeyecek.",
"passwordResetBodyHtml":
'<p>Birisi, {2} hesabınızın kimlik bilgilerini değiştirmeyi istedi. Sizseniz, sıfırlamak için aşağıdaki linke tıklayınız.</p><p><a href="{0}">Kimlik bilgilerini sıfırlamak için bağlantı</a></p><p>Bu bağlantının süresi {3} içerisinde sona erecek.</p><p>Kimlik bilgilerinizi sıfırlamak istemiyorsanız, bu mesajı göz ardı edin.</p>',
"executeActionsSubject": "Hesabınızı Güncelleyin",
"executeActionsBody":
"Yöneticiniz aşağıdaki işlemleri gerçekleştirerek {2} hesabınızı güncelledi: {3}. Bu işlemi başlatmak için aşağıdaki linke tıklayın.\n\n{0}\n\nBu bağlantının süresi {4} içerisinde sona erecek.\n\nYöneticinizin bunu istediğinden habersizseniz, bu mesajı göz ardı edin ve hiçbir şey değişmez.",
"executeActionsBodyHtml":
'<p>Yöneticiniz aşağıdaki işlemleri gerçekleştirerek {2} hesabınızı güncelledi: {3}. Bu işlemi başlatmak için aşağıdaki linke tıklayın.</p><p><a href="{0}">Hesap güncelleme bağlantısı</a></p><p>Bu bağlantının süresi {4} içerisinde sona erecek.</p><p>Yöneticinizin bunu istediğinden habersizseniz, bu mesajı göz ardı edin ve hiçbir şey değişmez.</p>',
"eventLoginErrorSubject": "Giriş hatası",
"eventLoginErrorBody": "{1} 'den {0} tarihinde başarısız bir giriş denemesi yapıldı. Bu siz değilseniz, lütfen yöneticiyle iletişime geçin.",
"eventLoginErrorBodyHtml":
"<p>{1} 'den {0} tarihinde başarısız bir giriş denemesi yapıldı. Bu siz değilseniz, lütfen yöneticiyle iletişime geçin.</p>",
"eventRemoveTotpSubject": "OTP'yi kaldır",
"eventRemoveTotpBody": "OTP, {0} tarihinden {1} tarihinde hesabınızdan kaldırıldı. Bu siz değilseniz, lütfen yöneticiyle iletişime geçin.",
"eventRemoveTotpBodyHtml":
"<p>OTP, {0} tarihinden {1} tarihinde hesabınızdan kaldırıldı. Bu siz değilseniz, lütfen yöneticiyle iletişime geçin.</p>",
"eventUpdatePasswordSubject": "Şifreyi güncelle",
"eventUpdatePasswordBody": "Şifreniz {0} tarihinde {0} tarihinde değiştirildi. Bu siz değilseniz, lütfen yöneticiyle iletişime geçin.",
"eventUpdatePasswordBodyHtml":
"<p>Şifreniz {0} tarihinde {0} tarihinde değiştirildi. Bu siz değilseniz, lütfen yöneticiyle iletişime geçin.</p>",
"eventUpdateTotpSubject": "OTP'yi Güncelle",
"eventUpdateTotpBody": "OTP, {0} tarihinden {1} tarihinde hesabınız için güncellendi. Bu siz değilseniz, lütfen yöneticiyle iletişime geçin.",
"eventUpdateTotpBodyHtml":
"<p>OTP, {0} tarihinden {1} tarihinde hesabınız için güncellendi. Bu siz değilseniz, lütfen yöneticiyle iletişime geçin.</p>",
"requiredAction.CONFIGURE_TOTP": "OTP'yi yapılandır",
"requiredAction.terms_and_conditions": "Şartlar ve Koşullar",
"requiredAction.UPDATE_PASSWORD": "Şifre Güncelleme",
"requiredAction.UPDATE_PROFILE": "Profilleri güncelle",
"requiredAction.VERIFY_EMAIL": "E-mail doğrula",
"linkExpirationFormatter.timePeriodUnit.seconds": "saniye",
"linkExpirationFormatter.timePeriodUnit.seconds.1": "saniye",
"linkExpirationFormatter.timePeriodUnit.minutes": "dakika",
"linkExpirationFormatter.timePeriodUnit.minutes.1": "dakika",
"linkExpirationFormatter.timePeriodUnit.hours": "saat",
"linkExpirationFormatter.timePeriodUnit.hours.1": "saat",
"linkExpirationFormatter.timePeriodUnit.days": "gün",
"linkExpirationFormatter.timePeriodUnit.days.1": "gün",
"emailVerificationBodyCode": "Lütfen aşağıdaki kodu girerek e-posta adresinizi doğrulayın.\n\n{0}\n\n.",
"emailVerificationBodyCodeHtml": "<p>Lütfen aşağıdaki kodu girerek e-posta adresinizi doğrulayın.</p><p><b>{0}</b></p>",
},
"zh-CN": {
"emailVerificationSubject": "验证电子邮件",
"emailVerificationBody":
"用户使用当前电子邮件注册 {2} 账户。如是本人操作,请点击以下链接完成邮箱验证\n\n{0}\n\n这个链接会在 {1} 分钟后过期.\n\n如果您没有注册用户请忽略这条消息。",
"emailVerificationBodyHtml":
'<p>用户使用当前电子邮件注册 {2} 账户。如是本人操作,请点击以下链接完成邮箱验证</p><p><a href="{0}">{0}</a></p><p>这个链接会在 {1} 分钟后过期.</p><p>如果您没有注册用户,请忽略这条消息。</p>',
"identityProviderLinkSubject": "链接 {0}",
"identityProviderLinkBody":
'有用户想要将账户 "{1}" 与用户{2}的账户"{0}" 做链接 . 如果是本人操作,请点击以下链接完成链接请求\n\n{3}\n\n这个链接会在 {4} 分钟后过期.\n\n如非本人操作请忽略这条消息。如果您链接账户您将可以通过{0}登录账户 {1}.',
"identityProviderLinkBodyHtml":
'<p>有用户想要将账户 <b>{1}</b> 与用户{2} 的账户<b>{0}</b> 做链接 . 如果是本人操作,请点击以下链接完成链接请求</p><p><a href="{3}">{3}</a></p><p>这个链接会在 {4} 分钟后过期。</p><p>如非本人操作,请忽略这条消息。如果您链接账户,您将可以通过{0}登录账户 {1}.</p>',
"passwordResetSubject": "重置密码",
"passwordResetBody":
"有用户要求修改账户 {2} 的密码.如是本人操作,请点击下面链接进行重置.\n\n{0}\n\n这个链接会在 {1} 分钟后过期.\n\n如果您不想重置您的密码请忽略这条消息密码不会改变。",
"passwordResetBodyHtml":
'<p>有用户要求修改账户 {2} 的密码如是本人操作,请点击下面链接进行重置.</p><p><a href="{0}">{0}</a></p><p>这个链接会在 {1} 分钟后过期</p><p>如果您不想重置您的密码,请忽略这条消息,密码不会改变。</p>',
"executeActionsSubject": "更新您的账户",
"executeActionsBody":
"您的管理员要求您更新账户 {2}. 点击以下链接开始更新\n\n{0}\n\n这个链接会在 {1} 分钟后失效.\n\n如果您不知道管理员要求更新账户信息请忽略这条消息。账户信息不会修改。",
"executeActionsBodyHtml":
'<p>您的管理员要求您更新账户{2}. 点击以下链接开始更新.</p><p><a href="{0}">{0}</a></p><p>这个链接会在 {1} 分钟后失效.</p><p>如果您不知道管理员要求更新账户信息,请忽略这条消息。账户信息不会修改。</p>',
"eventLoginErrorSubject": "登录错误",
"eventLoginErrorBody": "在{0} 由 {1}使用您的账户登录失败. 如果这不是您本人操作,请联系管理员.",
"eventLoginErrorBodyHtml": "<p>在{0} 由 {1}使用您的账户登录失败. 如果这不是您本人操作,请联系管理员.</p>",
"eventRemoveTotpSubject": "删除 OTP",
"eventRemoveTotpBody": "OTP在 {0} 由{1} 从您的账户中删除.如果这不是您本人操作,请联系管理员",
"eventRemoveTotpBodyHtml": "<p>OTP在 {0} 由{1} 从您的账户中删除.如果这不是您本人操作,请联系管理员。</p>",
"eventUpdatePasswordSubject": "更新密码",
"eventUpdatePasswordBody": "您的密码在{0} 由 {1}更改. 如非本人操作,请联系管理员",
"eventUpdatePasswordBodyHtml": "<p>您的密码在{0} 由 {1}更改. 如非本人操作,请联系管理员</p>",
"eventUpdateTotpSubject": "更新 OTP",
"eventUpdateTotpBody": "您账户的OTP 配置在{0} 由 {1}更改. 如非本人操作,请联系管理员。",
"eventUpdateTotpBodyHtml": "<p>您账户的OTP 配置在{0} 由 {1}更改. 如非本人操作,请联系管理员。</p>",
},
};
/* spell-checker: enable */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,278 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
export const kcMessages = {
"ca": {
"invalidPasswordHistoryMessage": "Contrasenya incorrecta: no pot ser igual a cap de les últimes {0} contrasenyes.",
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
"invalidPasswordMinLengthMessage": "Contrasenya incorrecta: longitud mínima {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres minúscules.",
"invalidPasswordMinSpecialCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} caràcters especials.",
"invalidPasswordMinUpperCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres majúscules.",
"invalidPasswordNotUsernameMessage": "Contrasenya incorrecta: no pot ser igual al nom d'usuari.",
"invalidPasswordRegexPatternMessage": "Contrasenya incorrecta: no compleix l'expressió regular.",
},
"de": {
"invalidPasswordMinLengthMessage": "Ungültiges Passwort: muss mindestens {0} Zeichen beinhalten.",
"invalidPasswordMinLowerCaseCharsMessage": "Ungültiges Passwort: muss mindestens {0} Kleinbuchstaben beinhalten.",
"invalidPasswordMinDigitsMessage": "Ungültiges Passwort: muss mindestens {0} Ziffern beinhalten.",
"invalidPasswordMinUpperCaseCharsMessage": "Ungültiges Passwort: muss mindestens {0} Großbuchstaben beinhalten.",
"invalidPasswordMinSpecialCharsMessage": "Ungültiges Passwort: muss mindestens {0} Sonderzeichen beinhalten.",
"invalidPasswordNotUsernameMessage": "Ungültiges Passwort: darf nicht identisch mit dem Benutzernamen sein.",
"invalidPasswordNotEmailMessage": "Ungültiges Passwort: darf nicht identisch mit der E-Mail-Adresse sein.",
"invalidPasswordRegexPatternMessage": "Ungültiges Passwort: stimmt nicht mit Regex-Muster überein.",
"invalidPasswordHistoryMessage": "Ungültiges Passwort: darf nicht identisch mit einem der letzten {0} Passwörter sein.",
"invalidPasswordBlacklistedMessage": "Ungültiges Passwort: Passwort ist zu bekannt und auf der schwarzen Liste.",
"invalidPasswordGenericMessage": "Ungültiges Passwort: neues Passwort erfüllt die Passwort-Anforderungen nicht.",
},
"en": {
"invalidPasswordMinLengthMessage": "Invalid password: minimum length {0}.",
"invalidPasswordMaxLengthMessage": "Invalid password: maximum length {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Invalid password: must contain at least {0} lower case characters.",
"invalidPasswordMinDigitsMessage": "Invalid password: must contain at least {0} numerical digits.",
"invalidPasswordMinUpperCaseCharsMessage": "Invalid password: must contain at least {0} upper case characters.",
"invalidPasswordMinSpecialCharsMessage": "Invalid password: must contain at least {0} special characters.",
"invalidPasswordNotUsernameMessage": "Invalid password: must not be equal to the username.",
"invalidPasswordNotEmailMessage": "Invalid password: must not be equal to the email.",
"invalidPasswordRegexPatternMessage": "Invalid password: fails to match regex pattern(s).",
"invalidPasswordHistoryMessage": "Invalid password: must not be equal to any of last {0} passwords.",
"invalidPasswordBlacklistedMessage": "Invalid password: password is blacklisted.",
"invalidPasswordGenericMessage": "Invalid password: new password does not match password policies.",
"ldapErrorInvalidCustomFilter": 'Custom configured LDAP filter does not start with "(" or does not end with ")".',
"ldapErrorConnectionTimeoutNotNumber": "Connection Timeout must be a number",
"ldapErrorReadTimeoutNotNumber": "Read Timeout must be a number",
"ldapErrorMissingClientId": "Client ID needs to be provided in config when Realm Roles Mapping is not used.",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType":
"Not possible to preserve group inheritance and use UID membership type together.",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "Can not set write only when LDAP provider mode is not WRITABLE",
"ldapErrorCantWriteOnlyAndReadOnly": "Can not set write-only and read-only together",
"ldapErrorCantEnableStartTlsAndConnectionPooling": "Can not enable both StartTLS and connection pooling.",
"ldapErrorCantEnableUnsyncedAndImportOff": "Can not disable Importing users when LDAP provider mode is UNSYNCED",
"ldapErrorMissingGroupsPathGroup": "Groups path group does not exist - please create the group on specified path first",
"clientRedirectURIsFragmentError": "Redirect URIs must not contain an URI fragment",
"clientRootURLFragmentError": "Root URL must not contain an URL fragment",
"clientRootURLIllegalSchemeError": "Root URL uses an illegal scheme",
"clientBaseURLIllegalSchemeError": "Base URL uses an illegal scheme",
"backchannelLogoutUrlIllegalSchemeError": "Backchannel logout URL uses an illegal scheme",
"clientRedirectURIsIllegalSchemeError": "A redirect URI uses an illegal scheme",
"clientBaseURLInvalid": "Base URL is not a valid URL",
"clientRootURLInvalid": "Root URL is not a valid URL",
"clientRedirectURIsInvalid": "A redirect URI is not a valid URI",
"backchannelLogoutUrlIsInvalid": "Backchannel logout URL is not a valid URL",
"pairwiseMalformedClientRedirectURI": "Client contained an invalid redirect URI.",
"pairwiseClientRedirectURIsMissingHost": "Client redirect URIs must contain a valid host component.",
"pairwiseClientRedirectURIsMultipleHosts":
"Without a configured Sector Identifier URI, client redirect URIs must not contain multiple host components.",
"pairwiseMalformedSectorIdentifierURI": "Malformed Sector Identifier URI.",
"pairwiseFailedToGetRedirectURIs": "Failed to get redirect URIs from the Sector Identifier URI.",
"pairwiseRedirectURIsMismatch": "Client redirect URIs does not match redirect URIs fetched from the Sector Identifier URI.",
"error-invalid-value": "Invalid value.",
"error-invalid-blank": "Please specify value.",
"error-empty": "Please specify value.",
"error-invalid-length": "Attribute {0} must have a length between {1} and {2}.",
"error-invalid-length-too-short": "Attribute {0} must have minimal length of {1}.",
"error-invalid-length-too-long": "Attribute {0} must have maximal length of {2}.",
"error-invalid-email": "Invalid email address.",
"error-invalid-number": "Invalid number.",
"error-number-out-of-range": "Attribute {0} must be a number between {1} and {2}.",
"error-number-out-of-range-too-small": "Attribute {0} must have minimal value of {1}.",
"error-number-out-of-range-too-big": "Attribute {0} must have maximal value of {2}.",
"error-pattern-no-match": "Invalid value.",
"error-invalid-uri": "Invalid URL.",
"error-invalid-uri-scheme": "Invalid URL scheme.",
"error-invalid-uri-fragment": "Invalid URL fragment.",
"error-user-attribute-required": "Please specify attribute {0}.",
"error-invalid-date": "Attribute {0} is invalid date.",
"error-user-attribute-read-only": "Attribute {0} is read only.",
"error-username-invalid-character": "{0} contains invalid character.",
"error-person-name-invalid-character": "{0} contains invalid character.",
},
"es": {
"invalidPasswordMinLengthMessage": "Contraseña incorrecta: longitud mínima {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras minúsculas.",
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
"invalidPasswordMinUpperCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras mayúsculas.",
"invalidPasswordMinSpecialCharsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres especiales.",
"invalidPasswordNotUsernameMessage": "Contraseña incorrecta: no puede ser igual al nombre de usuario.",
"invalidPasswordRegexPatternMessage": "Contraseña incorrecta: no cumple la expresión regular.",
"invalidPasswordHistoryMessage": "Contraseña incorrecta: no puede ser igual a ninguna de las últimas {0} contraseñas.",
},
"fr": {
"invalidPasswordMinLengthMessage": "Mot de passe invalide : longueur minimale requise de {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Mot de passe invalide : doit contenir au moins {0} lettre(s) en minuscule.",
"invalidPasswordMinDigitsMessage": "Mot de passe invalide : doit contenir au moins {0} chiffre(s).",
"invalidPasswordMinUpperCaseCharsMessage": "Mot de passe invalide : doit contenir au moins {0} lettre(s) en majuscule.",
"invalidPasswordMinSpecialCharsMessage": "Mot de passe invalide : doit contenir au moins {0} caractère(s) spéciaux.",
"invalidPasswordNotUsernameMessage": "Mot de passe invalide : ne doit pas être identique au nom d'utilisateur.",
"invalidPasswordRegexPatternMessage": "Mot de passe invalide : ne valide pas l'expression rationnelle.",
"invalidPasswordHistoryMessage": "Mot de passe invalide : ne doit pas être égal aux {0} derniers mot de passe.",
},
"it": {},
"ja": {
"invalidPasswordMinLengthMessage": "無効なパスワード: 最小{0}の長さが必要です。",
"invalidPasswordMinLowerCaseCharsMessage": "無効なパスワード: 少なくとも{0}文字の小文字を含む必要があります。",
"invalidPasswordMinDigitsMessage": "無効なパスワード: 少なくとも{0}文字の数字を含む必要があります。",
"invalidPasswordMinUpperCaseCharsMessage": "無効なパスワード: 少なくとも{0}文字の大文字を含む必要があります。",
"invalidPasswordMinSpecialCharsMessage": "無効なパスワード: 少なくとも{0}文字の特殊文字を含む必要があります。",
"invalidPasswordNotUsernameMessage": "無効なパスワード: ユーザー名と同じパスワードは禁止されています。",
"invalidPasswordRegexPatternMessage": "無効なパスワード: 正規表現パターンと一致しません。",
"invalidPasswordHistoryMessage": "無効なパスワード: 最近の{0}パスワードのいずれかと同じパスワードは禁止されています。",
"invalidPasswordBlacklistedMessage": "無効なパスワード: パスワードがブラックリストに含まれています。",
"invalidPasswordGenericMessage": "無効なパスワード: 新しいパスワードはパスワード・ポリシーと一致しません。",
"ldapErrorInvalidCustomFilter": "LDAPフィルターのカスタム設定が、「(」から開始または「)」で終了となっていません。",
"ldapErrorConnectionTimeoutNotNumber": "接続タイムアウトは数字でなければなりません",
"ldapErrorReadTimeoutNotNumber": "読み取りタイムアウトは数字でなければなりません",
"ldapErrorMissingClientId": "レルムロール・マッピングを使用しない場合は、クライアントIDは設定内で提供される必要があります。",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType":
"グループの継承を維持することと、UIDメンバーシップ・タイプを使用することは同時にできません。",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "LDAPプロバイダー・モードがWRITABLEではない場合は、write onlyを設定することはできません。",
"ldapErrorCantWriteOnlyAndReadOnly": "write-onlyとread-onlyを一緒に設定することはできません。",
"ldapErrorCantEnableStartTlsAndConnectionPooling": "StartTLSと接続プーリングの両方を有効にできません。",
"clientRedirectURIsFragmentError": "リダイレクトURIにURIフラグメントを含めることはできません。",
"clientRootURLFragmentError": "ルートURLにURLフラグメントを含めることはできません。",
"pairwiseMalformedClientRedirectURI": "クライアントに無効なリダイレクトURIが含まれていました。",
"pairwiseClientRedirectURIsMissingHost": "クライアントのリダイレクトURIには有効なホスト・コンポーネントが含まれている必要があります。",
"pairwiseClientRedirectURIsMultipleHosts":
"設定されたセレクター識別子URIがない場合は、クライアントのリダイレクトURIは複数のホスト・コンポーネントを含むことはできません。",
"pairwiseMalformedSectorIdentifierURI": "不正なセレクター識別子URIです。",
"pairwiseFailedToGetRedirectURIs": "セクター識別子URIからリダイレクトURIを取得できませんでした。",
"pairwiseRedirectURIsMismatch": "クライアントのリダイレクトURIは、セクター識別子URIからフェッチされたリダイレクトURIと一致しません。",
},
"lt": {
"invalidPasswordMinLengthMessage": "Per trumpas slaptažodis: mažiausias ilgis {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} mažąją raidę.",
"invalidPasswordMinDigitsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} skaitmenį.",
"invalidPasswordMinUpperCaseCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} didžiąją raidę.",
"invalidPasswordMinSpecialCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} specialų simbolį.",
"invalidPasswordNotUsernameMessage": "Neteisingas slaptažodis: slaptažodis negali sutapti su naudotojo vardu.",
"invalidPasswordRegexPatternMessage": "Neteisingas slaptažodis: slaptažodis netenkina regex taisyklės(ių).",
"invalidPasswordHistoryMessage": "Neteisingas slaptažodis: slaptažodis negali sutapti su prieš tai buvusiais {0} slaptažodžiais.",
"ldapErrorInvalidCustomFilter": 'Sukonfigūruotas LDAP filtras neprasideda "(" ir nesibaigia ")" simboliais.',
"ldapErrorMissingClientId": "Privaloma nurodyti kliento ID kai srities rolių susiejimas nėra nenaudojamas.",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType": "Grupių paveldėjimo ir UID narystės tipas kartu negali būti naudojami.",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "Negalima nustatyti rašymo rėžimo kuomet LDAP teikėjo rėžimas ne WRITABLE",
"ldapErrorCantWriteOnlyAndReadOnly": "Negalima nustatyti tik rašyti ir tik skaityti kartu",
"clientRedirectURIsFragmentError": "Nurodykite URI fragmentą, kurio negali būti peradresuojamuose URI adresuose",
"clientRootURLFragmentError": "Nurodykite URL fragmentą, kurio negali būti šakniniame URL adrese",
"pairwiseMalformedClientRedirectURI": "Klientas pateikė neteisingą nukreipimo nuorodą.",
"pairwiseClientRedirectURIsMissingHost": "Kliento nukreipimo nuorodos privalo būti nurodytos su serverio vardo komponentu.",
"pairwiseClientRedirectURIsMultipleHosts":
"Kuomet nesukonfigūruotas sektoriaus identifikatoriaus URL, kliento nukreipimo nuorodos privalo talpinti ne daugiau kaip vieną skirtingą serverio vardo komponentą.",
"pairwiseMalformedSectorIdentifierURI": "Neteisinga sektoriaus identifikatoriaus URI.",
"pairwiseFailedToGetRedirectURIs": "Nepavyko gauti nukreipimo nuorodų iš sektoriaus identifikatoriaus URI.",
"pairwiseRedirectURIsMismatch": "Kliento nukreipimo nuoroda neatitinka nukreipimo nuorodų iš sektoriaus identifikatoriaus URI.",
},
"nl": {
"invalidPasswordMinLengthMessage": "Ongeldig wachtwoord: de minimale lengte is {0} karakters.",
"invalidPasswordMinLowerCaseCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} kleine letters bevatten.",
"invalidPasswordMinDigitsMessage": "Ongeldig wachtwoord: het moet minstens {0} getallen bevatten.",
"invalidPasswordMinUpperCaseCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} hoofdletters bevatten.",
"invalidPasswordMinSpecialCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} speciale karakters bevatten.",
"invalidPasswordNotUsernameMessage": "Ongeldig wachtwoord: het mag niet overeenkomen met de gebruikersnaam.",
"invalidPasswordRegexPatternMessage": "Ongeldig wachtwoord: het voldoet niet aan het door de beheerder ingestelde patroon.",
"invalidPasswordHistoryMessage": "Ongeldig wachtwoord: het mag niet overeen komen met een van de laatste {0} wachtwoorden.",
"invalidPasswordGenericMessage": "Ongeldig wachtwoord: het nieuwe wachtwoord voldoet niet aan het wachtwoordbeleid.",
"ldapErrorInvalidCustomFilter": 'LDAP filter met aangepaste configuratie start niet met "(" of eindigt niet met ")".',
"ldapErrorConnectionTimeoutNotNumber": "Verbindingstimeout moet een getal zijn",
"ldapErrorReadTimeoutNotNumber": "Lees-timeout moet een getal zijn",
"ldapErrorMissingClientId": "Client ID moet ingesteld zijn als Realm Roles Mapping niet gebruikt wordt.",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType": "Kan groepsovererving niet behouden bij UID-lidmaatschapstype.",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "Alleen-schrijven niet mogelijk als LDAP provider mode niet WRITABLE is",
"ldapErrorCantWriteOnlyAndReadOnly": "Alleen-schrijven en alleen-lezen mogen niet tegelijk ingesteld zijn",
"clientRedirectURIsFragmentError": "Redirect URIs mogen geen URI fragment bevatten",
"clientRootURLFragmentError": "Root URL mag geen URL fragment bevatten",
"pairwiseMalformedClientRedirectURI": "Client heeft een ongeldige redirect URI.",
"pairwiseClientRedirectURIsMissingHost": "Client redirect URIs moeten een geldige host-component bevatten.",
"pairwiseClientRedirectURIsMultipleHosts":
"Zonder een geconfigureerde Sector Identifier URI mogen client redirect URIs niet meerdere host componenten hebben.",
"pairwiseMalformedSectorIdentifierURI": "Onjuist notatie in Sector Identifier URI.",
"pairwiseFailedToGetRedirectURIs": "Kon geen redirect URIs verkrijgen van de Sector Identifier URI.",
"pairwiseRedirectURIsMismatch": "Client redirect URIs komen niet overeen met redict URIs ontvangen van de Sector Identifier URI.",
},
"no": {
"invalidPasswordMinLengthMessage": "Ugyldig passord: minimum lengde {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Ugyldig passord: må inneholde minst {0} små bokstaver.",
"invalidPasswordMinDigitsMessage": "Ugyldig passord: må inneholde minst {0} sifre.",
"invalidPasswordMinUpperCaseCharsMessage": "Ugyldig passord: må inneholde minst {0} store bokstaver.",
"invalidPasswordMinSpecialCharsMessage": "Ugyldig passord: må inneholde minst {0} spesialtegn.",
"invalidPasswordNotUsernameMessage": "Ugyldig passord: kan ikke være likt brukernavn.",
"invalidPasswordRegexPatternMessage": "Ugyldig passord: tilfredsstiller ikke kravene for passord-mønster.",
"invalidPasswordHistoryMessage": "Ugyldig passord: kan ikke være likt noen av de {0} foregående passordene.",
"ldapErrorInvalidCustomFilter": 'Tilpasset konfigurasjon av LDAP-filter starter ikke med "(" eller slutter ikke med ")".',
"ldapErrorMissingClientId": "KlientID må være tilgjengelig i config når sikkerhetsdomenerollemapping ikke brukes.",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType": "Ikke mulig å bevare gruppearv og samtidig bruke UID medlemskapstype.",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "Kan ikke sette write-only når LDAP leverandør-modus ikke er WRITABLE",
"ldapErrorCantWriteOnlyAndReadOnly": "Kan ikke sette både write-only og read-only",
},
"pl": {},
"pt-BR": {
"invalidPasswordMinLengthMessage": "Senha inválida: deve conter ao menos {0} caracteres.",
"invalidPasswordMinLowerCaseCharsMessage": "Senha inválida: deve conter ao menos {0} caracteres minúsculos.",
"invalidPasswordMinDigitsMessage": "Senha inválida: deve conter ao menos {0} digitos numéricos.",
"invalidPasswordMinUpperCaseCharsMessage": "Senha inválida: deve conter ao menos {0} caracteres maiúsculos.",
"invalidPasswordMinSpecialCharsMessage": "Senha inválida: deve conter ao menos {0} caracteres especiais.",
"invalidPasswordNotUsernameMessage": "Senha inválida: não deve ser igual ao nome de usuário.",
"invalidPasswordRegexPatternMessage": "Senha inválida: falha ao passar por padrões.",
"invalidPasswordHistoryMessage": "Senha inválida: não deve ser igual às últimas {0} senhas.",
"ldapErrorInvalidCustomFilter": 'Filtro LDAP não inicia com "(" ou não termina com ")".',
"ldapErrorMissingClientId": "ID do cliente precisa ser definido na configuração quando mapeamentos de Roles do Realm não é utilizado.",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType":
"Não é possível preservar herança de grupos e usar tipo de associação de UID ao mesmo tempo.",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "Não é possível definir modo de somente escrita quando o provedor LDAP não suporta escrita",
"ldapErrorCantWriteOnlyAndReadOnly": "Não é possível definir somente escrita e somente leitura ao mesmo tempo",
"clientRedirectURIsFragmentError": "URIs de redirecionamento não podem conter fragmentos",
"clientRootURLFragmentError": "URL raiz não pode conter fragmentos",
},
"ru": {
"invalidPasswordMinLengthMessage": "Некорректный пароль: длина пароля должна быть не менее {0} символов(а).",
"invalidPasswordMinDigitsMessage": "Некорректный пароль: должен содержать не менее {0} цифр(ы).",
"invalidPasswordMinLowerCaseCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} символов(а) в нижнем регистре.",
"invalidPasswordMinUpperCaseCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} символов(а) в верхнем регистре.",
"invalidPasswordMinSpecialCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} спецсимволов(а).",
"invalidPasswordNotUsernameMessage": "Некорректный пароль: пароль не должен совпадать с именем пользователя.",
"invalidPasswordRegexPatternMessage": "Некорректный пароль: пароль не прошел проверку по регулярному выражению.",
"invalidPasswordHistoryMessage": "Некорректный пароль: пароль не должен совпадать с последним(и) {0} паролем(ями).",
"invalidPasswordGenericMessage": "Некорректный пароль: новый пароль не соответствует правилам пароля.",
"ldapErrorInvalidCustomFilter": 'Сконфигурированный пользователем фильтр LDAP не должен начинаться с "(" или заканчиваться на ")".',
"ldapErrorMissingClientId": "Client ID должен быть настроен в конфигурации, если не используется сопоставление ролей в realm.",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType": "Не удалось унаследовать группу и использовать членство UID типа вместе.",
"ldapErrorCantWriteOnlyForReadOnlyLdap": 'Невозможно установить режим "только на запись", когда LDAP провайдер не в режиме WRITABLE',
"ldapErrorCantWriteOnlyAndReadOnly": 'Невозможно одновременно установить режимы "только на чтение" и "только на запись"',
"clientRedirectURIsFragmentError": "URI перенаправления не должен содержать фрагмент URI",
"clientRootURLFragmentError": "Корневой URL не должен содержать фрагмент URL ",
"pairwiseMalformedClientRedirectURI": "Клиент содержит некорректный URI перенаправления.",
"pairwiseClientRedirectURIsMissingHost": "URI перенаправления клиента должен содержать корректный компонент хоста.",
"pairwiseClientRedirectURIsMultipleHosts":
"Без конфигурации по части идентификатора URI, URI перенаправления клиента не может содержать несколько компонентов хоста.",
"pairwiseMalformedSectorIdentifierURI": "Искаженная часть идентификатора URI.",
"pairwiseFailedToGetRedirectURIs": "Не удалось получить идентификаторы URI перенаправления из части идентификатора URI.",
"pairwiseRedirectURIsMismatch": "Клиент URI переадресации не соответствует URI переадресации, полученной из части идентификатора URI.",
},
"zh-CN": {
"invalidPasswordMinLengthMessage": "无效的密码:最短长度 {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "无效的密码:至少包含 {0} 小写字母",
"invalidPasswordMinDigitsMessage": "无效的密码:至少包含 {0} 个数字",
"invalidPasswordMinUpperCaseCharsMessage": "无效的密码:最短长度 {0} 大写字母",
"invalidPasswordMinSpecialCharsMessage": "无效的密码:最短长度 {0} 特殊字符",
"invalidPasswordNotUsernameMessage": "无效的密码: 不可以与用户名相同",
"invalidPasswordRegexPatternMessage": "无效的密码: 无法与正则表达式匹配",
"invalidPasswordHistoryMessage": "无效的密码:不能与最后使用的 {0} 个密码相同",
"ldapErrorInvalidCustomFilter": '定制的 LDAP过滤器不是以 "(" 开头或以 ")"结尾.',
"ldapErrorConnectionTimeoutNotNumber": "Connection Timeout 必须是个数字",
"ldapErrorMissingClientId": "当域角色映射未启用时,客户端 ID 需要指定。",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType": "无法在使用UID成员类型的同时维护组继承属性。",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "当LDAP提供方不是可写模式时无法设置只写",
"ldapErrorCantWriteOnlyAndReadOnly": "无法同时设置只读和只写",
"clientRedirectURIsFragmentError": "重定向URL不应包含URI片段",
"clientRootURLFragmentError": "根URL 不应包含 URL 片段",
"pairwiseMalformedClientRedirectURI": "客户端包含一个无效的重定向URL",
"pairwiseClientRedirectURIsMissingHost": "客户端重定向URL需要有一个有效的主机",
"pairwiseClientRedirectURIsMultipleHosts":
"Without a configured Sector Identifier URI, client redirect URIs must not contain multiple host components.",
"pairwiseMalformedSectorIdentifierURI": "Malformed Sector Identifier URI.",
"pairwiseFailedToGetRedirectURIs": "无法从服务器获得重定向URL",
"pairwiseRedirectURIsMismatch": "客户端的重定向URI与服务器端获取的URI配置不匹配。",
},
};
/* spell-checker: enable */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,137 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Desa",
"doCancel": "Cancel·la",
"doLogOutAllSessions": "Desconnecta de totes les sessions",
"doRemove": "Elimina",
"doAdd": "Afegeix",
"doSignOut": "Desconnectar",
"editAccountHtmlTitle": "Edita compte",
"federatedIdentitiesHtmlTitle": "Identitats federades",
"accountLogHtmlTitle": "Registre del compte",
"changePasswordHtmlTitle": "Canvia contrasenya",
"sessionsHtmlTitle": "Sessions",
"accountManagementTitle": "Gestió de Compte Keycloak",
"authenticatorTitle": "Autenticador",
"applicationsHtmlTitle": "Aplicacions",
"authenticatorCode": "Codi d'un sol ús",
"email": "Email",
"firstName": "Nom",
"givenName": "Nom de pila",
"fullName": "Nom complet",
"lastName": "Cognoms",
"familyName": "Cognom",
"password": "Contrasenya",
"passwordConfirm": "Confirma la contrasenya",
"passwordNew": "Nova contrasenya",
"username": "Usuari",
"address": "Adreça",
"street": "Carrer",
"locality": "Ciutat o Municipi",
"region": "Estat, Província, o Regió",
"postal_code": "Postal code",
"country": "País",
"emailVerified": "Email verificat",
"gssDelegationCredential": "GSS Delegation Credential",
"role_admin": "Administrador",
"role_realm-admin": "Administrador del domini",
"role_create-realm": "Crear domini",
"role_view-realm": "Veure domini",
"role_view-users": "Veure usuaris",
"role_view-applications": "Veure aplicacions",
"role_view-clients": "Veure clients",
"role_view-events": "Veure events",
"role_view-identity-providers": "Veure proveïdors d'identitat",
"role_manage-realm": "Gestionar domini",
"role_manage-users": "Gestinar usuaris",
"role_manage-applications": "Gestionar aplicacions",
"role_manage-identity-providers": "Gestionar proveïdors d'identitat",
"role_manage-clients": "Gestionar clients",
"role_manage-events": "Gestionar events",
"role_view-profile": "Veure perfil",
"role_manage-account": "Gestionar compte",
"role_read-token": "Llegir token",
"role_offline-access": "Accés sense connexió",
"client_account": "Compte",
"client_security-admin-console": "Consola d'Administració de Seguretat",
"client_realm-management": "Gestió de domini",
"client_broker": "Broker",
"requiredFields": "Camps obligatoris",
"allFieldsRequired": "Tots els camps obligatoris",
"backToApplication": "&laquo; Torna a l'aplicació",
"backTo": "Torna a {0}",
"date": "Data",
"event": "Event",
"ip": "IP",
"client": "Client",
"clients": "Clients",
"details": "Detalls",
"started": "Iniciat",
"lastAccess": "Últim accés",
"expires": "Expira",
"applications": "Aplicacions",
"account": "Compte",
"federatedIdentity": "Identitat federada",
"authenticator": "Autenticador",
"sessions": "Sessions",
"log": "Registre",
"application": "Aplicació",
"availablePermissions": "Permisos disponibles",
"grantedPermissions": "Permisos concedits",
"grantedPersonalInfo": "Informació personal concedida",
"additionalGrants": "Permisos addicionals",
"action": "Acció",
"inResource": "a",
"fullAccess": "Accés total",
"offlineToken": "Codi d'autorització offline",
"revoke": "Revocar permís",
"configureAuthenticators": "Autenticadors configurats",
"mobile": "Mòbil",
"totpStep1":
'Instal·la <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> o Google Authenticator al teu telèfon mòbil. Les dues aplicacions estan disponibles a <a href="https://play.google.com">Google Play</a> i en l\'App Store d\'Apple.',
"totpStep2": "Obre l'aplicació i escaneja el codi o introdueix la clau.",
"totpStep3": "Introdueix el codi únic que et mostra l'aplicació d'autenticació i fes clic a Envia per finalitzar la configuració",
"missingUsernameMessage": "Si us plau indica el teu usuari.",
"missingFirstNameMessage": "Si us plau indica el nom.",
"invalidEmailMessage": "Email no vàlid",
"missingLastNameMessage": "Si us plau indica els teus cognoms.",
"missingEmailMessage": "Si us plau indica l'email.",
"missingPasswordMessage": "Si us plau indica la contrasenya.",
"notMatchPasswordMessage": "Les contrasenyes no coincideixen.",
"missingTotpMessage": "Si us plau indica el teu codi d'autenticació",
"invalidPasswordExistingMessage": "La contrasenya actual no és correcta.",
"invalidPasswordConfirmMessage": "La confirmació de contrasenya no coincideix.",
"invalidTotpMessage": "El código de autenticación no es válido.",
"usernameExistsMessage": "L'usuari ja existeix",
"emailExistsMessage": "L'email ja existeix",
"readOnlyUserMessage": "No pots actualitzar el teu usuari perquè el teu compte és de només lectura.",
"readOnlyPasswordMessage": "No pots actualitzar la contrasenya perquè el teu compte és de només lectura.",
"successTotpMessage": "Aplicació d'autenticació mòbil configurada.",
"successTotpRemovedMessage": "Aplicació d'autenticació mòbil eliminada.",
"successGrantRevokedMessage": "Permís revocat correctament",
"accountUpdatedMessage": "El teu compte s'ha actualitzat.",
"accountPasswordUpdatedMessage": "La contrasenya s'ha actualitzat.",
"missingIdentityProviderMessage": "Proveïdor d'identitat no indicat.",
"invalidFederatedIdentityActionMessage": "Acció no vàlida o no indicada.",
"identityProviderNotFoundMessage": "No s'ha trobat un proveïdor d'identitat.",
"federatedIdentityLinkNotActiveMessage": "Aquesta identitat ja no està activa",
"federatedIdentityRemovingLastProviderMessage": "No pots eliminar l'última identitat federada perquè no tens fixada una contrasenya.",
"identityProviderRedirectErrorMessage": "Error en la redirecció al proveïdor d'identitat",
"identityProviderRemovedMessage": "Proveïdor d'identitat esborrat correctament.",
"accountDisabledMessage": "El compte està desactivada, contacteu amb l'administrador.",
"accountTemporarilyDisabledMessage": "El compte està temporalment desactivat, contacta amb l'administrador o intenta-ho de nou més tard.",
"invalidPasswordMinLengthMessage": "Contrasenya incorrecta: longitud mínima {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres minúscules.",
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
"invalidPasswordMinUpperCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres majúscules.",
"invalidPasswordMinSpecialCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} caràcters especials.",
"invalidPasswordNotUsernameMessage": "Contrasenya incorrecta: no pot ser igual al nom d'usuari.",
"invalidPasswordRegexPatternMessage": "Contrasenya incorrecta: no compleix l'expressió regular.",
"invalidPasswordHistoryMessage": "Contrasenya incorrecta: no pot ser igual a cap de les últimes {0} contrasenyes."
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,156 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Uložit",
"doCancel": "Zrušit",
"doLogOutAllSessions": "Odhlásit všechny relace",
"doRemove": "Odstranit",
"doAdd": "Přidat",
"doSignOut": "Odhlásit se",
"editAccountHtmlTitle": "Upravit účet",
"federatedIdentitiesHtmlTitle": "Propojené identity",
"accountLogHtmlTitle": "Log účtu",
"changePasswordHtmlTitle": "Změnit heslo",
"sessionsHtmlTitle": "Relace",
"accountManagementTitle": "Správa účtů Keycloak",
"authenticatorTitle": "Autentizátor",
"applicationsHtmlTitle": "Aplikace",
"authenticatorCode": "Jednorázový kód",
"email": "E-mail",
"firstName": "První křestní jméno",
"givenName": "Křestní jména",
"fullName": "Celé jméno",
"lastName": "Příjmení",
"familyName": "Rodinné jméno",
"password": "Heslo",
"passwordConfirm": "Nové heslo (znovu)",
"passwordNew": "Nové heslo",
"username": "Uživatelské jméno",
"address": "Adresa",
"street": "Ulice",
"locality": "Město nebo lokalita",
"region": "Kraj",
"postal_code": "PSČ",
"country": "Stát",
"emailVerified": "E-mail ověřen",
"gssDelegationCredential": "GSS delegované oprávnění",
"role_admin": "Správce",
"role_realm-admin": "Správce realmu",
"role_create-realm": "Vytvořit realm",
"role_view-realm": "Zobrazit realm",
"role_view-users": "Zobrazit uživatele",
"role_view-applications": "Zobrazit aplikace",
"role_view-clients": "Zobrazit klienty",
"role_view-events": "Zobrazit události",
"role_view-identity-providers": "Zobrazit poskytovatele identity",
"role_manage-realm": "Spravovat realm",
"role_manage-users": "Spravovat uživatele",
"role_manage-applications": "Spravovat aplikace",
"role_manage-identity-providers": "Spravovat poskytovatele identity",
"role_manage-clients": "Spravovat klienty",
"role_manage-events": "Spravovat události",
"role_view-profile": "Zobrazit profil",
"role_manage-account": "Spravovat účet",
"role_manage-account-links": "Spravovat odkazy na účet",
"role_read-token": "Číst token",
"role_offline-access": "Přístup offline",
"role_uma_authorization": "Získání oprávnění",
"client_account": "Účet",
"client_security-admin-console": "Administrátorská bezpečnostní konzole",
"client_admin-cli": "Administrátorské CLI",
"client_realm-management": "Správa realmů",
"client_broker": "Broker",
"requiredFields": "Požadovaná pole",
"allFieldsRequired": "Všechna pole vyžadovaná",
"backToApplication": "&laquo; Zpět na aplikaci",
"backTo": "Zpět na {0}",
"date": "Datum",
"event": "Událost",
"ip": "IP",
"client": "Klient",
"clients": "Klienti",
"details": "Podrobnosti",
"started": "Zahájeno",
"lastAccess": "Poslední přístup",
"expires": "Vyprší",
"applications": "Aplikace",
"account": "Účet",
"federatedIdentity": "Propojená identita",
"authenticator": "Autentizátor",
"sessions": "Relace",
"log": "Log",
"application": "Aplikace",
"availablePermissions": "Dostupná oprávnění",
"grantedPermissions": "Udělené oprávnění",
"grantedPersonalInfo": "Poskytnuté osobní informace",
"additionalGrants": "Dodatečné oprávnění",
"action": "Akce",
"inResource": "v",
"fullAccess": "Úplný přístup",
"offlineToken": "Offline Token",
"revoke": "Zrušit oprávnění",
"configureAuthenticators": "Konfigurované autentizátory",
"mobile": "Mobilní",
"totpStep1": "Nainstalujte jednu z následujících aplikací",
"totpStep2": "Otevřete aplikaci a naskenujte čárový kód",
"totpStep3": "Zadejte jednorázový kód poskytnutý aplikací a klepnutím na tlačítko Uložit dokončete nastavení.",
"totpManualStep2": "Otevřete aplikaci a zadejte klíč",
"totpManualStep3": "Použijte následující hodnoty konfigurace, pokud aplikace umožňuje jejich nastavení",
"totpUnableToScan": "Nelze skenovat?",
"totpScanBarcode": "Skenovat čárový kód?",
"totp.totp": "Založeno na čase",
"totp.hotp": "Založeno na čítači",
"totpType": "Typ",
"totpAlgorithm": "Algoritmus",
"totpDigits": "Číslice",
"totpInterval": "Interval",
"totpCounter": "Čítač",
"missingUsernameMessage": "Zadejte uživatelské jméno.",
"missingFirstNameMessage": "Zadejte prosím křestní jméno.",
"invalidEmailMessage": "Neplatná e-mailová adresa.",
"missingLastNameMessage": "Zadejte prosím příjmení.",
"missingEmailMessage": "Zadejte prosím e-mail.",
"missingPasswordMessage": "Zadejte prosím heslo.",
"notMatchPasswordMessage": "Hesla se neshodují.",
"missingTotpMessage": "Zadejte prosím kód autentizátoru.",
"invalidPasswordExistingMessage": "Neplatné stávající heslo.",
"invalidPasswordConfirmMessage": "Nová hesla se neshodují.",
"invalidTotpMessage": "Neplatný kód autentizátoru.",
"usernameExistsMessage": "Uživatelské jméno již existuje.",
"emailExistsMessage": "E-mail již existuje.",
"readOnlyUserMessage": "Nemůžete svůj účet aktualizovat, protože je pouze pro čtení.",
"readOnlyUsernameMessage": "Nemůžete aktualizovat své uživatelské jméno, protože je pouze pro čtení.",
"readOnlyPasswordMessage": "Nemůžete aktualizovat své heslo, protože váš účet je jen pro čtení.",
"successTotpMessage": "Ověření pomocí OTP úspěšně konfigurováno.",
"successTotpRemovedMessage": "Ověření pomocí OTP úspěšně odstraněno.",
"successGrantRevokedMessage": "Oprávnění bylo úspěšně zrušeno.",
"accountUpdatedMessage": "Váš účet byl aktualizován.",
"accountPasswordUpdatedMessage": "Vaše heslo bylo aktualizováno.",
"missingIdentityProviderMessage": "Chybějící poskytovatel identity.",
"invalidFederatedIdentityActionMessage": "Neplatná nebo chybějící akce.",
"identityProviderNotFoundMessage": "Poskytovatel identity nenalezen.",
"federatedIdentityLinkNotActiveMessage": "Tato identita již není aktivní.",
"federatedIdentityRemovingLastProviderMessage": "Nemůžete odstranit poslední propojenou identitu, protože nemáte heslo.",
"identityProviderRedirectErrorMessage": "Nepodařilo se přesměrovat na poskytovatele identity.",
"identityProviderRemovedMessage": "Poskytovatel identity byl úspěšně odstraněn.",
"identityProviderAlreadyLinkedMessage": "Propojená identita vrácená uživatelem {0} je již propojena s jiným uživatelem.",
"staleCodeAccountMessage": "Platnost vypršela. Zkuste to ještě jednou.",
"consentDenied": "Souhlas byl zamítnut.",
"accountDisabledMessage": "Účet je zakázán, kontaktujte správce.",
"accountTemporarilyDisabledMessage": "Účet je dočasně zakázán, kontaktujte správce nebo zkuste to později.",
"invalidPasswordMinLengthMessage": "Neplatné heslo: musí obsahovat minimálně {0} malých znaků.",
"invalidPasswordMinLowerCaseCharsMessage": "Neplatné heslo: musí obsahovat minimálně {0} malé znaky.",
"invalidPasswordMinDigitsMessage": "Neplatné heslo: musí obsahovat nejméně {0} číslic.",
"invalidPasswordMinUpperCaseCharsMessage": "Neplatné heslo: musí obsahovat nejméně {0} velkých písmenen.",
"invalidPasswordMinSpecialCharsMessage": "Neplatné heslo: musí obsahovat nejméně {0} speciálních znaků.",
"invalidPasswordNotUsernameMessage": "Neplatné heslo: nesmí být totožné s uživatelským jménem.",
"invalidPasswordRegexPatternMessage": "Neplatné heslo: neshoduje se zadaným regulárním výrazem.",
"invalidPasswordHistoryMessage": "Neplatné heslo: Nesmí se opakovat žádné z posledních {0} hesel.",
"invalidPasswordBlacklistedMessage": "Neplatné heslo: heslo je na černé listině.",
"invalidPasswordGenericMessage": "Neplatné heslo: nové heslo neodpovídá pravidlům hesla."
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,156 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Speichern",
"doCancel": "Abbrechen",
"doLogOutAllSessions": "Alle Sitzungen abmelden",
"doRemove": "Entfernen",
"doAdd": "Hinzufügen",
"doSignOut": "Abmelden",
"editAccountHtmlTitle": "Benutzerkonto bearbeiten",
"federatedIdentitiesHtmlTitle": "Föderierte Identitäten",
"accountLogHtmlTitle": "Benutzerkonto Log",
"changePasswordHtmlTitle": "Passwort Ändern",
"sessionsHtmlTitle": "Sitzungen",
"accountManagementTitle": "Keycloak Benutzerkontoverwaltung",
"authenticatorTitle": "Mehrfachauthentifizierung",
"applicationsHtmlTitle": "Applikationen",
"authenticatorCode": "One-time Code",
"email": "E-Mail",
"firstName": "Vorname",
"givenName": "Vorname",
"fullName": "Voller Name",
"lastName": "Nachname",
"familyName": "Nachname",
"password": "Passwort",
"passwordConfirm": "Passwort bestätigen",
"passwordNew": "Neues Passwort",
"username": "Benutzername",
"address": "Adresse",
"street": "Straße",
"region": "Staat, Provinz, Region",
"postal_code": "PLZ",
"locality": "Stadt oder Ortschaft",
"country": "Land",
"emailVerified": "E-Mail verifiziert",
"gssDelegationCredential": "GSS delegierte Berechtigung",
"role_admin": "Admin",
"role_realm-admin": "Realm Admin",
"role_create-realm": "Realm erstellen",
"role_view-realm": "Realm ansehen",
"role_view-users": "Benutzer ansehen",
"role_view-applications": "Applikationen ansehen",
"role_view-clients": "Clients ansehen",
"role_view-events": "Events ansehen",
"role_view-identity-providers": "Identity Provider ansehen",
"role_manage-realm": "Realm verwalten",
"role_manage-users": "Benutzer verwalten",
"role_manage-applications": "Applikationen verwalten",
"role_manage-identity-providers": "Identity Provider verwalten",
"role_manage-clients": "Clients verwalten",
"role_manage-events": "Events verwalten",
"role_view-profile": "Profile ansehen",
"role_manage-account": "Profile verwalten",
"role_manage-account-links": "Profil-Links verwalten",
"role_read-token": "Token lesen",
"role_offline-access": "Offline-Zugriff",
"role_uma_authorization": "Berechtigungen einholen",
"client_account": "Clientkonto",
"client_security-admin-console": "Security Adminkonsole",
"client_realm-management": "Realm-Management",
"client_broker": "Broker",
"requiredFields": "Erforderliche Felder",
"allFieldsRequired": "Alle Felder sind erforderlich",
"backToApplication": "&laquo; Zurück zur Applikation",
"backTo": "Zurück zu {0}",
"date": "Datum",
"event": "Ereignis",
"ip": "IP",
"client": "Client",
"clients": "Clients",
"details": "Details",
"started": "Startdatum",
"lastAccess": "Letzter Zugriff",
"expires": "Ablaufdatum",
"applications": "Applikationen",
"account": "Benutzerkonto",
"federatedIdentity": "Föderierte Identität",
"authenticator": "Mehrfachauthentifizierung",
"sessions": "Sitzungen",
"log": "Log",
"application": "Applikation",
"availablePermissions": "verfügbare Berechtigungen",
"grantedPermissions": "gewährte Berechtigungen",
"grantedPersonalInfo": "gewährte persönliche Informationen",
"additionalGrants": "zusätzliche Berechtigungen",
"action": "Aktion",
"inResource": "in",
"fullAccess": "Vollzugriff",
"offlineToken": "Offline-Token",
"revoke": "Berechtigung widerrufen",
"configureAuthenticators": "Mehrfachauthentifizierung konfigurieren",
"mobile": "Mobil",
"totpStep1": "Installieren Sie eine der folgenden Applikationen auf Ihrem Smartphone:",
"totpStep2": "Öffnen Sie die Applikation und scannen Sie den Barcode.",
"totpStep3": "Geben Sie den von der Applikation generierten One-time Code ein und klicken Sie auf Speichern.",
"totpManualStep2": "Öffnen Sie die Applikation und geben Sie den folgenden Schlüssel ein.",
"totpManualStep3": "Verwenden Sie die folgenden Konfigurationswerte, falls Sie diese für die Applikation anpassen können:",
"totpUnableToScan": "Sie können den Barcode nicht scannen?",
"totpScanBarcode": "Barcode scannen?",
"totp.totp": "zeitbasiert (time-based)",
"totp.hotp": "zählerbasiert (counter-based)",
"totpType": "Typ",
"totpAlgorithm": "Algorithmus",
"totpDigits": "Ziffern",
"totpInterval": "Intervall",
"totpCounter": "Zähler",
"missingUsernameMessage": "Bitte geben Sie einen Benutzernamen ein.",
"missingFirstNameMessage": "Bitte geben Sie einen Vornamen ein.",
"invalidEmailMessage": "Ungültige E-Mail Adresse.",
"missingLastNameMessage": "Bitte geben Sie einen Nachnamen ein.",
"missingEmailMessage": "Bitte geben Sie eine E-Mail Adresse ein.",
"missingPasswordMessage": "Bitte geben Sie ein Passwort ein.",
"notMatchPasswordMessage": "Die Passwörter sind nicht identisch.",
"missingTotpMessage": "Bitte geben Sie den One-time Code ein.",
"invalidPasswordExistingMessage": "Das aktuelle Passwort ist ungültig.",
"invalidPasswordConfirmMessage": "Die Passwortbestätigung ist nicht identisch.",
"invalidTotpMessage": "Ungültiger One-time Code.",
"usernameExistsMessage": "Der Benutzername existiert bereits.",
"emailExistsMessage": "Die E-Mail-Adresse existiert bereits.",
"readOnlyUserMessage": "Sie können Ihr Benutzerkonto nicht ändern, da es schreibgeschützt ist.",
"readOnlyUsernameMessage": "Sie können Ihren Benutzernamen nicht ändern, da er schreibgeschützt ist.",
"readOnlyPasswordMessage": "Sie können Ihr Passwort nicht ändern, da es schreibgeschützt ist.",
"successTotpMessage": "Mehrfachauthentifizierung erfolgreich konfiguriert.",
"successTotpRemovedMessage": "Mehrfachauthentifizierung erfolgreich entfernt.",
"successGrantRevokedMessage": "Berechtigung erfolgreich widerrufen.",
"accountUpdatedMessage": "Ihr Benutzerkonto wurde aktualisiert.",
"accountPasswordUpdatedMessage": "Ihr Passwort wurde aktualisiert.",
"missingIdentityProviderMessage": "Identity Provider nicht angegeben.",
"invalidFederatedIdentityActionMessage": "Ungültige oder fehlende Aktion.",
"identityProviderNotFoundMessage": "Angegebener Identity Provider nicht gefunden.",
"federatedIdentityLinkNotActiveMessage": "Diese Identität ist nicht mehr aktiv.",
"federatedIdentityRemovingLastProviderMessage": "Sie können den letzten Eintrag nicht entfernen, da Sie kein Passwort haben.",
"identityProviderRedirectErrorMessage": "Fehler bei der Weiterleitung zum Identity Provider.",
"identityProviderRemovedMessage": "Identity Provider erfolgreich entfernt.",
"identityProviderAlreadyLinkedMessage": "Die föderierte Identität von {0} ist bereits einem anderen Benutzer zugewiesen.",
"staleCodeAccountMessage": "Diese Seite ist nicht mehr gültig, bitte versuchen Sie es noch einmal.",
"consentDenied": "Einverständnis verweigert.",
"accountDisabledMessage": "Ihr Benutzerkonto ist gesperrt, bitte kontaktieren Sie den Admin.",
"accountTemporarilyDisabledMessage":
"Ihr Benutzerkonto ist temporär gesperrt, bitte kontaktieren Sie den Admin oder versuchen Sie es später noch einmal.",
"invalidPasswordMinLengthMessage": "Ungültiges Passwort: Es muss mindestens {0} Zeichen lang sein.",
"invalidPasswordMinLowerCaseCharsMessage": "Ungültiges Passwort: Es muss mindestens {0} Kleinbuchstaben beinhalten.",
"invalidPasswordMinDigitsMessage": "Ungültiges Passwort: Es muss mindestens {0} Zahl(en) beinhalten.",
"invalidPasswordMinUpperCaseCharsMessage": "Ungültiges Passwort: Es muss mindestens {0} Großbuchstaben beinhalten.",
"invalidPasswordMinSpecialCharsMessage": "Ungültiges Passwort: Es muss mindestens {0} Sonderzeichen beinhalten.",
"invalidPasswordNotUsernameMessage": "Ungültiges Passwort: Es darf nicht gleich sein wie der Benutzername.",
"invalidPasswordRegexPatternMessage": "Ungültiges Passwort: Es entspricht nicht dem Regex-Muster.",
"invalidPasswordHistoryMessage": "Ungültiges Passwort: Es darf nicht einem der letzten {0} Passwörter entsprechen.",
"invalidPasswordBlacklistedMessage": "Ungültiges Passwort: Das Passwort steht auf der Blocklist (schwarzen Liste).",
"invalidPasswordGenericMessge": "Ungültiges Passwort: Das neue Passwort verletzt die Passwort-Richtlinien."
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,325 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Save",
"doCancel": "Cancel",
"doLogOutAllSessions": "Log out all sessions",
"doRemove": "Remove",
"doAdd": "Add",
"doSignOut": "Sign Out",
"doLogIn": "Log In",
"doLink": "Link",
"editAccountHtmlTitle": "Edit Account",
"personalInfoHtmlTitle": "Personal Info",
"federatedIdentitiesHtmlTitle": "Federated Identities",
"accountLogHtmlTitle": "Account Log",
"changePasswordHtmlTitle": "Change Password",
"deviceActivityHtmlTitle": "Device Activity",
"sessionsHtmlTitle": "Sessions",
"accountManagementTitle": "Keycloak Account Management",
"authenticatorTitle": "Authenticator",
"applicationsHtmlTitle": "Applications",
"linkedAccountsHtmlTitle": "Linked Accounts",
"accountManagementWelcomeMessage": "Welcome to Keycloak Account Management",
"personalInfoIntroMessage": "Manage your basic information",
"accountSecurityTitle": "Account Security",
"accountSecurityIntroMessage": "Control your password and account access",
"applicationsIntroMessage": "Track and manage your app permission to access your account",
"resourceIntroMessage": "Share your resources among team members",
"passwordLastUpdateMessage": "Your password was updated at",
"updatePasswordTitle": "Update Password",
"updatePasswordMessageTitle": "Make sure you choose a strong password",
"updatePasswordMessage":
"A strong password contains a mix of numbers, letters, and symbols. It is hard to guess, does not resemble a real word, and is only used for this account.",
"personalSubTitle": "Your Personal Info",
"personalSubMessage": "Manage this basic information: your first name, last name and email",
"authenticatorCode": "One-time code",
"email": "Email",
"firstName": "First name",
"givenName": "Given name",
"fullName": "Full name",
"lastName": "Last name",
"familyName": "Family name",
"password": "Password",
"currentPassword": "Current Password",
"passwordConfirm": "Confirmation",
"passwordNew": "New Password",
"username": "Username",
"address": "Address",
"street": "Street",
"locality": "City or Locality",
"region": "State, Province, or Region",
"postal_code": "Zip or Postal code",
"country": "Country",
"emailVerified": "Email verified",
"gssDelegationCredential": "GSS Delegation Credential",
"profileScopeConsentText": "User profile",
"emailScopeConsentText": "Email address",
"addressScopeConsentText": "Address",
"phoneScopeConsentText": "Phone number",
"offlineAccessScopeConsentText": "Offline Access",
"samlRoleListScopeConsentText": "My Roles",
"rolesScopeConsentText": "User roles",
"role_admin": "Admin",
"role_realm-admin": "Realm Admin",
"role_create-realm": "Create realm",
"role_view-realm": "View realm",
"role_view-users": "View users",
"role_view-applications": "View applications",
"role_view-clients": "View clients",
"role_view-events": "View events",
"role_view-identity-providers": "View identity providers",
"role_view-consent": "View consents",
"role_manage-realm": "Manage realm",
"role_manage-users": "Manage users",
"role_manage-applications": "Manage applications",
"role_manage-identity-providers": "Manage identity providers",
"role_manage-clients": "Manage clients",
"role_manage-events": "Manage events",
"role_view-profile": "View profile",
"role_manage-account": "Manage account",
"role_manage-account-links": "Manage account links",
"role_manage-consent": "Manage consents",
"role_read-token": "Read token",
"role_offline-access": "Offline access",
"role_uma_authorization": "Obtain permissions",
"client_account": "Account",
"client_account-console": "Account Console",
"client_security-admin-console": "Security Admin Console",
"client_admin-cli": "Admin CLI",
"client_realm-management": "Realm Management",
"client_broker": "Broker",
"requiredFields": "Required fields",
"allFieldsRequired": "All fields required",
"backToApplication": "&laquo; Back to application",
"backTo": "Back to {0}",
"date": "Date",
"event": "Event",
"ip": "IP",
"client": "Client",
"clients": "Clients",
"details": "Details",
"started": "Started",
"lastAccess": "Last Access",
"expires": "Expires",
"applications": "Applications",
"account": "Account",
"federatedIdentity": "Federated Identity",
"authenticator": "Authenticator",
"device-activity": "Device Activity",
"sessions": "Sessions",
"log": "Log",
"application": "Application",
"availableRoles": "Available Roles",
"grantedPermissions": "Granted Permissions",
"grantedPersonalInfo": "Granted Personal Info",
"additionalGrants": "Additional Grants",
"action": "Action",
"inResource": "in",
"fullAccess": "Full Access",
"offlineToken": "Offline Token",
"revoke": "Revoke Grant",
"configureAuthenticators": "Configured Authenticators",
"mobile": "Mobile",
"totpStep1": "Install one of the following applications on your mobile:",
"totpStep2": "Open the application and scan the barcode:",
"totpStep3": "Enter the one-time code provided by the application and click Save to finish the setup.",
"totpStep3DeviceName": "Provide a Device Name to help you manage your OTP devices.",
"totpManualStep2": "Open the application and enter the key:",
"totpManualStep3": "Use the following configuration values if the application allows setting them:",
"totpUnableToScan": "Unable to scan?",
"totpScanBarcode": "Scan barcode?",
"totp.totp": "Time-based",
"totp.hotp": "Counter-based",
"totpType": "Type",
"totpAlgorithm": "Algorithm",
"totpDigits": "Digits",
"totpInterval": "Interval",
"totpCounter": "Counter",
"totpDeviceName": "Device Name",
"missingUsernameMessage": "Please specify username.",
"missingFirstNameMessage": "Please specify first name.",
"invalidEmailMessage": "Invalid email address.",
"missingLastNameMessage": "Please specify last name.",
"missingEmailMessage": "Please specify email.",
"missingPasswordMessage": "Please specify password.",
"notMatchPasswordMessage": "Passwords don't match.",
"invalidUserMessage": "Invalid user",
"missingTotpMessage": "Please specify authenticator code.",
"missingTotpDeviceNameMessage": "Please specify device name.",
"invalidPasswordExistingMessage": "Invalid existing password.",
"invalidPasswordConfirmMessage": "Password confirmation doesn't match.",
"invalidTotpMessage": "Invalid authenticator code.",
"usernameExistsMessage": "Username already exists.",
"emailExistsMessage": "Email already exists.",
"readOnlyUserMessage": "You can't update your account as it is read-only.",
"readOnlyUsernameMessage": "You can't update your username as it is read-only.",
"readOnlyPasswordMessage": "You can't update your password as your account is read-only.",
"successTotpMessage": "Mobile authenticator configured.",
"successTotpRemovedMessage": "Mobile authenticator removed.",
"successGrantRevokedMessage": "Grant revoked successfully.",
"accountUpdatedMessage": "Your account has been updated.",
"accountPasswordUpdatedMessage": "Your password has been updated.",
"missingIdentityProviderMessage": "Identity provider not specified.",
"invalidFederatedIdentityActionMessage": "Invalid or missing action.",
"identityProviderNotFoundMessage": "Specified identity provider not found.",
"federatedIdentityLinkNotActiveMessage": "This identity is not active anymore.",
"federatedIdentityRemovingLastProviderMessage": "You can't remove last federated identity as you don't have a password.",
"identityProviderRedirectErrorMessage": "Failed to redirect to identity provider.",
"identityProviderRemovedMessage": "Identity provider removed successfully.",
"identityProviderAlreadyLinkedMessage": "Federated identity returned by {0} is already linked to another user.",
"staleCodeAccountMessage": "The page expired. Please try one more time.",
"consentDenied": "Consent denied.",
"accountDisabledMessage": "Account is disabled, contact your administrator.",
"accountTemporarilyDisabledMessage": "Account is temporarily disabled, contact your administrator or try again later.",
"invalidPasswordMinLengthMessage": "Invalid password: minimum length {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Invalid password: must contain at least {0} lower case characters.",
"invalidPasswordMinDigitsMessage": "Invalid password: must contain at least {0} numerical digits.",
"invalidPasswordMinUpperCaseCharsMessage": "Invalid password: must contain at least {0} upper case characters.",
"invalidPasswordMinSpecialCharsMessage": "Invalid password: must contain at least {0} special characters.",
"invalidPasswordNotUsernameMessage": "Invalid password: must not be equal to the username.",
"invalidPasswordRegexPatternMessage": "Invalid password: fails to match regex pattern(s).",
"invalidPasswordHistoryMessage": "Invalid password: must not be equal to any of last {0} passwords.",
"invalidPasswordBlacklistedMessage": "Invalid password: password is blacklisted.",
"invalidPasswordGenericMessage": "Invalid password: new password doesn't match password policies.",
"myResources": "My Resources",
"myResourcesSub": "My resources",
"doDeny": "Deny",
"doRevoke": "Revoke",
"doApprove": "Approve",
"doRemoveSharing": "Remove Sharing",
"doRemoveRequest": "Remove Request",
"peopleAccessResource": "People with access to this resource",
"resourceManagedPolicies": "Permissions granting access to this resource",
"resourceNoPermissionsGrantingAccess": "No permissions granting access to this resource",
"anyAction": "Any action",
"description": "Description",
"name": "Name",
"scopes": "Scopes",
"resource": "Resource",
"user": "User",
"peopleSharingThisResource": "People sharing this resource",
"shareWithOthers": "Share with others",
"needMyApproval": "Need my approval",
"requestsWaitingApproval": "Your requests waiting approval",
"icon": "Icon",
"requestor": "Requestor",
"owner": "Owner",
"resourcesSharedWithMe": "Resources shared with me",
"permissionRequestion": "Permission Requestion",
"permission": "Permission",
"shares": "share(s)",
"notBeingShared": "This resource is not being shared.",
"notHaveAnyResource": "You don't have any resources",
"noResourcesSharedWithYou": "There are no resources shared with you",
"havePermissionRequestsWaitingForApproval": "You have {0} permission request(s) waiting for approval.",
"clickHereForDetails": "Click here for details.",
"resourceIsNotBeingShared": "The resource is not being shared",
"locale_ca": "Català",
"locale_cs": "Čeština",
"locale_de": "Deutsch",
"locale_en": "English",
"locale_es": "Español",
"locale_fr": "Français",
"locale_it": "Italian",
"locale_ja": "日本語",
"locale_nl": "Nederlands",
"locale_no": "Norsk",
"locale_lt": "Lietuvių",
"locale_pt-BR": "Português (Brasil)",
"locale_ru": "Русский",
"locale_sk": "Slovenčina",
"locale_sv": "Svenska",
"locale_tr": "Turkish",
"locale_zh-CN": "中文简体",
"applicaitonName": "Name",
"applicationType": "Application Type",
"applicationInUse": "In-use app only",
"clearAllFilter": "Clear all filters",
"activeFilters": "Active filters",
"filterByName": "Filter By Name ...",
"allApps": "All applications",
"internalApps": "Internal applications",
"thirdpartyApps": "Third-Party applications",
"appResults": "Results",
"clientNotFoundMessage": "Client not found.",
"authorizedProvider": "Authorized Provider",
"authorizedProviderMessage": "Authorized Providers linked with your account",
"identityProvider": "Identity Provider",
"identityProviderMessage": "To link your account with identity providers you have configured",
"socialLogin": "Social Login",
"userDefined": "User Defined",
"removeAccess": "Remove Access",
"removeAccessMessage": "You will need to grant access again, if you want to use this app account.",
"authenticatorStatusMessage": "Two-factor authentication is currently",
"authenticatorFinishSetUpTitle": "Your Two-Factor Authentication",
"authenticatorFinishSetUpMessage":
"Each time you sign in to your Keycloak account, you will be asked to provide a two-factor authentication code.",
"authenticatorSubTitle": "Set Up Two-Factor Authentication",
"authenticatorSubMessage": "To enhance the security of your account, enable at least one of the available two-factor authentication methods.",
"authenticatorMobileTitle": "Mobile Authenticator",
"authenticatorMobileMessage": "Use mobile Authenticator to get Verification codes as the two-factor authentication.",
"authenticatorMobileFinishSetUpMessage": "The authenticator has been bound to your phone.",
"authenticatorActionSetup": "Set up",
"authenticatorSMSTitle": "SMS Code",
"authenticatorSMSMessage": "Keycloak will send the Verification code to your phone as the two-factor authentication.",
"authenticatorSMSFinishSetUpMessage": "Text messages are sent to",
"authenticatorDefaultStatus": "Default",
"authenticatorChangePhone": "Change Phone Number",
"authenticatorBackupCodesTitle": "Backup Codes",
"authenticatorBackupCodesMessage": "Get your 8-digit backup codes",
"authenticatorBackupCodesFinishSetUpMessage": "12 backup codes were generated at this time. Each one can be used once.",
"authenticatorMobileSetupTitle": "Mobile Authenticator Setup",
"smscodeIntroMessage": "Enter your phone number and a verification code will be sent to your phone.",
"mobileSetupStep1": "Install an authenticator application on your phone. The applications listed here are supported.",
"mobileSetupStep2": "Open the application and scan the barcode:",
"mobileSetupStep3": "Enter the one-time code provided by the application and click Save to finish the setup.",
"scanBarCode": "Want to scan the barcode?",
"enterBarCode": "Enter the one-time code",
"doCopy": "Copy",
"doFinish": "Finish",
"authenticatorSMSCodeSetupTitle": "SMS Code Setup",
"chooseYourCountry": "Choose your country",
"enterYourPhoneNumber": "Enter your phone number",
"sendVerficationCode": "Send Verification Code",
"enterYourVerficationCode": "Enter your verification code",
"authenticatorBackupCodesSetupTitle": "Backup Codes Setup",
"backupcodesIntroMessage":
"If you lose access to your phone, you can still log into your account through backup codes. Keep them somewhere safe and accessible.",
"realmName": "Realm",
"doDownload": "Download",
"doPrint": "Print",
"backupCodesTips-1": "Each backup code can be used once.",
"backupCodesTips-2": "These codes were generated on",
"generateNewBackupCodes": "Generate New Backup Codes",
"backupCodesTips-3": "When you generate new backup codes, the current codes will not work anymore.",
"backtoAuthenticatorPage": "Back to Authenticator Page",
"resources": "Resources",
"sharedwithMe": "Shared with Me",
"share": "Share",
"sharedwith": "Shared with",
"accessPermissions": "Access Permissions",
"permissionRequests": "Permission Requests",
"approve": "Approve",
"approveAll": "Approve all",
"people": "people",
"perPage": "per page",
"currentPage": "Current Page",
"sharetheResource": "Share the resource",
"group": "Group",
"selectPermission": "Select Permission",
"addPeople": "Add people to share your resource with",
"addTeam": "Add team to share your resource with",
"myPermissions": "My Permissions",
"waitingforApproval": "Waiting for approval",
"anyPermission": "Any Permission",
"openshift.scope.user_info": "User information",
"openshift.scope.user_check-access": "User access information",
"openshift.scope.user_full": "Full Access",
"openshift.scope.list-projects": "List projects"
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,137 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Guardar",
"doCancel": "Cancelar",
"doLogOutAllSessions": "Desconectar de todas las sesiones",
"doRemove": "Eliminar",
"doAdd": "Añadir",
"doSignOut": "Desconectar",
"editAccountHtmlTitle": "Editar cuenta",
"federatedIdentitiesHtmlTitle": "Identidades federadas",
"accountLogHtmlTitle": "Registro de la cuenta",
"changePasswordHtmlTitle": "Cambiar contraseña",
"sessionsHtmlTitle": "Sesiones",
"accountManagementTitle": "Gestión de Cuenta Keycloak",
"authenticatorTitle": "Autenticador",
"applicationsHtmlTitle": "Aplicaciones",
"authenticatorCode": "Código de un solo uso",
"email": "Email",
"firstName": "Nombre",
"givenName": "Nombre de pila",
"fullName": "Nombre completo",
"lastName": "Apellidos",
"familyName": "Apellido",
"password": "Contraseña",
"passwordConfirm": "Confirma la contraseña",
"passwordNew": "Nueva contraseña",
"username": "Usuario",
"address": "Dirección",
"street": "Calle",
"locality": "Ciudad o Municipio",
"region": "Estado, Provincia, o Región",
"postal_code": "Código Postal",
"country": "País",
"emailVerified": "Email verificado",
"gssDelegationCredential": "GSS Delegation Credential",
"role_admin": "Administrador",
"role_realm-admin": "Administrador del dominio",
"role_create-realm": "Crear dominio",
"role_view-realm": "Ver dominio",
"role_view-users": "Ver usuarios",
"role_view-applications": "Ver aplicaciones",
"role_view-clients": "Ver clientes",
"role_view-events": "Ver eventos",
"role_view-identity-providers": "Ver proveedores de identidad",
"role_manage-realm": "Gestionar dominio",
"role_manage-users": "Gestionar usuarios",
"role_manage-applications": "Gestionar aplicaciones",
"role_manage-identity-providers": "Gestionar proveedores de identidad",
"role_manage-clients": "Gestionar clientes",
"role_manage-events": "Gestionar eventos",
"role_view-profile": "Ver perfil",
"role_manage-account": "Gestionar cuenta",
"role_read-token": "Leer token",
"role_offline-access": "Acceso sin conexión",
"client_account": "Cuenta",
"client_security-admin-console": "Consola de Administración de Seguridad",
"client_realm-management": "Gestión de dominio",
"client_broker": "Broker",
"requiredFields": "Campos obligatorios",
"allFieldsRequired": "Todos los campos obligatorios",
"backToApplication": "&laquo; Volver a la aplicación",
"backTo": "Volver a {0}",
"date": "Fecha",
"event": "Evento",
"ip": "IP",
"client": "Cliente",
"clients": "Clientes",
"details": "Detalles",
"started": "Iniciado",
"lastAccess": "Último acceso",
"expires": "Expira",
"applications": "Aplicaciones",
"account": "Cuenta",
"federatedIdentity": "Identidad federada",
"authenticator": "Autenticador",
"sessions": "Sesiones",
"log": "Regisro",
"application": "Aplicación",
"availablePermissions": "Permisos disponibles",
"grantedPermissions": "Permisos concedidos",
"grantedPersonalInfo": "Información personal concedida",
"additionalGrants": "Permisos adicionales",
"action": "Acción",
"inResource": "en",
"fullAccess": "Acceso total",
"offlineToken": "Código de autorización offline",
"revoke": "Revocar permiso",
"configureAuthenticators": "Autenticadores configurados",
"mobile": "Móvil",
"totpStep1":
'Instala <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> o Google Authenticator en tu teléfono móvil. Ambas aplicaciones están disponibles en <a href="https://play.google.com">Google Play</a> y en la App Store de Apple.',
"totpStep2": "Abre la aplicación y escanea el código o introduce la clave.",
"totpStep3": "Introduce el código único que te muestra la aplicación de autenticación y haz clic en Enviar para finalizar la configuración",
"missingUsernameMessage": "Por favor indica tu usuario.",
"missingFirstNameMessage": "Por favor indica el nombre.",
"invalidEmailMessage": "Email no válido",
"missingLastNameMessage": "Por favor indica tus apellidos.",
"missingEmailMessage": "Por favor indica el email.",
"missingPasswordMessage": "Por favor indica tu contraseña.",
"notMatchPasswordMessage": "Las contraseñas no coinciden.",
"missingTotpMessage": "Por favor indica tu código de autenticación",
"invalidPasswordExistingMessage": "La contraseña actual no es correcta.",
"invalidPasswordConfirmMessage": "La confirmación de contraseña no coincide.",
"invalidTotpMessage": "El código de autenticación no es válido.",
"usernameExistsMessage": "El usuario ya existe",
"emailExistsMessage": "El email ya existe",
"readOnlyUserMessage": "No puedes actualizar tu usuario porque tu cuenta es de solo lectura.",
"readOnlyPasswordMessage": "No puedes actualizar tu contraseña porque tu cuenta es de solo lectura.",
"successTotpMessage": "Aplicación de autenticación móvil configurada.",
"successTotpRemovedMessage": "Aplicación de autenticación móvil eliminada.",
"successGrantRevokedMessage": "Permiso revocado correctamente",
"accountUpdatedMessage": "Tu cuenta se ha actualizado.",
"accountPasswordUpdatedMessage": "Tu contraseña se ha actualizado.",
"missingIdentityProviderMessage": "Proveedor de identidad no indicado.",
"invalidFederatedIdentityActionMessage": "Acción no válida o no indicada.",
"identityProviderNotFoundMessage": "No se encontró un proveedor de identidad.",
"federatedIdentityLinkNotActiveMessage": "Esta identidad ya no está activa",
"federatedIdentityRemovingLastProviderMessage": "No puedes eliminar la última identidad federada porque no tienes fijada una contraseña.",
"identityProviderRedirectErrorMessage": "Error en la redirección al proveedor de identidad",
"identityProviderRemovedMessage": "Proveedor de identidad borrado correctamente.",
"accountDisabledMessage": "La cuenta está desactivada, contacta con el administrador.",
"accountTemporarilyDisabledMessage": "La cuenta está temporalmente desactivada, contacta con el administrador o inténtalo de nuevo más tarde.",
"invalidPasswordMinLengthMessage": "Contraseña incorrecta: longitud mínima {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras minúsculas.",
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
"invalidPasswordMinUpperCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras mayúsculas.",
"invalidPasswordMinSpecialCharsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres especiales.",
"invalidPasswordNotUsernameMessage": "Contraseña incorrecta: no puede ser igual al nombre de usuario.",
"invalidPasswordRegexPatternMessage": "Contraseña incorrecta: no cumple la expresión regular.",
"invalidPasswordHistoryMessage": "Contraseña incorrecta: no puede ser igual a ninguna de las últimas {0} contraseñas."
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,150 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Sauvegarder",
"doCancel": "Annuler",
"doLogOutAllSessions": "Déconnexion de toutes les sessions",
"doRemove": "Supprimer",
"doAdd": "Ajouter",
"doSignOut": "Déconnexion",
"editAccountHtmlTitle": "Édition du compte",
"federatedIdentitiesHtmlTitle": "Identités fédérées",
"accountLogHtmlTitle": "Accès au compte",
"changePasswordHtmlTitle": "Changer de mot de passe",
"sessionsHtmlTitle": "Sessions",
"accountManagementTitle": "Gestion du compte Keycloak",
"authenticatorTitle": "Authentification",
"applicationsHtmlTitle": "Applications",
"authenticatorCode": "Mot de passe unique",
"email": "Courriel",
"firstName": "Prénom",
"givenName": "Prénom",
"fullName": "Nom complet",
"lastName": "Nom",
"familyName": "Nom de famille",
"password": "Mot de passe",
"passwordConfirm": "Confirmation",
"passwordNew": "Nouveau mot de passe",
"username": "Compte",
"address": "Adresse",
"street": "Rue",
"locality": "Ville ou Localité",
"region": "État, Province ou Région",
"postal_code": "Code Postal",
"country": "Pays",
"emailVerified": "Courriel vérifié",
"gssDelegationCredential": "Accréditation de délégation GSS",
"role_admin": "Administrateur",
"role_realm-admin": "Administrateur du domaine",
"role_create-realm": "Créer un domaine",
"role_view-realm": "Voir un domaine",
"role_view-users": "Voir les utilisateurs",
"role_view-applications": "Voir les applications",
"role_view-clients": "Voir les clients",
"role_view-events": "Voir les événements",
"role_view-identity-providers": "Voir les fournisseurs d'identités",
"role_manage-realm": "Gérer le domaine",
"role_manage-users": "Gérer les utilisateurs",
"role_manage-applications": "Gérer les applications",
"role_manage-identity-providers": "Gérer les fournisseurs d'identités",
"role_manage-clients": "Gérer les clients",
"role_manage-events": "Gérer les événements",
"role_view-profile": "Voir le profil",
"role_manage-account": "Gérer le compte",
"role_read-token": "Lire le jeton d'authentification",
"role_offline-access": "Accès hors-ligne",
"client_account": "Compte",
"client_security-admin-console": "Console d'administration de la sécurité",
"client_admin-cli": "Admin CLI",
"client_realm-management": "Gestion du domaine",
"client_broker": "Broker",
"requiredFields": "Champs obligatoires",
"allFieldsRequired": "Tous les champs sont obligatoires",
"backToApplication": "&laquo; Revenir à l'application",
"backTo": "Revenir à {0}",
"date": "Date",
"event": "Evénement",
"ip": "IP",
"client": "Client",
"clients": "Clients",
"details": "Détails",
"started": "Début",
"lastAccess": "Dernier accès",
"expires": "Expiration",
"applications": "Applications",
"account": "Compte",
"federatedIdentity": "Identité fédérée",
"authenticator": "Authentification",
"sessions": "Sessions",
"log": "Connexion",
"application": "Application",
"availablePermissions": "Permissions disponibles",
"grantedPermissions": "Permissions accordées",
"grantedPersonalInfo": "Informations personnelles accordées",
"additionalGrants": "Droits additionnels",
"action": "Action",
"inResource": "dans",
"fullAccess": "Accès complet",
"offlineToken": "Jeton d'authentification hors-ligne",
"revoke": "Révoquer un droit",
"configureAuthenticators": "Authentifications configurées.",
"mobile": "Téléphone mobile",
"totpStep1": "Installez une des applications suivantes sur votre mobile",
"totpStep2": "Ouvrez l'application et scannez le code-barres ou entrez la clef.",
"totpStep3": "Entrez le code à usage unique fourni par l'application et cliquez sur Sauvegarder pour terminer.",
"totpManualStep2": "Ouvrez l'application et entrez la clef",
"totpManualStep3": "Utilisez les valeurs de configuration suivante si l'application les autorise",
"totpUnableToScan": "Impossible de scanner ?",
"totpScanBarcode": "Scanner le code-barres ?",
"totp.totp": "Basé sur le temps",
"totp.hotp": "Basé sur un compteur",
"totpType": "Type",
"totpAlgorithm": "Algorithme",
"totpDigits": "Chiffres",
"totpInterval": "Intervalle",
"totpCounter": "Compteur",
"missingUsernameMessage": "Veuillez entrer votre nom d'utilisateur.",
"missingFirstNameMessage": "Veuillez entrer votre prénom.",
"invalidEmailMessage": "Courriel invalide.",
"missingLastNameMessage": "Veuillez entrer votre nom.",
"missingEmailMessage": "Veuillez entrer votre courriel.",
"missingPasswordMessage": "Veuillez entrer votre mot de passe.",
"notMatchPasswordMessage": "Les mots de passe ne sont pas identiques",
"missingTotpMessage": "Veuillez entrer le code d'authentification.",
"invalidPasswordExistingMessage": "Mot de passe existant invalide.",
"invalidPasswordConfirmMessage": "Le mot de passe de confirmation ne correspond pas.",
"invalidTotpMessage": "Le code d'authentification est invalide.",
"usernameExistsMessage": "Le nom d'utilisateur existe déjà.",
"emailExistsMessage": "Le courriel existe déjà.",
"readOnlyUserMessage": "Vous ne pouvez pas mettre à jour votre compte car il est en lecture seule.",
"readOnlyPasswordMessage": "Vous ne pouvez pas mettre à jour votre mot de passe car votre compte est en lecture seule.",
"successTotpMessage": "L'authentification via téléphone mobile est configurée.",
"successTotpRemovedMessage": "L'authentification via téléphone mobile est supprimée.",
"successGrantRevokedMessage": "Droit révoqué avec succès.",
"accountUpdatedMessage": "Votre compte a été mis à jour.",
"accountPasswordUpdatedMessage": "Votre mot de passe a été mis à jour.",
"missingIdentityProviderMessage": "Le fournisseur d'identité n'est pas spécifié.",
"invalidFederatedIdentityActionMessage": "Action manquante ou invalide.",
"identityProviderNotFoundMessage": "Le fournisseur d'identité spécifié n'est pas trouvé.",
"federatedIdentityLinkNotActiveMessage": "Cette identité n'est plus active dorénavant.",
"federatedIdentityRemovingLastProviderMessage":
"Vous ne pouvez pas supprimer votre dernière fédération d'identité sans avoir de mot de passe spécifié.",
"identityProviderRedirectErrorMessage": "Erreur de redirection vers le fournisseur d'identité.",
"identityProviderRemovedMessage": "Le fournisseur d'identité a été supprimé correctement.",
"identityProviderAlreadyLinkedMessage": "Le fournisseur d'identité retourné par {0} est déjà lié à un autre utilisateur.",
"accountDisabledMessage": "Ce compte est désactivé, veuillez contacter votre administrateur.",
"accountTemporarilyDisabledMessage": "Ce compte est temporairement désactivé, veuillez contacter votre administrateur ou réessayez plus tard.",
"invalidPasswordMinLengthMessage": "Mot de passe invalide: longueur minimale {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Mot de passe invalide: doit contenir au moins {0} lettre(s) en minuscule.",
"invalidPasswordMinDigitsMessage": "Mot de passe invalide: doit contenir au moins {0} chiffre(s).",
"invalidPasswordMinUpperCaseCharsMessage": "Mot de passe invalide: doit contenir au moins {0} lettre(s) en majuscule.",
"invalidPasswordMinSpecialCharsMessage": "Mot de passe invalide: doit contenir au moins {0} caractère(s) spéciaux.",
"invalidPasswordNotUsernameMessage": "Mot de passe invalide: ne doit pas être identique au nom d'utilisateur.",
"invalidPasswordRegexPatternMessage": "Mot de passe invalide: ne valide pas l'expression rationnelle.",
"invalidPasswordHistoryMessage": "Mot de passe invalide: ne doit pas être égal aux {0} derniers mots de passe."
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,310 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Salva",
"doCancel": "Annulla",
"doLogOutAllSessions": "Effettua il logout da tutte le sessioni",
"doRemove": "Elimina",
"doAdd": "Aggiungi",
"doSignOut": "Esci",
"doLogIn": "Log In",
"doLink": "Link",
"editAccountHtmlTitle": "Modifica Account",
"personalInfoHtmlTitle": "Informazioni personali",
"federatedIdentitiesHtmlTitle": "Identità federate",
"accountLogHtmlTitle": "Log dell'account",
"changePasswordHtmlTitle": "Cambia password",
"deviceActivityHtmlTitle": "Attività dei dispositivi",
"sessionsHtmlTitle": "Sessioni",
"accountManagementTitle": "Gestione degli account di Keycloak",
"authenticatorTitle": "Autenticatore",
"applicationsHtmlTitle": "Applicazioni",
"linkedAccountsHtmlTitle": "Account collegati",
"accountManagementWelcomeMessage": "Benvenuto nella gestione degli account di Keycloak",
"personalInfoIntroMessage": "Gestisci le tue informazioni di base",
"accountSecurityTitle": "Sicurezza dell'account",
"accountSecurityIntroMessage": "Controlla la tua password e gli accessi dell'account",
"applicationsIntroMessage": "Traccia e gestisci i permessi delle applicazioni nell'accesso al tuo account",
"resourceIntroMessage": "Condividi le tue risorse tra i membri del team",
"passwordLastUpdateMessage": "La tua password è stata aggiornata il",
"updatePasswordTitle": "Aggiornamento password",
"updatePasswordMessageTitle": "Assicurati di scegliere una password robusta",
"updatePasswordMessage":
"Una password robusta contiene un misto di numeri, lettere, e simboli. È difficile da indovinare, non assomiglia a una parola reale, ed è utilizzata solo per questo account.",
"personalSubTitle": "Le tue informazioni personali",
"personalSubMessage": "Gestisce queste informazioni di base: il tuo nome, cognome, e indirizzo email",
"authenticatorCode": "Codice monouso",
"email": "Email",
"firstName": "Nome",
"givenName": "Nome",
"fullName": "Nome completo",
"lastName": "Cognome",
"familyName": "Cognome",
"password": "Password",
"currentPassword": "Password attuale",
"passwordConfirm": "Conferma password",
"passwordNew": "Nuova password",
"username": "Username",
"address": "Indirizzo",
"street": "Via",
"locality": "Città o località",
"region": "Stato, Provincia, o Regione",
"postal_code": "CAP",
"country": "Paese",
"emailVerified": "Email verificata",
"gssDelegationCredential": "Credenziali delega GSS",
"profileScopeConsentText": "Profilo utente",
"emailScopeConsentText": "Indirizzo email",
"addressScopeConsentText": "Indirizzo",
"phoneScopeConsentText": "Numero di telefono",
"offlineAccessScopeConsentText": "Accesso offline",
"samlRoleListScopeConsentText": "I miei ruoli",
"rolesScopeConsentText": "Ruoli utente",
"role_admin": "Admin",
"role_realm-admin": "Realm admin",
"role_create-realm": "Crea realm",
"role_view-realm": "Visualizza realm",
"role_view-users": "Visualizza utenti",
"role_view-applications": "Visualizza applicazioni",
"role_view-clients": "Visualizza client",
"role_view-events": "Visualizza eventi",
"role_view-identity-providers": "Visualizza identity provider",
"role_view-consent": "Visualizza consensi",
"role_manage-realm": "Gestisci realm",
"role_manage-users": "Gestisci utenti",
"role_manage-applications": "Gestisci applicazioni",
"role_manage-identity-providers": "Gestisci identity provider",
"role_manage-clients": "Gestisci client",
"role_manage-events": "Gestisci eventi",
"role_view-profile": "Visualizza profilo",
"role_manage-account": "Gestisci account",
"role_manage-account-links": "Gestisci i link dell'account",
"role_manage-consent": "Gestisci consensi",
"role_read-token": "Leggi token",
"role_offline-access": "Accesso offline",
"role_uma_authorization": "Ottieni permessi",
"client_account": "Account",
"client_account-console": "Console account",
"client_security-admin-console": "Console di amministrazione di sicurezza",
"client_admin-cli": "Admin CLI",
"client_realm-management": "Gestione realm",
"client_broker": "Broker",
"requiredFields": "Campi obbligatori",
"allFieldsRequired": "Tutti campi obbligatori",
"backToApplication": "&laquo; Torna all'applicazione",
"backTo": "Torna a {0}",
"date": "Data",
"event": "Evento",
"ip": "IP",
"client": "Client",
"clients": "Client",
"details": "Dettagli",
"started": "Iniziato",
"lastAccess": "Ultimo accesso",
"expires": "Scade",
"applications": "Applicazioni",
"account": "Account",
"federatedIdentity": "Identità federate",
"authenticator": "Autenticatore",
"device-activity": "Attività dei dispositivi",
"sessions": "Sessioni",
"log": "Log",
"application": "Applicazione",
"availablePermissions": "Autorizzazioni disponibili",
"grantedPermissions": "Autorizzazioni concesse",
"grantedPersonalInfo": "Informazioni personali concesse",
"additionalGrants": "Ulteriori concessioni",
"action": "Azione",
"inResource": "in",
"fullAccess": "Accesso completo",
"offlineToken": "Token offline",
"revoke": "Revoca concessione",
"configureAuthenticators": "Autenticatori configurati",
"mobile": "Dispositivo mobile",
"totpStep1": "Installa una delle seguenti applicazioni sul tuo dispositivo mobile",
"totpStep2": "Apri l'applicazione e scansiona il codice QR",
"totpStep3": "Scrivi il codice monouso fornito dall'applicazione e clicca Salva per completare il setup.",
"totpStep3DeviceName": "Fornisci il nome del dispositivo per aiutarti a gestire i dispositivi di autenticazione.",
"totpManualStep2": "Apri l'applicazione e scrivi la chiave",
"totpManualStep3": "Usa le seguenti impostazioni se l'applicazione lo consente",
"totpUnableToScan": "Non riesci a scansionare il codice QR?",
"totpScanBarcode": "Vuoi scansionare il codice QR?",
"totp.totp": "Basato sull'ora",
"totp.hotp": "Basato sul contatore",
"totpType": "Tipo",
"totpAlgorithm": "Algoritmo",
"totpDigits": "Cifre",
"totpInterval": "Intervallo",
"totpCounter": "Contatore",
"totpDeviceName": "Nome dispositivo",
"missingUsernameMessage": "Inserisci lo username.",
"missingFirstNameMessage": "Inserisci il nome.",
"invalidEmailMessage": "Indirizzo email non valido.",
"missingLastNameMessage": "Inserisci il cognome.",
"missingEmailMessage": "Inserisci l'indirizzo email.",
"missingPasswordMessage": "Inserisci la password.",
"notMatchPasswordMessage": "Le password non coincidono.",
"invalidUserMessage": "Utente non valido",
"missingTotpMessage": "Inserisci il codice di autenticazione.",
"missingTotpDeviceNameMessage": "Inserisci il nome del dispositivo di autenticazione.",
"invalidPasswordExistingMessage": "Password esistente non valida.",
"invalidPasswordConfirmMessage": "La password di conferma non coincide.",
"invalidTotpMessage": "Codice di autenticazione non valido.",
"usernameExistsMessage": "Username già esistente.",
"emailExistsMessage": "Email già esistente.",
"readOnlyUserMessage": "Non puoi aggiornare il tuo account poiché è in modalità sola lettura.",
"readOnlyUsernameMessage": "Non puoi aggiornare il tuo nome utente poiché è in modalità sola lettura.",
"readOnlyPasswordMessage": "Non puoi aggiornare il tuo account poiché è in modalità sola lettura.",
"successTotpMessage": "Autenticatore mobile configurato.",
"successTotpRemovedMessage": "Autenticatore mobile eliminato.",
"successGrantRevokedMessage": "Concessione revocata con successo.",
"accountUpdatedMessage": "Il tuo account è stato aggiornato.",
"accountPasswordUpdatedMessage": "La tua password è stata aggiornata.",
"missingIdentityProviderMessage": "Identity provider non specificato.",
"invalidFederatedIdentityActionMessage": "Azione non valida o mancante.",
"identityProviderNotFoundMessage": "L'identity provider specificato non è stato trovato.",
"federatedIdentityLinkNotActiveMessage": "Questo identity non è più attivo.",
"federatedIdentityRemovingLastProviderMessage": "Non puoi rimuovere l'ultima identità federata poiché non hai più la password.",
"identityProviderRedirectErrorMessage": "Il reindirizzamento all'identity provider è fallito.",
"identityProviderRemovedMessage": "Identity provider eliminato correttamente.",
"identityProviderAlreadyLinkedMessage": "L'identità federata restituita da {0} è già collegata ad un altro utente.",
"staleCodeAccountMessage": "La pagina è scaduta. Prova di nuovo.",
"consentDenied": "Consenso negato.",
"accountDisabledMessage": "Account disabilitato, contatta l'amministratore.",
"accountTemporarilyDisabledMessage": "L'account è temporaneamente disabilitato, contatta l'amministratore o riprova più tardi.",
"invalidPasswordMinLengthMessage": "Password non valida: lunghezza minima {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Password non valida: deve contenere almeno {0} caratteri minuscoli.",
"invalidPasswordMinDigitsMessage": "Password non valida: deve contenere almeno {0} numeri.",
"invalidPasswordMinUpperCaseCharsMessage": "Password non valida: deve contenere almeno {0} caratteri maiuscoli.",
"invalidPasswordMinSpecialCharsMessage": "Password non valida: deve contenere almeno {0} caratteri speciali.",
"invalidPasswordNotUsernameMessage": "Password non valida: non deve essere uguale allo username.",
"invalidPasswordRegexPatternMessage": "Password non valida: fallito il match con una o più espressioni regolari.",
"invalidPasswordHistoryMessage": "Password non valida: non deve essere uguale a una delle ultime {0} password.",
"invalidPasswordBlacklistedMessage": "Password non valida: la password non è consentita.",
"invalidPasswordGenericMessage": "Password non valida: la nuova password non rispetta le indicazioni previste.",
"myResources": "Le mie risorse",
"myResourcesSub": "Le mie risorse",
"doDeny": "Nega",
"doRevoke": "Revoca",
"doApprove": "Approva",
"doRemoveSharing": "Rimuovi condivisione",
"doRemoveRequest": "Rimuovi richiesta",
"peopleAccessResource": "Persone che hanno accesso a questa risorsa",
"resourceManagedPolicies": "Permessi che danno accesso a questa risorsa",
"resourceNoPermissionsGrantingAccess": "Nessun permesso dà accesso a questa risorsa",
"anyAction": "Qualsiasi azione",
"description": "Descrizione",
"name": "Nome",
"scopes": "Ambito",
"resource": "Risorsa",
"user": "Utente",
"peopleSharingThisResource": "Persone che condividono questa risorsa",
"shareWithOthers": "Condividi con altri",
"needMyApproval": "Richiede la mia approvazione",
"requestsWaitingApproval": "La tua richiesta è in attesa di approvazione",
"icon": "Icona",
"requestor": "Richiedente",
"owner": "Proprietario",
"resourcesSharedWithMe": "Risorse condivise con me",
"permissionRequestion": "Richiesta di permesso",
"permission": "Permesso",
"shares": "condivisioni",
"notBeingShared": "Questa risorsa non è in condivisione.",
"notHaveAnyResource": "Non hai nessuna risorsa",
"noResourcesSharedWithYou": "Non ci sono risorse condivise con te",
"havePermissionRequestsWaitingForApproval": "Hai {0} richiesta(e) di permesso in attesa di approvazione.",
"clickHereForDetails": "Clicca qui per i dettagli.",
"resourceIsNotBeingShared": "La risorsa non è in condivisione",
"locale_it": "Italiano",
"applicaitonName": "Nome",
"applicationType": "Tipo applicazione",
"applicationInUse": "In-use app only",
"clearAllFilter": "Azzera tutti i filtri",
"activeFilters": "Filtri attivi",
"filterByName": "Filtra per nome ...",
"allApps": "Tutte le applicazioni",
"internalApps": "Applicazioni interne",
"thirdpartyApps": "Applicazioni di terze parti",
"appResults": "Risultati",
"clientNotFoundMessage": "Client non trovato.",
"authorizedProvider": "Provider autorizzato",
"authorizedProviderMessage": "Provider autorizzati collegati al tuo account",
"identityProvider": "Identity provider",
"identityProviderMessage": "Collegare il tuo account con gli identity provider che hai configurato",
"socialLogin": "Social Login",
"userDefined": "Definito dall'utente",
"removeAccess": "Rimuovi accesso",
"removeAccessMessage": "Devi concedere di nuovo l'accesso, se vuoi utilizzare l'account di questa applicazione.",
"authenticatorStatusMessage": "L'autenticazione a due fattori è attualmente",
"authenticatorFinishSetUpTitle": "La tua autenticazione a due fattori",
"authenticatorFinishSetUpMessage":
"Ogni volta che effettui l'accesso al tuo account Keycloak, ti verrà richiesto di fornire il tuo codice di autenticazione a due fattori.",
"authenticatorSubTitle": "Imposta l'autenticazione a due fattori",
"authenticatorSubMessage":
"Per incrementare la sicurezza del tuo account, attiva almeno uno dei metodi disponibili per l'autenticazione a due fattori.",
"authenticatorMobileTitle": "Autenticatore mobile",
"authenticatorMobileMessage": "Utilizza l'autenticatore mobile per ottenere i codici di verifica per l'autenticazione a due fattori.",
"authenticatorMobileFinishSetUpMessage": "L'autenticatore è stato collegato al tuo telefono.",
"authenticatorActionSetup": "Set up",
"authenticatorSMSTitle": "Codice SMS",
"authenticatorSMSMessage": "Keycloak invierà il codice di verifica al tuo telefono per l'autenticazione a due fattori.",
"authenticatorSMSFinishSetUpMessage": "I messaggi di testo vengono inviati a",
"authenticatorDefaultStatus": "Default",
"authenticatorChangePhone": "Cambia numero di telefono",
"authenticatorBackupCodesTitle": "Codici di backup",
"authenticatorBackupCodesMessage": "Ottieni i tuoi codici di backup a otto cifre",
"authenticatorBackupCodesFinishSetUpMessage": "Sono stati generati dodici codici di backup. Ognuno può essere usato una sola volta.",
"authenticatorMobileSetupTitle": "Setup autenticatore mobile",
"smscodeIntroMessage": "Inserisci il tuo numero di telefono e ti verrà inviato un codice di verifica.",
"mobileSetupStep1": "Installa un'applicazione di autenticazione sul tuo telefono. Sono supportate le applicazioni qui elencate.",
"mobileSetupStep2": "Apri l'applicazione e scansiona il codice QR:",
"mobileSetupStep3": "Inserisci il codice monouso fornito dall'applicazione e clicca Salva per completare il setup.",
"scanBarCode": "Vuoi scansionare il codice QR?",
"enterBarCode": "Inserisci il codice monouso",
"doCopy": "Copia",
"doFinish": "Termina",
"authenticatorSMSCodeSetupTitle": "Setup codice SMS",
"chooseYourCountry": "Scegli la tua nazione",
"enterYourPhoneNumber": "Inserisci il tuo numero di telefono",
"sendVerficationCode": "Invia il codice di verifica",
"enterYourVerficationCode": "Inserisci il codice di verifica",
"authenticatorBackupCodesSetupTitle": "Setup backup codici",
"backupcodesIntroMessage":
"Se non disponi più del tuo telefono, puoi comunque accedere al tuo account attraverso i codici di backup. Conservali in un posto sicuro e accessibile.",
"realmName": "Realm",
"doDownload": "Download",
"doPrint": "Stampa",
"backupCodesTips-1": "Ogni codice di backup può essere usato una sola volta.",
"backupCodesTips-2": "Questi codici sono stati generati il",
"generateNewBackupCodes": "Genera dei nuovi codici di backup",
"backupCodesTips-3": "Quando generi dei nuovi codici di backup, quelli attuali non funzioneranno più.",
"backtoAuthenticatorPage": "Torna alla pagina dell'autenticatore",
"resources": "Risorse",
"sharedwithMe": "Condiviso con me",
"share": "Condiviso",
"sharedwith": "Condiviso con",
"accessPermissions": "Permessi di accesso",
"permissionRequests": "Richieste di permesso",
"approve": "Approva",
"approveAll": "Approva tutti",
"people": "persone",
"perPage": "per pagina",
"currentPage": "Pagina corrente",
"sharetheResource": "Condividi la risorsa",
"group": "Gruppo",
"selectPermission": "Seleziona permessi",
"addPeople": "Aggiungi persone con le quali condividere la tua risorsa",
"addTeam": "Aggiungi gruppi con i quali condividere la tua risorsa",
"myPermissions": "Miei permessi",
"waitingforApproval": "Attesa dell'approvazione",
"anyPermission": "Qualsiasi permesso",
"openshift.scope.user_info": "Informazioni utente",
"openshift.scope.user_check-access": "Informazioni per l'accesso dell'utente",
"openshift.scope.user_full": "Accesso completo",
"openshift.scope.list-projects": "Elenca progetti"
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,324 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "保存",
"doCancel": "キャンセル",
"doLogOutAllSessions": "全セッションからログアウト",
"doRemove": "削除",
"doAdd": "追加",
"doSignOut": "サインアウト",
"doLogIn": "ログイン",
"doLink": "リンク",
"editAccountHtmlTitle": "アカウントの編集",
"personalInfoHtmlTitle": "個人情報",
"federatedIdentitiesHtmlTitle": "連携済みアイデンティティー",
"accountLogHtmlTitle": "アカウントログ",
"changePasswordHtmlTitle": "パスワード変更",
"deviceActivityHtmlTitle": "デバイス・アクティビティー",
"sessionsHtmlTitle": "セッション",
"accountManagementTitle": "Keycloakアカウント管理",
"authenticatorTitle": "オーセンティケーター",
"applicationsHtmlTitle": "アプリケーション",
"linkedAccountsHtmlTitle": "リンクされたアカウント",
"accountManagementWelcomeMessage": "Keycloakアカウント管理へようこそ",
"personalInfoIntroMessage": "基本情報を管理する",
"accountSecurityTitle": "アカウント・セキュリティー",
"accountSecurityIntroMessage": "パスワードとアカウント・アクセスを制御する",
"applicationsIntroMessage": "アカウントへアクセスするためにアプリのパーミッションを追跡して管理する",
"resourceIntroMessage": "チームメンバー間でリソースを共有する",
"passwordLastUpdateMessage": "パスワードは更新されました",
"updatePasswordTitle": "パスワードの更新",
"updatePasswordMessageTitle": "強力なパスワードを選択してください",
"updatePasswordMessage":
"強力なパスワードは、数字、文字、記号を含みます。推測が難しく、実在する言葉に似ておらず、このアカウントだけで使用されています。",
"personalSubTitle": "個人情報",
"personalSubMessage": "この基本情報を管理してください:名、姓、メール",
"authenticatorCode": "ワンタイムコード",
"email": "Eメール",
"firstName": "名",
"givenName": "名",
"fullName": "氏名",
"lastName": "姓",
"familyName": "姓",
"password": "パスワード",
"currentPassword": "現在のパスワード",
"passwordConfirm": "新しいパスワード(確認)",
"passwordNew": "新しいパスワード",
"username": "ユーザー名",
"address": "住所",
"street": "番地",
"locality": "市区町村",
"region": "都道府県",
"postal_code": "郵便番号",
"country": "国",
"emailVerified": "確認済みEメール",
"gssDelegationCredential": "GSS委譲クレデンシャル",
"profileScopeConsentText": "ユーザー・プロファイル",
"emailScopeConsentText": "メールアドレス",
"addressScopeConsentText": "アドレス",
"phoneScopeConsentText": "電話番号",
"offlineAccessScopeConsentText": "オフライン・アクセス",
"samlRoleListScopeConsentText": "ロール",
"rolesScopeConsentText": "ユーザーロール",
"role_admin": "管理者",
"role_realm-admin": "レルム管理者",
"role_create-realm": "レルムの作成",
"role_view-realm": "レルムの参照",
"role_view-users": "ユーザーの参照",
"role_view-applications": "アプリケーションの参照",
"role_view-clients": "クライアントの参照",
"role_view-events": "イベントの参照",
"role_view-identity-providers": "アイデンティティー・プロバイダーの参照",
"role_view-consent": "同意の参照",
"role_manage-realm": "レルムの管理",
"role_manage-users": "ユーザーの管理",
"role_manage-applications": "アプリケーションの管理",
"role_manage-identity-providers": "アイデンティティー・プロバイダーの管理",
"role_manage-clients": "クライアントの管理",
"role_manage-events": "イベントの管理",
"role_view-profile": "プロファイルの参照",
"role_manage-account": "アカウントの管理",
"role_manage-account-links": "アカウントリンクの管理",
"role_manage-consent": "同意の管理",
"role_read-token": "トークンの参照",
"role_offline-access": "オフライン・アクセス",
"role_uma_authorization": "パーミッションの取得",
"client_account": "アカウント",
"client_account-console": "アカウント・コンソール",
"client_security-admin-console": "セキュリティー管理コンソール",
"client_admin-cli": "管理CLI",
"client_realm-management": "レルム管理",
"client_broker": "ブローカー",
"requiredFields": "必須",
"allFieldsRequired": "全ての入力項目が必須",
"backToApplication": "&laquo; アプリケーションに戻る",
"backTo": "{0}に戻る",
"date": "日付",
"event": "イベント",
"ip": "IP",
"client": "クライアント",
"clients": "クライアント",
"details": "詳細",
"started": "開始",
"lastAccess": "最終アクセス",
"expires": "有効期限",
"applications": "アプリケーション",
"account": "アカウント",
"federatedIdentity": "連携済みアイデンティティー",
"authenticator": "オーセンティケーター",
"device-activity": "デバイス・アクティビティー",
"sessions": "セッション",
"log": "ログ",
"application": "アプリケーション",
"availableRoles": "利用可能なロール",
"grantedPermissions": "許可されたパーミッション",
"grantedPersonalInfo": "許可された個人情報",
"additionalGrants": "追加の許可",
"action": "アクション",
"inResource": "in",
"fullAccess": "フルアクセス",
"offlineToken": "オフライン・トークン",
"revoke": "許可の取り消し",
"configureAuthenticators": "設定済みのオーセンティケーター",
"mobile": "モバイル",
"totpStep1": "モバイルに以下のアプリケーションのいずれかをインストールしてください。",
"totpStep2": "アプリケーションを開き、バーコードをスキャンしてください。",
"totpStep3": "アプリケーションで提供されたワンタイムコードを入力して保存をクリックし、セットアップを完了してください。",
"totpStep3DeviceName": "OTPデバイスの管理に役立つようなデバイス名を指定してください。",
"totpManualStep2": "アプリケーションを開き、キーを入力してください。",
"totpManualStep3": "アプリケーションが設定できる場合は、次の設定値を使用してください。",
"totpUnableToScan": "スキャンできませんか?",
"totpScanBarcode": "バーコードをスキャンしますか?",
"totp.totp": "時間ベース",
"totp.hotp": "カウンターベース",
"totpType": "タイプ",
"totpAlgorithm": "アルゴリズム",
"totpDigits": "数字",
"totpInterval": "間隔",
"totpCounter": "カウンター",
"totpDeviceName": "デバイス名",
"missingUsernameMessage": "ユーザー名を入力してください。",
"missingFirstNameMessage": "名を入力してください。",
"invalidEmailMessage": "無効なメールアドレスです。",
"missingLastNameMessage": "姓を入力してください。",
"missingEmailMessage": "Eメールを入力してください。",
"missingPasswordMessage": "パスワードを入力してください。",
"notMatchPasswordMessage": "パスワードが一致していません。",
"invalidUserMessage": "無効なユーザーです。",
"missingTotpMessage": "オーセンティケーター・コードを入力してください。",
"missingTotpDeviceNameMessage": "デバイス名を指定してください。",
"invalidPasswordExistingMessage": "既存のパスワードが不正です。",
"invalidPasswordConfirmMessage": "新しいパスワード(確認)と一致していません。",
"invalidTotpMessage": "無効なオーセンティケーター・コードです。",
"usernameExistsMessage": "既に存在するユーザー名です。",
"emailExistsMessage": "既に存在するEメールです。",
"readOnlyUserMessage": "読み取り専用のため、アカウントを更新することはできません。",
"readOnlyUsernameMessage": "読み取り専用のため、ユーザー名を更新することはできません。",
"readOnlyPasswordMessage": "読み取り専用のため、パスワードを更新することはできません。",
"successTotpMessage": "モバイル・オーセンティケーターが設定されました。",
"successTotpRemovedMessage": "モバイル・オーセンティケーターが削除されました。",
"successGrantRevokedMessage": "許可が正常に取り消しされました。",
"accountUpdatedMessage": "アカウントが更新されました。",
"accountPasswordUpdatedMessage": "パスワードが更新されました。",
"missingIdentityProviderMessage": "アイデンティティー・プロバイダーが指定されていません。",
"invalidFederatedIdentityActionMessage": "無効または存在しないアクションです。",
"identityProviderNotFoundMessage": "指定されたアイデンティティー・プロバイダーが見つかりません。",
"federatedIdentityLinkNotActiveMessage": "このアイデンティティーは有効ではありません。",
"federatedIdentityRemovingLastProviderMessage": "パスワードがないため、最後の連携済みアイデンティティーが削除できません。",
"identityProviderRedirectErrorMessage": "アイデンティティー・プロバイダーへのリダイレクトに失敗しました。",
"identityProviderRemovedMessage": "アイデンティティー・プロバイダーが正常に削除されました。",
"identityProviderAlreadyLinkedMessage": "{0}から返された連携済みアイデンティティーは既に他のユーザーに関連付けされています。",
"staleCodeAccountMessage": "有効期限切れです。再度お試しください。",
"consentDenied": "同意が拒否されました。",
"accountDisabledMessage": "アカウントが無効です。管理者に連絡してください。",
"accountTemporarilyDisabledMessage": "アカウントが一時的に無効です。管理者に連絡するか、しばらく時間をおいてから再度お試しください。",
"invalidPasswordMinLengthMessage": "無効なパスワード: 最小{0}の長さが必要です。",
"invalidPasswordMinLowerCaseCharsMessage": "無効なパスワード: 少なくとも{0}文字の小文字を含む必要があります。",
"invalidPasswordMinDigitsMessage": "無効なパスワード: 少なくとも{0}文字の数字を含む必要があります。",
"invalidPasswordMinUpperCaseCharsMessage": "無効なパスワード:少なくとも{0}文字の大文字を含む必要があります。",
"invalidPasswordMinSpecialCharsMessage": "無効なパスワード: 少なくとも{0}文字の特殊文字を含む必要があります。",
"invalidPasswordNotUsernameMessage": "無効なパスワード: ユーザー名と同じパスワードは禁止されています。",
"invalidPasswordRegexPatternMessage": "無効なパスワード: 正規表現パターンと一致しません。",
"invalidPasswordHistoryMessage": "無効なパスワード: 最近の{0}パスワードのいずれかと同じパスワードは禁止されています。",
"invalidPasswordBlacklistedMessage": "無効なパスワード: パスワードがブラックリストに含まれています。",
"invalidPasswordGenericMessage": "無効なパスワード: 新しいパスワードはパスワード・ポリシーと一致しません。",
"myResources": "マイリソース",
"myResourcesSub": "マイリソース",
"doDeny": "拒否",
"doRevoke": "取り消し",
"doApprove": "承認",
"doRemoveSharing": "共有の削除",
"doRemoveRequest": "要求の削除",
"peopleAccessResource": "このリソースにアクセスできる人",
"resourceManagedPolicies": "このリソースへのアクセスを許可するパーミッション",
"resourceNoPermissionsGrantingAccess": "このリソースへのアクセスを許可する権限はありません",
"anyAction": "任意のアクション",
"description": "説明",
"name": "名前",
"scopes": "スコープ",
"resource": "リソース",
"user": "ユーザー",
"peopleSharingThisResource": "このリソースを共有している人",
"shareWithOthers": "他人と共有",
"needMyApproval": "承認が必要",
"requestsWaitingApproval": "承認待ちの要求",
"icon": "アイコン",
"requestor": "要求者",
"owner": "オーナー",
"resourcesSharedWithMe": "共有しているリソース",
"permissionRequestion": "パーミッションの要求",
"permission": "パーミッション",
"shares": "共有(複数)",
"notBeingShared": "このリソースは共有されていません。",
"notHaveAnyResource": "リソースがありません。",
"noResourcesSharedWithYou": "共有しているリソースはありません",
"havePermissionRequestsWaitingForApproval": "承認を待っている{0}個のパーミッションの要求があります。",
"clickHereForDetails": "詳細はこちらをクリックしてください。",
"resourceIsNotBeingShared": "リソースは共有されていません。",
"locale_ca": "Català",
"locale_de": "Deutsch",
"locale_en": "English",
"locale_es": "Español",
"locale_fr": "Français",
"locale_it": "Italian",
"locale_ja": "日本語",
"locale_nl": "Nederlands",
"locale_no": "Norsk",
"locale_lt": "Lietuvių",
"locale_pt-BR": "Português (Brasil)",
"locale_ru": "Русский",
"locale_sk": "Slovenčina",
"locale_sv": "Svenska",
"locale_tr": "Turkish",
"locale_zh-CN": "中文简体",
"applicaitonName": "名前",
"applicationType": "アプリケーション・タイプ",
"applicationInUse": "使用中のアプリケーションのみ",
"clearAllFilter": "すべてのフィルターをクリア",
"activeFilters": "アクティブなフィルター",
"filterByName": "名前でフィルタリング...",
"allApps": "すべてのアプリケーション",
"internalApps": "内部アプリケーション",
"thirdpartyApps": "サードパーティーのアプリケーション",
"appResults": "結果",
"clientNotFoundMessage": "クライアントが見つかりません。",
"authorizedProvider": "認可済みプロバイダー",
"authorizedProviderMessage": "アカウントにリンクされた認可済みプロバイダー",
"identityProvider": "アイデンティティー・プロバイダー",
"identityProviderMessage": "アカウントと設定したアイデンティティー・プロバイダーをリンクするには",
"socialLogin": "ソーシャル・ログイン",
"userDefined": "ユーザー定義",
"removeAccess": "アクセス権の削除",
"removeAccessMessage": "このアプリ・アカウントを使用する場合は、アクセス権を再度付与する必要があります。",
"authenticatorStatusMessage": "2要素認証は現在",
"authenticatorFinishSetUpTitle": "あなたの2要素認証",
"authenticatorFinishSetUpMessage": "Keycloakアカウントにサインインするたびに、2要素認証コードを入力するように求められます。",
"authenticatorSubTitle": "2要素認証を設定する",
"authenticatorSubMessage": "アカウントのセキュリティーを強化するには、利用可能な2要素認証の方式のうち少なくとも1つを有効にします。",
"authenticatorMobileTitle": "モバイル・オーセンティケーター",
"authenticatorMobileMessage": "モバイル・オーセンティケーターを使用して、2要素認証として確認コードを取得します。",
"authenticatorMobileFinishSetUpMessage": "オーセンティケーターはあなたの携帯電話にバインドされています。",
"authenticatorActionSetup": "セットアップ",
"authenticatorSMSTitle": "SMSコード",
"authenticatorSMSMessage": "Keycloakは、2要素認証として確認コードを携帯電話に送信します。",
"authenticatorSMSFinishSetUpMessage": "テキスト・メッセージが次の電話番号宛に送信されます:",
"authenticatorDefaultStatus": "デフォルト",
"authenticatorChangePhone": "電話番号の変更",
"authenticatorBackupCodesTitle": "バックアップ・コード",
"authenticatorBackupCodesMessage": "8桁のバックアップ・コードの入手",
"authenticatorBackupCodesFinishSetUpMessage": "この時点で12個のバックアップ・コードが生成されました。それぞれ一度だけ使用できます。",
"authenticatorMobileSetupTitle": "モバイル・オーセンティケーターのセットアップ",
"smscodeIntroMessage": "電話番号を入力すると、確認コードがあなたの電話に送信されます。",
"mobileSetupStep1":
"携帯電話にオーセンティケーター・アプリケーションをインストールします。ここにリストされているアプリケーションがサポートされています。",
"mobileSetupStep2": "アプリケーションを開き、バーコードをスキャンしてください。",
"mobileSetupStep3": "アプリケーションから提供されたワンタイムコードを入力し、保存をクリックしてセットアップを終了します。",
"scanBarCode": "バーコードをスキャンしますか?",
"enterBarCode": "ワンタイムコードを入力してください",
"doCopy": "コピー",
"doFinish": "終了",
"authenticatorSMSCodeSetupTitle": "SMSコードのセットアップ",
"chooseYourCountry": "国を選んでください",
"enterYourPhoneNumber": "電話番号を入力してください",
"sendVerficationCode": "確認コードの送信",
"enterYourVerficationCode": "確認コードを入力してください",
"authenticatorBackupCodesSetupTitle": "バックアップ・コードのセットアップ",
"backupcodesIntroMessage":
"携帯電話にアクセスできない場合でも、バックアップ・コードを使用してアカウントにログインできます。どこか安全でアクセス可能な場所に保管してください。",
"realmName": "レルム",
"doDownload": "ダウンロード",
"doPrint": "印刷",
"backupCodesTips-1": "各バックアップ・コードは1回使用できます。",
"backupCodesTips-2": "これらのコードはこの日に生成されました:",
"generateNewBackupCodes": "新しいバックアップ・コードを生成する",
"backupCodesTips-3": "新しいバックアップ・コードを生成すると、現在のコードは機能しなくなります。",
"backtoAuthenticatorPage": "オーセンティケーター・ページに戻る",
"resources": "リソース",
"sharedwithMe": "私と共有",
"share": "共有",
"sharedwith": "共有",
"accessPermissions": "アクセス・パーミッション",
"permissionRequests": "パーミッションの要求",
"approve": "承認",
"approveAll": "すべて承認",
"people": "人",
"perPage": "1ページあたり",
"currentPage": "現在のページ",
"sharetheResource": "リソースの共有",
"group": "グループ",
"selectPermission": "パーミッションを選択",
"addPeople": "あなたのリソースを共有する人を追加",
"addTeam": "あなたのリソースを共有するチームを追加",
"myPermissions": "私のパーミッション",
"waitingforApproval": "承認待ち",
"anyPermission": "任意のパーミッション",
"openshift.scope.user_info": "ユーザー情報",
"openshift.scope.user_check-access": "ユーザーアクセス情報",
"openshift.scope.user_full": "フルアクセス",
"openshift.scope.list-projects": "プロジェクトの一覧表示"
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,143 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Saugoti",
"doCancel": "Atšaukti",
"doLogOutAllSessions": "Atjungti visas sesijas",
"doRemove": "Šalinti",
"doAdd": "Pridėti",
"doSignOut": "Atsijungti",
"editAccountHtmlTitle": "Redaguoti paskyrą",
"federatedIdentitiesHtmlTitle": "Susietos paskyros",
"accountLogHtmlTitle": "Paskyros žurnalas",
"changePasswordHtmlTitle": "Keisti slaptažodį",
"sessionsHtmlTitle": "Prisijungimo sesijos",
"accountManagementTitle": "Keycloak Naudotojų Administravimas",
"authenticatorTitle": "Autentifikatorius",
"applicationsHtmlTitle": "Programos",
"authenticatorCode": "Vienkartinis kodas",
"email": "El. paštas",
"firstName": "Vardas",
"givenName": "Pavardė",
"fullName": "Pilnas vardas",
"lastName": "Pavardė",
"familyName": "Pavardė",
"password": "Slaptažodis",
"passwordConfirm": "Pakartotas slaptažodis",
"passwordNew": "Naujas slaptažodis",
"username": "Naudotojo vardas",
"address": "Adresas",
"street": "Gatvė",
"locality": "Miestas arba vietovė",
"region": "Rajonas",
"postal_code": "Pašto kodas",
"country": "Šalis",
"emailVerified": "El. pašto adresas patvirtintas",
"gssDelegationCredential": "GSS prisijungimo duomenų delegavimas",
"role_admin": "Administratorius",
"role_realm-admin": "Srities administravimas",
"role_create-realm": "Kurti sritį",
"role_view-realm": "Peržiūrėti sritį",
"role_view-users": "Peržiūrėti naudotojus",
"role_view-applications": "Peržiūrėti programas",
"role_view-clients": "Peržiūrėti klientines programas",
"role_view-events": "Peržiūrėti įvykių žurnalą",
"role_view-identity-providers": "Peržiūrėti tapatybės teikėjus",
"role_manage-realm": "Valdyti sritis",
"role_manage-users": "Valdyti naudotojus",
"role_manage-applications": "Valdyti programas",
"role_manage-identity-providers": "Valdyti tapatybės teikėjus",
"role_manage-clients": "Valdyti programas",
"role_manage-events": "Valdyti įvykius",
"role_view-profile": "Peržiūrėti paskyrą",
"role_manage-account": "Valdyti paskyrą",
"role_read-token": "Skaityti prieigos rakšą",
"role_offline-access": "Darbas neprisijungus",
"role_uma_authorization": "Įgauti UMA autorizavimo teises",
"client_account": "Paskyra",
"client_security-admin-console": "Saugumo administravimo konsolė",
"client_admin-cli": "Administravimo CLI",
"client_realm-management": "Srities valdymas",
"client_broker": "Tarpininkas",
"requiredFields": "Privalomi laukai",
"allFieldsRequired": "Visi laukai yra privalomi",
"backToApplication": "&laquo; Grįžti į programą",
"backTo": "Atgal į {0}",
"date": "Data",
"event": "Įvykis",
"ip": "IP",
"client": "Klientas",
"clients": "Klientai",
"details": "Detaliau",
"started": "Sukūrimo laikas",
"lastAccess": "Vėliausia prieiga",
"expires": "Galioja iki",
"applications": "Programos",
"account": "Paskyra",
"federatedIdentity": "Susieta tapatybė",
"authenticator": "Autentifikatorius",
"sessions": "Sesijos",
"log": "Įvykiai",
"application": "Programa",
"availablePermissions": "Galimos teisės",
"grantedPermissions": "Įgalintos teisės",
"grantedPersonalInfo": "Įgalinta asmeninė informacija",
"additionalGrants": "Papildomi įgaliojimai",
"action": "Veiksmas",
"inResource": "yra",
"fullAccess": "Pilna prieiga",
"offlineToken": "Režimo neprisijungus raktas (token)",
"revoke": "Atšaukti įgaliojimą",
"configureAuthenticators": "Sukonfigūruotas autentifikatorius",
"mobile": "Mobilus",
"totpStep1":
'Įdiekite <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> arba Google Authenticator savo įrenginyje. Programėlės prieinamos <a href="https://play.google.com">Google Play</a> ir Apple App Store.',
"totpStep2": "Atidarykite programėlę ir nuskenuokite barkodą arba įveskite kodą.",
"totpStep3": "Įveskite programėlėje sugeneruotą vieną kartą galiojantį kodą ir paspauskite Saugoti norėdami prisijungti.",
"missingUsernameMessage": "Prašome įvesti naudotojo vardą.",
"missingFirstNameMessage": "Prašome įvesti vardą.",
"invalidEmailMessage": "Neteisingas el. pašto adresas.",
"missingLastNameMessage": "Prašome įvesti pavardę.",
"missingEmailMessage": "Prašome įvesti el. pašto adresą.",
"missingPasswordMessage": "Prašome įvesti slaptažodį.",
"notMatchPasswordMessage": "Slaptažodžiai nesutampa.",
"missingTotpMessage": "Prašome įvesti autentifikacijos kodą.",
"invalidPasswordExistingMessage": "Neteisingas dabartinis slaptažodis.",
"invalidPasswordConfirmMessage": "Pakartotas slaptažodis nesutampa.",
"invalidTotpMessage": "Neteisingas autentifikacijos kodas.",
"usernameExistsMessage": "Toks naudotojas jau egzistuoja.",
"emailExistsMessage": "El. pašto adresas jau egzistuoja.",
"readOnlyUserMessage": "Tik skaitymui sukonfigūruotos paskyros duomenų atnaujinti neleidžiama.",
"readOnlyPasswordMessage": "Tik skaitymui sukonfigūruotos paskyros slaptažodžio atnaujinti neleidžiama.",
"successTotpMessage": "Mobilus autentifikatorius sukonfigūruotas.",
"successTotpRemovedMessage": "Mobilus autentifikatorius pašalintas.",
"successGrantRevokedMessage": "Įgalinimas pašalintas sėkmingai.",
"accountUpdatedMessage": "Jūsų paskyros duomenys sėkmingai atnaujinti.",
"accountPasswordUpdatedMessage": "Jūsų paskyros slaptažodis pakeistas.",
"missingIdentityProviderMessage": "Nenurodytas tapatybės teikėjas.",
"invalidFederatedIdentityActionMessage": "Neteisingas arba nežinomas veiksmas.",
"identityProviderNotFoundMessage": "Nurodytas tapatybės teikėjas nerastas.",
"federatedIdentityLinkNotActiveMessage": "Nurodyta susieta tapatybė neaktyvi.",
"federatedIdentityRemovingLastProviderMessage":
"Jūs negalite pašalinti paskutinio tapatybės teikėjo sąsajos, nes Jūs neturite nusistatę paskyros slaptažodžio.",
"identityProviderRedirectErrorMessage": "Klaida nukreipiant į tapatybės teikėjo puslapį.",
"identityProviderRemovedMessage": "Tapatybės teikėjas sėkmingai pašalintas.",
"identityProviderAlreadyLinkedMessage": "Susieta tapatybė iš {0} jau susieta su kita paskyra.",
"staleCodeAccountMessage": "Puslapio galiojimas baigėsi. Bandykite dar kartą.",
"consentDenied": "Prieiga draudžiama.",
"accountDisabledMessage": "Paskyros galiojimas sustabdytas, kreipkitės į administratorių.",
"accountTemporarilyDisabledMessage": "Paskyros galiojimas laikinai sustabdytas. Kreipkitės į administratorių arba pabandykite vėliau.",
"invalidPasswordMinLengthMessage": "Per trumpas slaptažodis: mažiausias ilgis {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} mažąją raidę.",
"invalidPasswordMinDigitsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} skaitmenį.",
"invalidPasswordMinUpperCaseCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} didžiąją raidę.",
"invalidPasswordMinSpecialCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} specialų simbolį.",
"invalidPasswordNotUsernameMessage": "Neteisingas slaptažodis: slaptažodis negali sutapti su naudotojo vardu.",
"invalidPasswordRegexPatternMessage": "Neteisingas slaptažodis: slaptažodis netenkina regex taisyklės(ių).",
"invalidPasswordHistoryMessage": "Neteisingas slaptažodis: slaptažodis negali sutapti su prieš tai buvusiais {0} slaptažodžiais."
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,143 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Opslaan",
"doCancel": "Annuleer",
"doLogOutAllSessions": "Alle sessies uitloggen",
"doRemove": "Verwijder",
"doAdd": "Voeg toe",
"doSignOut": "Afmelden",
"editAccountHtmlTitle": "Bewerk account",
"federatedIdentitiesHtmlTitle": "Federated Identities",
"accountLogHtmlTitle": "Account log",
"changePasswordHtmlTitle": "Verander wachtwoord",
"sessionsHtmlTitle": "Sessies",
"accountManagementTitle": "Keycloak Accountbeheer",
"authenticatorTitle": "Authenticator",
"applicationsHtmlTitle": "Toepassingen",
"authenticatorCode": "Eenmalige code",
"email": "E-mailadres",
"firstName": "Voornaam",
"givenName": "Voornaam",
"fullName": "Volledige naam",
"lastName": "Achternaam",
"familyName": "Achternaam",
"password": "Wachtwoord",
"passwordConfirm": "Bevestiging",
"passwordNew": "Nieuw Wachtwoord",
"username": "Gebruikersnaam",
"address": "Adres",
"street": "Straat",
"locality": "Stad of plaats",
"region": "Staat, provincie of regio",
"postal_code": "Postcode",
"country": "Land",
"emailVerified": "E-mailadres geverifieerd",
"gssDelegationCredential": "GSS gedelegeerde aanmeldgegevens",
"role_admin": "Beheer",
"role_realm-admin": "Realmbeheer",
"role_create-realm": "Creëer realm",
"role_view-realm": "Bekijk realm",
"role_view-users": "Bekijk gebruikers",
"role_view-applications": "Bekijk toepassingen",
"role_view-clients": "Bekijk clients",
"role_view-events": "Bekijk gebeurtenissen",
"role_view-identity-providers": "Bekijk identity providers",
"role_manage-realm": "Beheer realm",
"role_manage-users": "Beheer gebruikers",
"role_manage-applications": "Beheer toepassingen",
"role_manage-identity-providers": "Beheer identity providers",
"role_manage-clients": "Beheer clients",
"role_manage-events": "Beheer gebeurtenissen",
"role_view-profile": "Bekijk profiel",
"role_manage-account": "Beheer account",
"role_manage-account-links": "Beheer accountkoppelingen",
"role_read-token": "Lees token",
"role_offline-access": "Offline toegang",
"role_uma_authorization": "Verkrijg UMA rechten",
"client_account": "Account",
"client_security-admin-console": "Console Veligheidsbeheer",
"client_admin-cli": "Beheer CLI",
"client_realm-management": "Realmbeheer",
"client_broker": "Broker",
"requiredFields": "Verplichte velden",
"allFieldsRequired": "Alle velden verplicht",
"backToApplication": "&laquo; Terug naar toepassing",
"backTo": "Terug naar {0}",
"date": "Datum",
"event": "Gebeurtenis",
"ip": "IP",
"client": "Client",
"clients": "Clients",
"details": "Details",
"started": "Gestart",
"lastAccess": "Laatste toegang",
"expires": "Vervalt",
"applications": "Toepassingen",
"account": "Account",
"federatedIdentity": "Federated Identity",
"authenticator": "Authenticator",
"sessions": "Sessies",
"log": "Log",
"application": "Toepassing",
"availablePermissions": "Beschikbare rechten",
"grantedPermissions": "Gegunde rechten",
"grantedPersonalInfo": "Gegunde Persoonsgegevens",
"additionalGrants": "Verdere vergunningen",
"action": "Actie",
"inResource": "in",
"fullAccess": "Volledige toegang",
"offlineToken": "Offline Token",
"revoke": "Vergunning intrekken",
"configureAuthenticators": "Ingestelde authenticators",
"mobile": "Mobiel nummer",
"totpStep1": "Installeer een van de onderstaande applicaties op uw mobiele apparaat:",
"totpStep2": "Open de toepassing en scan de QR-code of voer de sleutel in.",
"totpStep3": "Voer de door de toepassing gegeven eenmalige code in en klik op Opslaan om de configuratie af te ronden.",
"missingUsernameMessage": "Gebruikersnaam ontbreekt.",
"missingFirstNameMessage": "Voornaam onbreekt.",
"invalidEmailMessage": "Ongeldig e-mailadres.",
"missingLastNameMessage": "Achternaam ontbreekt.",
"missingEmailMessage": "E-mailadres ontbreekt.",
"missingPasswordMessage": "Wachtwoord ontbreekt.",
"notMatchPasswordMessage": "Wachtwoorden komen niet overeen.",
"missingTotpMessage": "Authenticatiecode ontbreekt.",
"invalidPasswordExistingMessage": "Ongeldig bestaand wachtwoord.",
"invalidPasswordConfirmMessage": "Wachtwoordbevestiging komt niet overeen.",
"invalidTotpMessage": "Ongeldige authenticatiecode.",
"emailExistsMessage": "E-mailadres bestaat reeds.",
"readOnlyUserMessage": "U kunt uw account niet bijwerken aangezien het account alleen-lezen is.",
"readOnlyPasswordMessage": "U kunt uw wachtwoord niet wijzigen omdat uw account alleen-lezen is.",
"successTotpMessage": "Mobiele authenticator geconfigureerd.",
"successTotpRemovedMessage": "Mobiele authenticator verwijderd.",
"successGrantRevokedMessage": "Vergunning succesvol ingetrokken",
"accountUpdatedMessage": "Uw account is gewijzigd.",
"accountPasswordUpdatedMessage": "Uw wachtwoord is gewijzigd.",
"missingIdentityProviderMessage": "Geen identity provider aangegeven.",
"invalidFederatedIdentityActionMessage": "Ongeldige of ontbrekende actie op federated identity.",
"identityProviderNotFoundMessage": "Gespecificeerde identity provider niet gevonden.",
"federatedIdentityLinkNotActiveMessage": "Deze federated identity is niet langer geldig.",
"federatedIdentityRemovingLastProviderMessage":
"U kunt de laatste federated identity provider niet verwijderen aangezien u dan niet langer zou kunnen inloggen.",
"identityProviderRedirectErrorMessage": "Kon niet herverwijzen naar identity provider.",
"identityProviderRemovedMessage": "Identity provider met succes verwijderd.",
"identityProviderAlreadyLinkedMessage": "Door {0} teruggegeven federated identity is al gekoppeld aan een andere gebruiker.",
"staleCodeAccountMessage": "De pagina is verlopen. Probeer het nogmaals.",
"consentDenied": "Toestemming geweigerd",
"accountDisabledMessage": "Account is gedeactiveerd. Contacteer de beheerder.",
"accountTemporarilyDisabledMessage": "Account is tijdelijk deactiveerd, neem contact op met de beheerder of probeer het later opnieuw.",
"invalidPasswordMinLengthMessage": "Ongeldig wachtwoord: de minimale lengte is {0} karakters.",
"invalidPasswordMinLowerCaseCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} kleine letters bevatten.",
"invalidPasswordMinDigitsMessage": "Ongeldig wachtwoord: het moet minstens {0} getallen bevatten.",
"invalidPasswordMinUpperCaseCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} hoofdletters bevatten.",
"invalidPasswordMinSpecialCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} speciale karakters bevatten.",
"invalidPasswordNotUsernameMessage": "Ongeldig wachtwoord: het mag niet overeenkomen met de gebruikersnaam.",
"invalidPasswordRegexPatternMessage": "Ongeldig wachtwoord: het voldoet niet aan het door de beheerder ingestelde patroon.",
"invalidPasswordHistoryMessage": "Ongeldig wachtwoord: het mag niet overeen komen met een van de laatste {0} wachtwoorden.",
"invalidPasswordGenericMessage": "Ongeldig wachtwoord: het nieuwe wachtwoord voldoet niet aan het wachtwoordbeleid."
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,153 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Lagre",
"doCancel": "Avbryt",
"doLogOutAllSessions": "Logg ut av alle sesjoner",
"doRemove": "Fjern",
"doAdd": "Legg til",
"doSignOut": "Logg ut",
"editAccountHtmlTitle": "Rediger konto",
"federatedIdentitiesHtmlTitle": "Federerte identiteter",
"accountLogHtmlTitle": "Kontologg",
"changePasswordHtmlTitle": "Endre passord",
"sessionsHtmlTitle": "Sesjoner",
"accountManagementTitle": "Keycloak kontoadministrasjon",
"authenticatorTitle": "Autentikator",
"applicationsHtmlTitle": "Applikasjoner",
"authenticatorCode": "Engangskode",
"email": "E-post",
"firstName": "Fornavn",
"givenName": "Fornavn",
"fullName": "Fullt navn",
"lastName": "Etternavn",
"familyName": "Etternavn",
"password": "Passord",
"passwordConfirm": "Bekreftelse",
"passwordNew": "Nytt passord",
"username": "Brukernavn",
"address": "Adresse",
"street": "Gate-/veinavn + husnummer",
"locality": "By",
"region": "Fylke",
"postal_code": "Postnummer",
"country": "Land",
"emailVerified": "E-post bekreftet",
"gssDelegationCredential": "GSS legitimasjonsdelegering",
"role_admin": "Administrator",
"role_realm-admin": "Administrator for sikkerhetsdomene",
"role_create-realm": "Opprette sikkerhetsdomene",
"role_view-realm": "Se sikkerhetsdomene",
"role_view-users": "Se brukere",
"role_view-applications": "Se applikasjoner",
"role_view-clients": "Se klienter",
"role_view-events": "Se hendelser",
"role_view-identity-providers": "Se identitetsleverandører",
"role_manage-realm": "Administrere sikkerhetsdomene",
"role_manage-users": "Administrere brukere",
"role_manage-applications": "Administrere applikasjoner",
"role_manage-identity-providers": "Administrere identitetsleverandører",
"role_manage-clients": "Administrere klienter",
"role_manage-events": "Administrere hendelser",
"role_view-profile": "Se profil",
"role_manage-account": "Administrere konto",
"role_read-token": "Lese token",
"role_offline-access": "Frakoblet tilgang",
"role_uma_authorization": "Skaffe tillatelser",
"client_account": "Konto",
"client_security-admin-console": "Sikkerhetsadministrasjonskonsoll",
"client_admin-cli": "Kommandolinje-grensesnitt for administrator",
"client_realm-management": "Sikkerhetsdomene-administrasjon",
"client_broker": "Broker",
"requiredFields": "Obligatoriske felt",
"allFieldsRequired": "Alle felt må fylles ut",
"backToApplication": "&laquo; Tilbake til applikasjonen",
"backTo": "Tilbake til {0}",
"date": "Dato",
"event": "Hendelse",
"ip": "IP",
"client": "Klient",
"clients": "Klienter",
"details": "Detaljer",
"started": "Startet",
"lastAccess": "Sist benyttet",
"expires": "Utløper",
"applications": "Applikasjoner",
"account": "Konto",
"federatedIdentity": "Federert identitet",
"authenticator": "Autentikator",
"sessions": "Sesjoner",
"log": "Logg",
"application": "Applikasjon",
"availablePermissions": "Tilgjengelige rettigheter",
"grantedPermissions": "Innvilgede rettigheter",
"grantedPersonalInfo": "Innvilget personlig informasjon",
"additionalGrants": "Ekstra rettigheter",
"action": "Handling",
"inResource": "i",
"fullAccess": "Full tilgang",
"offlineToken": "Offline token",
"revoke": "Opphev rettighet",
"configureAuthenticators": "Konfigurerte autentikatorer",
"mobile": "Mobiltelefon",
"totpStep1": "Installer ett av følgende programmer på mobilen din.",
"totpStep2": "Åpne applikasjonen og skann strekkoden eller skriv inn koden.",
"totpStep3": "Skriv inn engangskoden gitt av applikasjonen og klikk Lagre for å fullføre.",
"missingUsernameMessage": "Vennligst oppgi brukernavn.",
"missingFirstNameMessage": "Vennligst oppgi fornavn.",
"invalidEmailMessage": "Ugyldig e-postadresse.",
"missingLastNameMessage": "Vennligst oppgi etternavn.",
"missingEmailMessage": "Vennligst oppgi e-postadresse.",
"missingPasswordMessage": "Vennligst oppgi passord.",
"notMatchPasswordMessage": "Passordene er ikke like.",
"missingTotpMessage": "Vennligst oppgi engangskode.",
"invalidPasswordExistingMessage": "Ugyldig eksisterende passord.",
"invalidPasswordConfirmMessage": "Passordene er ikke like.",
"invalidTotpMessage": "Ugyldig engangskode.",
"usernameExistsMessage": "Brukernavnet finnes allerede.",
"emailExistsMessage": "E-postadressen finnes allerede.",
"readOnlyUserMessage": "Du kan ikke oppdatere kontoen din ettersom den er skrivebeskyttet.",
"readOnlyPasswordMessage": "Du kan ikke oppdatere passordet ditt ettersom kontoen din er skrivebeskyttet.",
"successTotpMessage": "Autentikator for mobiltelefon er konfigurert.",
"successTotpRemovedMessage": "Autentikator for mobiltelefon er fjernet.",
"successGrantRevokedMessage": "Vellykket oppheving av rettighet.",
"accountUpdatedMessage": "Kontoen din har blitt oppdatert.",
"accountPasswordUpdatedMessage": "Ditt passord har blitt oppdatert.",
"missingIdentityProviderMessage": "Identitetsleverandør er ikke spesifisert.",
"invalidFederatedIdentityActionMessage": "Ugyldig eller manglende handling.",
"identityProviderNotFoundMessage": "Spesifisert identitetsleverandør ikke funnet.",
"federatedIdentityLinkNotActiveMessage": "Denne identiteten er ikke lenger aktiv.",
"federatedIdentityRemovingLastProviderMessage": "Du kan ikke fjerne siste federerte identitet ettersom du ikke har et passord.",
"identityProviderRedirectErrorMessage": "Redirect til identitetsleverandør feilet.",
"identityProviderRemovedMessage": "Fjerning av identitetsleverandør var vellykket.",
"identityProviderAlreadyLinkedMessage": "Federert identitet returnert av {0} er allerede koblet til en annen bruker.",
"staleCodeAccountMessage": "Siden har utløpt. Vennligst prøv en gang til.",
"consentDenied": "Samtykke avslått.",
"accountDisabledMessage": "Konto er deaktivert, kontakt administrator.",
"accountTemporarilyDisabledMessage": "Konto er midlertidig deaktivert, kontakt administrator eller prøv igjen senere.",
"invalidPasswordMinLengthMessage": "Ugyldig passord: minimum lengde {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Ugyldig passord: må inneholde minimum {0} små bokstaver.",
"invalidPasswordMinDigitsMessage": "Ugyldig passord: må inneholde minimum {0} sifre.",
"invalidPasswordMinUpperCaseCharsMessage": "Ugyldig passord: må inneholde minimum {0} store bokstaver.",
"invalidPasswordMinSpecialCharsMessage": "Ugyldig passord: må inneholde minimum {0} spesialtegn.",
"invalidPasswordNotUsernameMessage": "Ugyldig passord: kan ikke være likt brukernavn.",
"invalidPasswordRegexPatternMessage": "Ugyldig passord: tilfredsstiller ikke kravene for passord-mønster.",
"invalidPasswordHistoryMessage": "Ugyldig passord: kan ikke være likt noen av de {0} foregående passordene.",
"locale_ca": "Català",
"locale_de": "Deutsch",
"locale_en": "English",
"locale_es": "Español",
"locale_fr": "Français",
"locale_it": "Italian",
"locale_ja": "日本語",
"locale_no": "Norsk",
"locale_nl": "Nederlands",
"locale_pt-BR": "Português (Brasil)",
"locale_ru": "Русский",
"locale_zh-CN": "中文简体"
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,8 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,140 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Salvar",
"doCancel": "Cancelar",
"doLogOutAllSessions": "Sair de todas as sessões",
"doRemove": "Remover",
"doAdd": "Adicionar",
"doSignOut": "Sair",
"editAccountHtmlTitle": "Editar Conta",
"federatedIdentitiesHtmlTitle": "Identidades Federadas",
"accountLogHtmlTitle": "Log da conta",
"changePasswordHtmlTitle": "Alterar senha",
"sessionsHtmlTitle": "Sessões",
"accountManagementTitle": "Gerenciamento de Conta",
"authenticatorTitle": "Autenticator",
"applicationsHtmlTitle": "Aplicativos",
"authenticatorCode": "Código autenticador",
"email": "E-mail",
"firstName": "Primeiro nome",
"givenName": "Primeiro nome",
"fullName": "Nome completo",
"lastName": "Sobrenome",
"familyName": "Sobrenome",
"password": "Senha",
"passwordConfirm": "Confirmação",
"passwordNew": "Nova senha",
"username": "Nome de usúario",
"address": "Endereço",
"street": "Logradouro",
"locality": "Cidade ou Localidade",
"region": "Estado",
"postal_code": "CEP",
"country": "País",
"emailVerified": "E-mail verificado",
"gssDelegationCredential": "GSS Delegação de Credencial",
"role_admin": "Admin",
"role_realm-admin": "Realm Admin",
"role_create-realm": "Cria realm",
"role_view-realm": "Visualiza realm",
"role_view-users": "Visualiza usuários",
"role_view-applications": "Visualiza aplicações",
"role_view-clients": "Visualiza clientes",
"role_view-events": "Visualiza eventos",
"role_view-identity-providers": "Visualiza provedores de identidade",
"role_manage-realm": "Gerencia realm",
"role_manage-users": "Gerencia usuários",
"role_manage-applications": "Gerencia aplicações",
"role_manage-identity-providers": "Gerencia provedores de identidade",
"role_manage-clients": "Gerencia clientes",
"role_manage-events": "Gerencia eventos",
"role_view-profile": "Visualiza perfil",
"role_manage-account": "Gerencia conta",
"role_read-token": "Lê token",
"role_offline-access": "Acesso Offline",
"role_uma_authorization": "Obter permissões",
"client_account": "Conta",
"client_security-admin-console": "Console de Administração de Segurança",
"client_admin-cli": "Admin CLI",
"client_realm-management": "Gerenciamento de Realm",
"client_broker": "Broker",
"requiredFields": "Campos obrigatórios",
"allFieldsRequired": "Todos os campos são obrigatórios",
"backToApplication": "&laquo; Voltar para aplicação",
"backTo": "Voltar para {0}",
"date": "Data",
"event": "Evento",
"ip": "IP",
"client": "Cliente",
"clients": "Clientes",
"details": "Detalhes",
"started": "Iniciado",
"lastAccess": "Último acesso",
"expires": "Expira",
"applications": "Aplicativos",
"account": "Conta",
"federatedIdentity": "Identidade Federada",
"authenticator": "Autenticador",
"sessions": "Sessões",
"log": "Log",
"application": "Aplicativo",
"availablePermissions": "Permissões Disponíveis",
"grantedPermissions": "Permissões Concedidas",
"grantedPersonalInfo": "Informações Pessoais Concedidas",
"additionalGrants": "Concessões Adicionais",
"action": "Ação",
"inResource": "em",
"fullAccess": "Acesso Completo",
"offlineToken": "Offline Token",
"revoke": "Revogar Concessões",
"configureAuthenticators": "Autenticadores Configurados",
"mobile": "Mobile",
"totpStep1":
'Instalar <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> ou Google Authenticator em seu dispositivo. Ambas aplicações estão disponíveis no <a href="https://play.google.com">Google Play</a> e na Apple App Store.',
"totpStep2": "Abra o aplicativo e escaneie o código de barras ou entre com o código.",
"totpStep3": "Digite o código fornecido pelo aplicativo e clique em Salvar para concluir a configuração.",
"missingUsernameMessage": "Por favor, especifique o nome de usuário.",
"missingFirstNameMessage": "Por favor, informe o primeiro nome.",
"invalidEmailMessage": "E-mail inválido.",
"missingLastNameMessage": "Por favor, informe o sobrenome.",
"missingEmailMessage": "Por favor, informe o e-mail.",
"missingPasswordMessage": "Por favor, informe a senha.",
"notMatchPasswordMessage": "As senhas não coincidem.",
"missingTotpMessage": "Por favor, informe o código autenticador.",
"invalidPasswordExistingMessage": "Senha atual inválida.",
"invalidPasswordConfirmMessage": "A senha de confirmação não coincide.",
"invalidTotpMessage": "Código autenticador inválido.",
"usernameExistsMessage": "Este nome de usuário já existe.",
"emailExistsMessage": "Este e-mail já existe.",
"readOnlyUserMessage": "Você não pode atualizar sua conta, uma vez que é apenas de leitura",
"readOnlyPasswordMessage": "Você não pode atualizar sua senha, sua conta é somente leitura",
"successTotpMessage": "Autenticador mobile configurado.",
"successTotpRemovedMessage": "Autenticador mobile removido.",
"successGrantRevokedMessage": "Concessões revogadas com sucesso.",
"accountUpdatedMessage": "Sua conta foi atualizada",
"accountPasswordUpdatedMessage": "Sua senha foi atualizada",
"missingIdentityProviderMessage": "Provedor de identidade não especificado",
"invalidFederatedIdentityActionMessage": "Ação inválida ou ausente",
"identityProviderNotFoundMessage": "O provedor de identidade especificado não foi encontrado",
"federatedIdentityLinkNotActiveMessage": "Esta identidade não está mais em atividade",
"federatedIdentityRemovingLastProviderMessage": "Você não pode remover a última identidade federada como você não tem senha",
"identityProviderRedirectErrorMessage": "Falha ao redirecionar para o provedor de identidade",
"identityProviderRemovedMessage": "Provedor de identidade removido com sucesso",
"identityProviderAlreadyLinkedMessage": "Identidade federada retornado por {0} já está ligado a outro usuário.",
"accountDisabledMessage": "Conta desativada, contate o administrador",
"accountTemporarilyDisabledMessage": "A conta está temporariamente indisponível, contate o administrador ou tente novamente mais tarde",
"invalidPasswordMinLengthMessage": "Senha inválida: comprimento mínimo {0}",
"invalidPasswordMinLowerCaseCharsMessage": "Senha inválida: deve conter pelo menos {0} caractere(s) minúsculo",
"invalidPasswordMinDigitsMessage": "Senha inválida: deve conter pelo menos {0} número(s)",
"invalidPasswordMinUpperCaseCharsMessage": "Senha inválida: deve conter pelo menos {0} caractere(s) maiúsculo",
"invalidPasswordMinSpecialCharsMessage": "Senha inválida: deve conter pelo menos {0} caractere(s) especial",
"invalidPasswordNotUsernameMessage": "Senha inválida: não deve ser igual ao nome de usuário",
"invalidPasswordRegexPatternMessage": "Senha inválida: não corresponde ao padrão da expressão regular.",
"invalidPasswordHistoryMessage": "Senha inválida: não pode ser igual a qualquer uma das {0} últimas senhas."
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,143 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Сохранить",
"doCancel": "Отмена",
"doLogOutAllSessions": "Выйти из всех сессий",
"doRemove": "Удалить",
"doAdd": "Добавить",
"doSignOut": "Выход",
"editAccountHtmlTitle": "Изменение учетной записи",
"federatedIdentitiesHtmlTitle": "Федеративные идентификаторы",
"accountLogHtmlTitle": "Лог учетной записи",
"changePasswordHtmlTitle": "Смена пароля",
"sessionsHtmlTitle": "Сессии",
"accountManagementTitle": "Управление учетной записью",
"authenticatorTitle": "Аутентификатор",
"applicationsHtmlTitle": "Приложения",
"authenticatorCode": "Одноразовый код",
"email": "E-mail",
"firstName": "Имя",
"givenName": "Имя",
"fullName": "Полное имя",
"lastName": "Фамилия",
"familyName": "Фамилия",
"password": "Пароль",
"passwordConfirm": "Подтверждение пароля",
"passwordNew": "Новый пароль",
"username": "Имя пользователя",
"address": "Адрес",
"street": "Улица",
"locality": "Город",
"region": "Регион",
"postal_code": "Почтовый индекс",
"country": "Страна",
"emailVerified": "E-mail подтвержден",
"gssDelegationCredential": "Делегирование учетных данных через GSS",
"role_admin": "Администратор",
"role_realm-admin": "Администратор realm",
"role_create-realm": "Создать realm",
"role_view-realm": "Просмотр realm",
"role_view-users": "Просмотр пользователей",
"role_view-applications": "Просмотр приложений",
"role_view-clients": "Просмотр клиентов",
"role_view-events": "Просмотр событий",
"role_view-identity-providers": "Просмотр провайдеров учетных записей",
"role_manage-realm": "Управление realm",
"role_manage-users": "Управление пользователями",
"role_manage-applications": "Управление приложениями",
"role_manage-identity-providers": "Управление провайдерами учетных записей",
"role_manage-clients": "Управление клиентами",
"role_manage-events": "Управление событиями",
"role_view-profile": "Просмотр профиля",
"role_manage-account": "Управление учетной записью",
"role_read-token": "Чтение токена",
"role_offline-access": "Доступ оффлайн",
"role_uma_authorization": "Получение разрешений",
"client_account": "Учетная запись",
"client_security-admin-console": "Консоль администратора безопасности",
"client_admin-cli": "Командный интерфейс администратора",
"client_realm-management": "Управление Realm",
"client_broker": "Брокер",
"requiredFields": "Обязательные поля",
"allFieldsRequired": "Все поля обязательны",
"backToApplication": "&laquo; Назад в приложение",
"backTo": "Назад в {0}",
"date": "Дата",
"event": "Событие",
"ip": "IP",
"client": "Клиент",
"clients": "Клиенты",
"details": "Детали",
"started": "Начата",
"lastAccess": "Последний доступ",
"expires": "Истекает",
"applications": "Приложения",
"account": "Учетная запись",
"federatedIdentity": "Федеративный идентификатор",
"authenticator": "Аутентификатор",
"sessions": "Сессии",
"log": "Журнал",
"application": "Приложение",
"availablePermissions": "Доступные разрешения",
"grantedPermissions": "Согласованные разрешения",
"grantedPersonalInfo": "Согласованная персональная информация",
"additionalGrants": "Дополнительные согласования",
"action": "Действие",
"inResource": "в",
"fullAccess": "Полный доступ",
"offlineToken": "Оффлайн токен",
"revoke": "Отозвать согласование",
"configureAuthenticators": "Сконфигурированные аутентификаторы",
"mobile": "Мобильное приложение",
"totpStep1":
'Установите <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> или Google Authenticator. Оба приложения доступны на <a href="https://play.google.com">Google Play</a> и в Apple App Store.',
"totpStep2": "Откройте приложение и просканируйте баркод, либо введите ключ.",
"totpStep3": "Введите одноразовый код, выданный приложением, и нажмите сохранить для завершения установки.",
"missingUsernameMessage": "Введите имя пользователя.",
"missingFirstNameMessage": "Введите имя.",
"invalidEmailMessage": "Введите корректный E-mail.",
"missingLastNameMessage": "Введите фамилию.",
"missingEmailMessage": "Введите E-mail.",
"missingPasswordMessage": "Введите пароль.",
"notMatchPasswordMessage": "Пароли не совпадают.",
"missingTotpMessage": "Введите код аутентификатора.",
"invalidPasswordExistingMessage": "Существующий пароль неверный.",
"invalidPasswordConfirmMessage": "Подтверждение пароля не совпадает.",
"invalidTotpMessage": "Неверный код аутентификатора.",
"usernameExistsMessage": "Имя пользователя уже существует.",
"emailExistsMessage": "E-mail уже существует.",
"readOnlyUserMessage": "Вы не можете обновить информацию вашей учетной записи, т.к. она доступна только для чтения.",
"readOnlyPasswordMessage": "Вы не можете обновить пароль вашей учетной записи, т.к. он доступен только для чтения.",
"successTotpMessage": "Аутентификатор в мобильном приложении сконфигурирован.",
"successTotpRemovedMessage": "Аутентификатор в мобильном приложении удален.",
"successGrantRevokedMessage": "Согласование отозвано успешно.",
"accountUpdatedMessage": "Ваша учетная запись обновлена.",
"accountPasswordUpdatedMessage": "Ваш пароль обновлен.",
"missingIdentityProviderMessage": "Провайдер учетных записей не задан.",
"invalidFederatedIdentityActionMessage": "Некорректное или недопустимое действие.",
"identityProviderNotFoundMessage": "Заданный провайдер учетных записей не найден.",
"federatedIdentityLinkNotActiveMessage": "Идентификатор больше не активен.",
"federatedIdentityRemovingLastProviderMessage": "Вы не можете удалить последний федеративный идентификатор, т.к. Вы не имеете пароля.",
"identityProviderRedirectErrorMessage": "Ошибка перенаправления в провайдер учетных записей.",
"identityProviderRemovedMessage": "Провайдер учетных записей успешно удален.",
"identityProviderAlreadyLinkedMessage": "Федеративный идентификатор, возвращенный {0} уже используется другим пользователем.",
"staleCodeAccountMessage": "Страница устарела. Попробуйте еще раз.",
"consentDenied": "В согласовании отказано.",
"accountDisabledMessage": "Учетная запись заблокирована, обратитесь к администратору.",
"accountTemporarilyDisabledMessage": "Учетная запись временно заблокирована, обратитесь к администратору или попробуйте позже.",
"invalidPasswordMinLengthMessage": "Некорректный пароль: длина пароля должна быть не менее {0} символа(ов).",
"invalidPasswordMinLowerCaseCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} символа(ов) в нижнем регистре.",
"invalidPasswordMinDigitsMessage": "Некорректный пароль: пароль должен содержать не менее {0} цифр(ы).",
"invalidPasswordMinUpperCaseCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} символа(ов) в верхнем регистре.",
"invalidPasswordMinSpecialCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} спецсимвола(ов).",
"invalidPasswordNotUsernameMessage": "Некорректный пароль: пароль не должен совпадать с именем пользователя.",
"invalidPasswordRegexPatternMessage": "Некорректный пароль: пароль не удовлетворяет регулярному выражению.",
"invalidPasswordHistoryMessage": "Некорректный пароль: пароль не должен совпадать с последним(и) {0} паролями.",
"invalidPasswordGenericMessage": "Некорректный пароль: новый пароль не соответствует правилам пароля."
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,180 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Uložiť",
"doCancel": "Zrušiť",
"doLogOutAllSessions": "Odhlásenie všetkých relácií",
"doRemove": "Odstrániť",
"doAdd": "Pridať",
"doSignOut": "Odhlásiť",
"editAccountHtmlTitle": "Upraviť účet",
"federatedIdentitiesHtmlTitle": "Prepojená identita",
"accountLogHtmlTitle": "Denník zmien užívateľských účtov",
"changePasswordHtmlTitle": "Zmena hesla",
"sessionsHtmlTitle": "Relácie",
"accountManagementTitle": "Správa účtu Keycloak",
"authenticatorTitle": "Autentifikátor",
"applicationsHtmlTitle": "Aplikácie",
"authenticatorCode": "Jednorázový kód",
"email": "E-mail",
"firstName": "Meno",
"givenName": "Meno pri narodení",
"fullName": "Celé meno",
"lastName": "Priezvisko",
"familyName": "Rodné meno",
"password": "Heslo",
"passwordConfirm": "Potrvrdenie hesla",
"passwordNew": "Nové heslo",
"username": "Meno používateľa",
"address": "Adresa",
"street": "Ulica",
"locality": "Mesto alebo lokalita",
"region": "Kraj",
"postal_code": "PSČ",
"country": "Štát",
"emailVerified": "E-mail overený",
"gssDelegationCredential": "GSS delegované oprávnenie",
"role_admin": "Administrátor",
"role_realm-admin": "Administrátor realmu",
"role_create-realm": "Vytvoriť realm",
"role_view-realm": "Zobraziť realm",
"role_view-users": "Zobraziť používateľov",
"role_view-applications": "Zobraziť aplikácie",
"role_view-clients": "Zobraziť klientov",
"role_view-events": "Zobraziť udalosti",
"role_view-identity-providers": "Zobraziť klientov poskytovateľov identity",
"role_manage-realm": "Spravovať realm",
"role_manage-users": "Spravovať používateľov",
"role_manage-applications": "Spravovať aplikácie",
"role_manage-identity-providers": "Spravovať poskytovateľov identity",
"role_manage-clients": "Spravovať klientov",
"role_manage-events": "Spravovať udalosti",
"role_view-profile": "Zobraziť profil",
"role_manage-account": "Spravovať účet",
"role_manage-account-links": "Spravovať odkazy na účet",
"role_read-token": "Čítať token",
"role_offline-access": "Offline prístup",
"role_uma_authorization": "Autorizácia používateľom riadeného prístupu",
"client_account": "Účet klienta",
"client_security-admin-console": "Bezpečnostná administrátorská konzola",
"client_admin-cli": "Spravovať CLI klienta",
"client_realm-management": "Spravovať realmy klienta",
"client_broker": "Broker",
"requiredFields": "Povinné polia",
"allFieldsRequired": "Všetky požadované polia",
"backToApplication": "&laquo; Späť na aplikáciu",
"backTo": "Späť na {0}",
"date": "Dátum",
"event": "Udalosť",
"ip": "IP",
"client": "Klient",
"clients": "Klienti",
"details": "Podrobnosti",
"started": "Začíname",
"lastAccess": "Posledný prístup",
"expires": "Vyprší",
"applications": "Aplikácie",
"account": "Účet",
"federatedIdentity": "Prepojená identita",
"authenticator": "Autentifikátor",
"sessions": "Relácie",
"log": "Denník",
"application": "Aplikácia",
"availablePermissions": "Dostupné oprávnenia",
"grantedPermissions": "Pridelené oprávnenia",
"grantedPersonalInfo": "Poskytnuté osobné informácie",
"additionalGrants": "Dodatočné oprávnenia",
"action": "Akcia",
"inResource": "v",
"fullAccess": "Úplný prístup",
"offlineToken": "Offline token",
"revoke": "Zrušiť oprávnenie",
"configureAuthenticators": "Nakonfigurované autentifikátory",
"mobile": "Mobilný",
"totpStep1":
'Nainštalujte vo svojom zariadení <a href="https://freeotp.github.io/" target="_blank"> FreeOTP </a> alebo Google Authenticator. Obidve aplikácie sú k dispozícii v <a href="https://play.google.com"> Google Play </a> a Apple App Store.',
"totpStep2": "Otvorte aplikáciu a naskenujte čiarový kód alebo zadajte kľúč.",
"totpStep3": "Zadajte jednorazový kód poskytnutý aplikáciou a kliknutím na tlačidlo Uložiť dokončíte nastavenie.",
"totpManualStep2": "Otvorte aplikáciu a zadajte kľúč",
"totpManualStep3": "Použite nasledujúce hodnoty konfigurácie, ak aplikácia umožňuje ich nastavenie",
"totpUnableToScan": "Nemožno skenovať?",
"totpScanBarcode": "Skenovanie čiarového kódu?",
"totp.totp": "Založené na čase",
"totp.hotp": "Založené na počítadle",
"totpType": "Typ",
"totpAlgorithm": "Algoritmus",
"totpDigits": "Číslica",
"totpInterval": "Interval",
"totpCounter": "Počítadlo",
"missingUsernameMessage": "Zadajte používateľské meno.",
"missingFirstNameMessage": "Zadajte meno.",
"invalidEmailMessage": "Neplatná e-mailová adresa.",
"missingLastNameMessage": "Zadajte priezvisko.",
"missingEmailMessage": "Zadajte e-mail.",
"missingPasswordMessage": "Zadajte heslo, prosím.",
"notMatchPasswordMessage": "Heslá sa nezhodujú.",
"missingTotpMessage": "Zadajte jednorazový kód, prosím",
"invalidPasswordExistingMessage": "Neplatné existujúce heslo.",
"invalidPasswordConfirmMessage": "Potvrdenie hesla sa nezhoduje.",
"invalidTotpMessage": "Neplatný jednorazový kód.",
"usernameExistsMessage": "Užívateľské meno už existuje.",
"emailExistsMessage": "E-mail už existuje.",
"readOnlyUserMessage": "Váš účet nemôžete aktualizovať, pretože je iba na čítanie.",
"readOnlyUsernameMessage": "Nemôžete aktualizovať svoje používateľské meno, pretože je iba na čítanie.",
"readOnlyPasswordMessage": "Heslo nemôžete aktualizovať, pretože váš účet je iba na čítanie.",
"successTotpMessage": "Konfigurácia mobilného autentifikátora.",
"successTotpRemovedMessage": "Mobilný autentifikátor bol odstránený.",
"successGrantRevokedMessage": "Oprávnenie bolo úspešne zrušené.",
"accountUpdatedMessage": "Váš účet bol aktualizovaný.",
"accountPasswordUpdatedMessage": "Vaše heslo bolo aktualizované.",
"missingIdentityProviderMessage": "Poskytovateľ identity nie je zadaný.",
"invalidFederatedIdentityActionMessage": "Neplatná alebo chýbajúca akcia.",
"identityProviderNotFoundMessage": "Zadaný poskytovateľ identity nenájdený.",
"federatedIdentityLinkNotActiveMessage": "Identita už nie je aktívna.",
"federatedIdentityRemovingLastProviderMessage": "Nemôžete odstrániť poslednú spojenú identitu, pretože nemáte heslo.",
"identityProviderRedirectErrorMessage": "Nepodarilo sa presmerovať na poskytovateľa identity.",
"identityProviderRemovedMessage": "Poskytovateľ identity bol úspešne odstránený.",
"identityProviderAlreadyLinkedMessage": "Spojená identita vrátená {0} je už prepojená s iným používateľom.",
"staleCodeAccountMessage": "Platnosť vypršala. Skúste ešte raz.",
"consentDenied": "Súhlas bol zamietnutý.",
"accountDisabledMessage": "Účet je zakázaný, kontaktujte správcu.",
"accountTemporarilyDisabledMessage": "Účet je dočasne zakázaný, kontaktujte administrátora alebo skúste neskôr.",
"invalidPasswordMinLengthMessage": "Neplatné heslo: minimálna dĺžka {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Neplatné heslo: musí obsahovať minimálne {0} malé písmená.",
"invalidPasswordMinDigitsMessage": "Neplatné heslo: musí obsahovať aspoň {0} číslic.",
"invalidPasswordMinUpperCaseCharsMessage": "Neplatné heslo: musí obsahovať aspoň {0} veľké písmená.",
"invalidPasswordMinSpecialCharsMessage": "Neplatné heslo: musí obsahovať aspoň {0} špeciálne znaky.",
"invalidPasswordNotUsernameMessage": "Neplatné heslo: nesmie byť rovnaké ako používateľské meno.",
"invalidPasswordRegexPatternMessage": "Neplatné heslo: nezodpovedá regulárnemu výrazu.",
"invalidPasswordHistoryMessage": "Neplatné heslo: nesmie sa rovnať žiadnemu z posledných {0} hesiel.",
"invalidPasswordBlacklistedMessage": "Neplatné heslo: heslo je na čiernej listine.",
"invalidPasswordGenericMessage": "Neplatné heslo: nové heslo nezodpovedá pravidlám hesiel.",
"myResources": "Moje Zdroje",
"myResourcesSub": "Moje zdroje",
"doDeny": "Zakázať",
"doRevoke": "Odvolať",
"doApprove": "Schváliť",
"doRemoveSharing": "Odstránenie zdieľania",
"doRemoveRequest": "Odstrániť požiadavku",
"peopleAccessResource": "Ľudia s prístupom k tomuto zdroju",
"name": "Názov",
"scopes": "Rozsahy",
"resource": "Zdroj",
"user": "Používateľ",
"peopleSharingThisResource": "Ľudia zdieľajúci tento zdroj",
"shareWithOthers": "Zdieľať s ostatnými",
"needMyApproval": "Potrebuje môj súhlas",
"requestsWaitingApproval": "Vaše požiadavky čakajú na schválenie",
"icon": "Ikona",
"requestor": "Žiadateľ",
"owner": "Vlastník",
"resourcesSharedWithMe": "Zdroje zdieľané so mnou",
"permissionRequestion": "Žiadosti o povolenie",
"permission": "Oprávnenie",
"shares": "podiel (y)"
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,139 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Spara",
"doCancel": "Avbryt",
"doLogOutAllSessions": "Logga ut från samtliga sessioner",
"doRemove": "Ta bort",
"doAdd": "Lägg till",
"doSignOut": "Logga ut",
"editAccountHtmlTitle": "Redigera konto",
"federatedIdentitiesHtmlTitle": "Federerade identiteter",
"accountLogHtmlTitle": "Kontologg",
"changePasswordHtmlTitle": "Byt lösenord",
"sessionsHtmlTitle": "Sessioner",
"accountManagementTitle": "Kontohantering för Keycloak",
"authenticatorTitle": "Autentiserare",
"applicationsHtmlTitle": "Applikationer",
"authenticatorCode": "Engångskod",
"email": "E-post",
"firstName": "Förnamn",
"lastName": "Efternamn",
"password": "Lösenord",
"passwordConfirm": "Bekräftelse",
"passwordNew": "Nytt lösenord",
"username": "Användarnamn",
"address": "Adress",
"street": "Gata",
"locality": "Postort",
"region": "Stat, Provins eller Region",
"postal_code": "Postnummer",
"country": "Land",
"emailVerified": "E-post verifierad",
"gssDelegationCredential": "GSS Delegation Credential",
"role_admin": "Administratör",
"role_realm-admin": "Realm-administratör",
"role_create-realm": "Skapa realm",
"role_view-realm": "Visa realm",
"role_view-users": "Visa användare",
"role_view-applications": "Visa applikationer",
"role_view-clients": "Visa klienter",
"role_view-events": "Visa event",
"role_view-identity-providers": "Visa identitetsleverantörer",
"role_manage-realm": "Hantera realm",
"role_manage-users": "Hantera användare",
"role_manage-applications": "Hantera applikationer",
"role_manage-identity-providers": "Hantera identitetsleverantörer",
"role_manage-clients": "Hantera klienter",
"role_manage-events": "Hantera event",
"role_view-profile": "Visa profil",
"role_manage-account": "Hantera konto",
"role_read-token": "Läs element",
"role_offline-access": "Åtkomst offline",
"role_uma_authorization": "Erhåll tillstånd",
"client_account": "Konto",
"client_security-admin-console": "Säkerhetsadministratörskonsol",
"client_admin-cli": "Administratörs-CLI",
"client_realm-management": "Realmhantering",
"requiredFields": "Obligatoriska fält",
"allFieldsRequired": "Samtliga fält krävs",
"backToApplication": "&laquo; Tillbaka till applikationen",
"backTo": "Tillbaka till {0}",
"date": "Datum",
"event": "Event",
"ip": "IP",
"client": "Klient",
"clients": "Klienter",
"details": "Detaljer",
"started": "Startade",
"lastAccess": "Senast åtkomst",
"expires": "Upphör",
"applications": "Applikationer",
"account": "Konto",
"federatedIdentity": "Federerad identitet",
"authenticator": "Autentiserare",
"sessions": "Sessioner",
"log": "Logg",
"application": "Applikation",
"availablePermissions": "Tillgängliga rättigheter",
"grantedPermissions": "Beviljade rättigheter",
"grantedPersonalInfo": "Medgiven personlig information",
"additionalGrants": "Ytterligare medgivanden",
"action": "Åtgärd",
"inResource": "i",
"fullAccess": "Fullständig åtkomst",
"offlineToken": "Offline token",
"revoke": "Upphäv rättighet",
"configureAuthenticators": "Konfigurerade autentiserare",
"mobile": "Mobil",
"totpStep1":
'Installera <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> eller Google Authenticator på din enhet. Båda applikationerna finns tillgängliga på <a href="https://play.google.com">Google Play</a> och Apple App Store.',
"totpStep2": "Öppna applikationen och skanna streckkoden eller skriv i nyckeln.",
"totpStep3": "Fyll i engångskoden som tillhandahålls av applikationen och klicka på Spara för att avsluta inställningarna.",
"missingUsernameMessage": "Vänligen ange användarnamn.",
"missingFirstNameMessage": "Vänligen ange förnamn.",
"invalidEmailMessage": "Ogiltig e-postadress.",
"missingLastNameMessage": "Vänligen ange efternamn.",
"missingEmailMessage": "Vänligen ange e-post.",
"missingPasswordMessage": "Vänligen ange lösenord.",
"notMatchPasswordMessage": "Lösenorden matchar inte.",
"missingTotpMessage": "Vänligen ange autentiseringskoden.",
"invalidPasswordExistingMessage": "Det nuvarande lösenordet är ogiltigt.",
"invalidPasswordConfirmMessage": "Lösenordsbekräftelsen matchar inte.",
"invalidTotpMessage": "Autentiseringskoden är ogiltig.",
"usernameExistsMessage": "Användarnamnet finns redan.",
"emailExistsMessage": "E-posten finns redan.",
"readOnlyUserMessage": "Du kan inte uppdatera ditt konto eftersom det är skrivskyddat.",
"readOnlyPasswordMessage": "Du kan inte uppdatera ditt lösenord eftersom ditt konto är skrivskyddat.",
"successTotpMessage": "Mobilautentiseraren är inställd.",
"successTotpRemovedMessage": "Mobilautentiseraren är borttagen.",
"successGrantRevokedMessage": "Upphävandet av rättigheten lyckades.",
"accountUpdatedMessage": "Ditt konto har uppdaterats.",
"accountPasswordUpdatedMessage": "Ditt lösenord har uppdaterats.",
"missingIdentityProviderMessage": "Identitetsleverantör är inte angiven.",
"invalidFederatedIdentityActionMessage": "Åtgärden är ogiltig eller saknas.",
"identityProviderNotFoundMessage": "Angiven identitetsleverantör hittas inte.",
"federatedIdentityLinkNotActiveMessage": "Den här identiteten är inte längre aktiv.",
"federatedIdentityRemovingLastProviderMessage": "Du kan inte ta bort senaste federerade identiteten eftersom du inte har ett lösenord.",
"identityProviderRedirectErrorMessage": "Misslyckades med att omdirigera till identitetsleverantör.",
"identityProviderRemovedMessage": "Borttagningen av identitetsleverantören lyckades.",
"identityProviderAlreadyLinkedMessage": "Den federerade identiteten som returnerades av {0} är redan länkad till en annan användare.",
"staleCodeAccountMessage": "Sidan har upphört att gälla. Vänligen försök igen.",
"consentDenied": "Samtycket förnekades.",
"accountDisabledMessage": "Kontot är inaktiverat, kontakta administratör.",
"accountTemporarilyDisabledMessage": "Kontot är tillfälligt inaktiverat, kontakta administratör eller försök igen senare.",
"invalidPasswordMinLengthMessage": "Ogiltigt lösenord. Minsta längd är {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Ogiltigt lösenord: måste innehålla minst {0} små bokstäver.",
"invalidPasswordMinDigitsMessage": "Ogiltigt lösenord: måste innehålla minst {0} siffror.",
"invalidPasswordMinUpperCaseCharsMessage": "Ogiltigt lösenord: måste innehålla minst {0} stora bokstäver.",
"invalidPasswordMinSpecialCharsMessage": "Ogiltigt lösenord: måste innehålla minst {0} specialtecken.",
"invalidPasswordNotUsernameMessage": "Ogiltigt lösenord: Får inte vara samma som användarnamnet.",
"invalidPasswordRegexPatternMessage": "Ogiltigt lösenord: matchar inte kravet för lösenordsmönster.",
"invalidPasswordHistoryMessage": "Ogiltigt lösenord: Får inte vara samma som de senaste {0} lösenorden.",
"invalidPasswordGenericMessage": "Ogiltigt lösenord: Det nya lösenordet stämmer inte med lösenordspolicyn."
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,309 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Kaydet",
"doCancel": "İptal",
"doLogOutAllSessions": "Tüm Oturumları Kapat",
"doRemove": "Sil",
"doAdd": "Ekle",
"doSignOut": ıkış",
"doLogIn": "Oturum aç",
"doLink": "Bağlantı",
"editAccountHtmlTitle": "Hesabım",
"personalInfoHtmlTitle": "Kişisel bilgi",
"federatedIdentitiesHtmlTitle": "Değiştirilen Kimlikler",
"accountLogHtmlTitle": "Kullanıcı Logları",
"changePasswordHtmlTitle": "Şifre Değiştirme",
"deviceActivityHtmlTitle": "Cihaz Etkinliği",
"sessionsHtmlTitle": "Oturum",
"accountManagementTitle": "Keycloak Kullanıcı Hesabı Yönetimi",
"authenticatorTitle": "Kimlik Doğrulama",
"applicationsHtmlTitle": "Uygulama",
"linkedAccountsHtmlTitle": "Bağlantılı Hesaplar",
"accountManagementWelcomeMessage": "Keycloak Hesap Yönetimine Hoş Geldiniz",
"personalInfoIntroMessage": "Temel bilgilerinizi yönetin",
"accountSecurityTitle": "Hesap Güvenliği",
"accountSecurityIntroMessage": "Şifrenizi ve hesap erişiminizi kontrol edin",
"applicationsIntroMessage": "Hesabınıza erişmek için uygulama izninizi takip edin ve yönetin",
"resourceIntroMessage": "Kaynaklarınızı ekip üyeleri arasında paylaşın",
"passwordLastUpdateMessage": "Şifreniz güncellendi",
"updatePasswordTitle": "Şifre güncelle",
"updatePasswordMessageTitle": "Güçlü bir şifre seçtiğinizden emin olun",
"updatePasswordMessage":
"Güçlü bir şifre, sayılar, harfler ve sembollerin karışımından oluşmalıdır. Tahmin etmesi zor ve gerçek bir kelimeye benzemeyen şifre sadece bu hesap için kullanılır.",
"personalSubTitle": "Kişisel Bilgileriniz",
"personalSubMessage": "Bu temel bilgileri yönetin: adınız, soyadınız ve e-posta adresiniz",
"authenticatorCode": "Kimlik Doğrulama Kodu",
"email": "E-Mail",
"firstName": "Ad",
"givenName": "Ad",
"fullName": "Ad Soyad",
"lastName": "Soyad",
"familyName": "Soyad",
"password": "Şifre",
"currentPassword": "Şimdiki Şifre",
"passwordConfirm": "Şifre Doğrulama",
"passwordNew": "Yeni Şifre",
"username": "Kullanıcı Adı",
"address": "Adres",
"street": "Cadde",
"region": "Bölge",
"postal_code": "Posta Kodu",
"locality": "Şehir",
"country": "Ülke",
"emailVerified": "E-Mail Doğrulandı",
"gssDelegationCredential": "GSS Yetki Bilgisi",
"profileScopeConsentText": "Kullanıcı profili",
"emailScopeConsentText": "Email adresi",
"addressScopeConsentText": "Adres",
"phoneScopeConsentText": "Telefon numarası",
"offlineAccessScopeConsentText": "Çevrimdışı Erişim",
"samlRoleListScopeConsentText": "Rollerim",
"rolesScopeConsentText": "Kullanıcı rolleri",
"role_admin": "Admin",
"role_realm-admin": "Realm Admin",
"role_create-realm": "Realm Oluştur",
"role_view-realm": "Realm görüntüle",
"role_view-users": "Kullanıcıları görüntüle",
"role_view-applications": "Uygulamaları görüntüle",
"role_view-clients": "İstemci görüntüle",
"role_view-events": "Olay görüntüle",
"role_view-identity-providers": "Kimlik Sağlayıcılar",
"role_manage-realm": "Realm yönet",
"role_manage-users": "Kullanıcıları yönet",
"role_manage-applications": "Uygulamaları yönet",
"role_manage-identity-providers": "Kimlik Sağlayıcıları Yönet",
"role_manage-clients": "İstemci yönet",
"role_manage-events": "Olay yönet",
"role_view-profile": "Profilleri görüntüle",
"role_manage-account": "Profilleri Yönet",
"role_manage-account-links": "Profil bağlantılarını yönet",
"role_read-token": "Token oku",
"role_offline-access": "Çevirimdışı Yetki",
"role_uma_authorization": "İzinleri Al",
"client_account": "Müşteri Hesabı",
"client_security-admin-console": "Güvenlik Yönetici Konsolu",
"client_admin-cli": "Admin CLI",
"client_realm-management": "Realm-Management",
"client_broker": "Broker",
"requiredFields": "Zorunlu Alanlar",
"allFieldsRequired": "Tüm Alanlar Zorunlu",
"backToApplication": "&laquo; Uygulamaya Dön",
"backTo": "Geri Dön {0}",
"date": "Gün",
"event": "Olay",
"ip": "IP",
"client": "İstemci",
"clients": "İstemciler",
"details": "Detaylar",
"started": "Başlangıç Tarihi",
"lastAccess": "Son Erişim Tarihi",
"expires": "Son Kullanma Tarihi",
"applications": "Uygulama",
"account": "Hesap",
"federatedIdentity": "Federal Kimlik",
"authenticator": "Kimlik Doğrulama",
"device-activity": "Cihaz Etkinliği",
"sessions": "Oturum",
"log": "Log",
"application": "Uygulama",
"availablePermissions": "Kullanılabilir İzinler",
"availableRoles": "Kullanılabilir Roller",
"grantedPermissions": "Verilen İzinler",
"grantedPersonalInfo": "İzin Verilen Kişisel Bilgiler",
"additionalGrants": "Ek İzinler",
"action": "Aksiyon",
"inResource": "Kaynak",
"fullAccess": "Tam Yetki",
"offlineToken": "Çevirimdışı-Token",
"revoke": "İzni İptal et",
"configureAuthenticators": "Çoklu Kimlik Doğrulama",
"mobile": "Mobil",
"totpStep1": "Akıllı Telefonunuza aşağıdaki uygulamalardan birini yükleyin:",
"totpStep2": "Uygulamayıın ve barkodu okutun.",
"totpStep3": "Uygulama tarafından oluşturulan tek seferlik kodu girin ve Kaydet'i tıklayın.",
"totpManualStep2": "Uygulamayıın ve aşağıdaki anahtarı girin.",
"totpManualStep3": "Bunları uygulama için özelleştirebilirseniz aşağıdaki yapılandırma değerlerini kullanın:",
"totpUnableToScan": "Barkodu tarayamıyor musunuz?",
"totpScanBarcode": "Barkod Tara?",
"totp.totp": "Zaman bazlı (time-based)",
"totp.hotp": "Sayaç tabanlı (counter-based)",
"totpType": "Tip",
"totpAlgorithm": "Algoritma",
"totpDigits": "Basamak",
"totpInterval": "Aralık",
"totpCounter": "Sayaç",
"missingUsernameMessage": "Lütfen bir kullanıcı adı giriniz.",
"missingFirstNameMessage": "Lütfen bir ad girin.",
"invalidEmailMessage": "Geçersiz e-posta adresi.",
"missingLastNameMessage": "Lütfen bir soyadı giriniz.",
"missingEmailMessage": "Lütfen bir e-mail adresi giriniz.",
"missingPasswordMessage": "Lütfen bir şifre giriniz.",
"notMatchPasswordMessage": "Şifreler aynı değil.",
"missingTotpMessage": "Lütfen tek seferlik kodu girin.",
"invalidPasswordExistingMessage": "Mevcut şifre geçersiz.",
"invalidPasswordConfirmMessage": "Şifre onayı aynı değil.",
"invalidTotpMessage": "Geçersiz tek seferlik kod.",
"usernameExistsMessage": "Kullanıcı adı zaten mevcut.",
"emailExistsMessage": "E-posta adresi zaten mevcut.",
"readOnlyUserMessage": "Yazma korumalı olduğundan kullanıcı hesabınızı değiştiremezsiniz.",
"readOnlyUsernameMessage": "Yazma korumalı olduğundan kullanıcı adınızı değiştiremezsiniz.",
"readOnlyPasswordMessage": "Yazma korumalı olduğundan şifrenizi değiştiremezsiniz.",
"successTotpMessage": "Çoklu kimlik doğrulaması başarıyla yapılandırıldı.",
"successTotpRemovedMessage": "Çoklu kimlik doğrulama başarıyla kaldırıldı.",
"successGrantRevokedMessage": "İzin başarıyla iptal edildi.",
"accountUpdatedMessage": "Kullanıcı hesabınız güncellendi.",
"accountPasswordUpdatedMessage": "Şifreniz güncellendi.",
"missingIdentityProviderMessage": "Kimlik Sağlayıcısı belirtilmemiş.",
"invalidFederatedIdentityActionMessage": "Geçersiz veya eksik eylem.",
"identityProviderNotFoundMessage": "Belirtilen Kimlik Sağlayıcı bulunamadı.",
"federatedIdentityLinkNotActiveMessage": "Bu kimlik artık aktif değil.",
"federatedIdentityRemovingLastProviderMessage": "Şifreniz olmadığı için son girişi kaldıramazsınız.",
"identityProviderRedirectErrorMessage": "Kimlik sağlayıcıya iletilirken hata oluştu.",
"identityProviderRemovedMessage": "Kimlik Sağlayıcısı başarıyla kaldırıldı.",
"identityProviderAlreadyLinkedMessage": "Değiştirilmiş {0} kimliği başka bir kullanıcıya atanmış.",
"staleCodeAccountMessage": "Bu sayfa artık geçerli değil, lütfen tekrar deneyin.",
"consentDenied": "Onay reddedildi.",
"accountDisabledMessage": "Hesabınız kilitlendi, lütfen yöneticiyle iletişime geçin.",
"accountTemporarilyDisabledMessage": "Hesabınız geçici olarak kilitlendi, lütfen yöneticiyle iletişime geçin veya daha sonra tekrar deneyin.",
"invalidPasswordMinLengthMessage": "Geçersiz Şifre: En az {0} karakter uzunluğunda olmalı.",
"invalidPasswordMinLowerCaseCharsMessage": "Geçersiz Şifre : En az {0} küçük harf içermelidir.",
"invalidPasswordMinDigitsMessage": "Geçersiz Şifre: En az {0} sayı(lar) içermelidir.",
"invalidPasswordMinUpperCaseCharsMessage": "Geçersiz Şifre: En az {0} büyük harf içermelidir.",
"invalidPasswordMinSpecialCharsMessage": "Geçersiz Şifre: En az {0} özel karakter içermelidir.",
"invalidPasswordNotUsernameMessage": "Geçersiz Şifre: Kullanıcı adıyla aynı olamaz.",
"invalidPasswordRegexPatternMessage": "Geçersiz Şifre: Regex Patternine uygun değil.",
"invalidPasswordHistoryMessage": "Geçersiz Şifre: Son {0} şifreden biri olamaz.",
"invalidPasswordBlacklistedMessage": "Geçersiz Şifre: Şifre bloklanmış şifreler listesindedir (kara liste).",
"invalidPasswordGenericMessge": "Geçersiz Şifre: Yeni şifre, şifre kurallarını ihlal ediyor.",
"myResources": "Kaynaklarım",
"myResourcesSub": "Kaynaklarım",
"doDeny": "Reddet",
"doRevoke": "Geri al",
"doApprove": "Onayla",
"doRemoveSharing": "Paylaşımı Kaldır",
"doRemoveRequest": "İsteği Kaldır",
"peopleAccessResource": "Bu kaynağa erişimi olan kişiler",
"resourceManagedPolicies": "Bu kaynağa erişim izni veren izinler",
"resourceNoPermissionsGrantingAccess": "Bu kaynağa erişim izni verilmeyen izin yok",
"anyAction": "Herhangi bir eylem",
"description": "Açıklama",
"name": "İsim",
"scopes": "Kapsam",
"resource": "Kaynak",
"user": "Kullanıcı",
"peopleSharingThisResource": "Bu kaynağı paylaşan kullanıcılar",
"shareWithOthers": "Başkalarıyla paylaş",
"needMyApproval": "Onayım gerekli",
"requestsWaitingApproval": "Talepleriniz onay bekliyor",
"icon": "Icon",
"requestor": "Talep eden",
"owner": "Sahip",
"resourcesSharedWithMe": "Kaynaklar benimle paylaşıldı",
"permissionRequestion": "İzin Talepleri",
"permission": "İzin",
"shares": "Paylaşım(lar)",
"locale_ca": "Katalanca",
"locale_de": "Almanca",
"locale_en": "İngilizce",
"locale_es": "İspanyolca",
"locale_fr": "Fransızca",
"locale_it": "İtalyanca",
"locale_ja": "Japonca",
"locale_nl": "Felemenkçe",
"locale_no": "Norveçce",
"locale_pl": "Lehçe",
"locale_pt_BR": "Portekizce",
"locale_pt-BR": "Portekizce",
"locale_ru": "Rusça",
"locale_lt": "Litvanca",
"locale_zh-CN": "Çince",
"locale_sk": "Slovakça",
"locale_sv": "İsveççe",
"locale_tr": "Türkçe",
"applicaitonName": "İsim",
"applicationType": "Uygulama Tipi",
"applicationInUse": "Yalnızca uygulama içi kullanım",
"clearAllFilter": "Tüm filtreleri temizle",
"activeFilters": "Aktif filtreler",
"filterByName": "İsme Göre Filtrele ...",
"allApps": "Bütün uygulamalar",
"internalApps": "İç uygulamalar",
"thirdpartyApps": "Üçüncü parti uygulamalar",
"appResults": "Sonuçlar",
"authorizedProvider": "Yetkili Tedarikçi",
"authorizedProviderMessage": "Yetkili Sağlayıcılar hesabınızla bağlantılı",
"identityProvider": "Kimlik Sağlayıcısı",
"identityProviderMessage": "Hesabınızı yapılandırdığınız kimlik sağlayıcılarıyla bağlamak için",
"socialLogin": "Sosyal Giriş",
"userDefined": "Kullanıcı tanımlı",
"removeAccess": "Erişimi Kaldır",
"removeAccessMessage": "Bu uygulama hesabını kullanmak istiyorsanız tekrar erişim vermeniz gerekir.",
"authenticatorStatusMessage": "İki faktörlü kimlik doğrulama aktif",
"authenticatorFinishSetUpTitle": "İki Faktörlü Doğrulama",
"authenticatorFinishSetUpMessage": "Keycloak hesabınızda her oturum açtığınızda, iki faktörlü bir doğrulama kodu girmeniz istenecektir.",
"authenticatorSubTitle": "İki Faktörlü Kimlik Doğrulamayı Ayarlama",
"authenticatorSubMessage":
"Hesabınızın güvenliğini artırmak için mevcut iki faktörlü kimlik doğrulama yöntemlerinden en az birini etkinleştirin.",
"authenticatorMobileTitle": "Mobil Kimlik Doğrulayıcı",
"authenticatorMobileMessage": "Doğrulama kodlarını iki faktörlü kimlik doğrulama olarak almak için mobil Doğrulayıcı'yı kullanın.",
"authenticatorMobileFinishSetUpMessage": "Doğrulayıcı, telefonunuza bağlı.",
"authenticatorActionSetup": "Kur",
"authenticatorSMSTitle": "SMS Kodu",
"authenticatorSMSMessage": "Keycloak, doğrulama kodunu telefonunuza iki faktörlü kimlik doğrulaması olarak gönderecektir.",
"authenticatorSMSFinishSetUpMessage": "Kısa mesajlar gönderilir",
"authenticatorDefaultStatus": "Varsayılan",
"authenticatorChangePhone": "Telefon Numarasını Değiştir",
"authenticatorBackupCodesTitle": "Yedekleme Kodları",
"authenticatorBackupCodesMessage": "8 haneli yedek kodlarınızı alın",
"authenticatorBackupCodesFinishSetUpMessage": "Şu anda 12 haneli yedek kod oluşturuldu. Her biri bir kez kullanılabilir.",
"authenticatorMobileSetupTitle": "Mobil Kimlik Doğrulama Kurulumu",
"smscodeIntroMessage": "Telefon numaranızı girin ve telefonunuza bir doğrulama kodu gönderilecektir.",
"mobileSetupStep1": "Telefonunuza bir kimlik doğrulama uygulaması yükleyin. Burada listelenen uygulamalar desteklenmektedir.",
"mobileSetupStep2": "Uygulamayıın ve barkodu tarayın.",
"mobileSetupStep3": "Uygulama tarafından sağlanan tek seferlik kodu girin ve kurulumu tamamlamak için Kaydet'e tıklayın.",
"scanBarCode": "Barkodu taramak ister misiniz?",
"enterBarCode": "Tek seferlik kodu girin",
"doCopy": "Kopyala",
"doFinish": "Bitir",
"authenticatorSMSCodeSetupTitle": "SMS Kodu Kurulumu",
"chooseYourCountry": "Ülkenizi seçin",
"enterYourPhoneNumber": "Telefon numaranızı girin",
"sendVerficationCode": "Doğrulama kodu Gönder",
"enterYourVerficationCode": "Onaylama kodunu girin",
"authenticatorBackupCodesSetupTitle": "Yedekleme Kodları Kurulumu",
"backupcodesIntroMessage":
"Telefonunuza erişimi kaybederseniz, yine de yedek kodlar aracılığıyla hesabınıza giriş yapabilirsiniz. Onları güvenli ve erişilebilir bir yerde saklayın.",
"realmName": "Realm",
"doDownload": "İndir",
"doPrint": "Yazdır",
"backupCodesTips-1": "Her yedek kod bir kez kullanılabilir.",
"backupCodesTips-2": "Bu kodlar üzerinde oluşturuldu",
"generateNewBackupCodes": "Yeni Yedekleme Kodları Oluştur",
"backupCodesTips-3": "Yeni yedek kodlar oluşturduğunuzda, mevcut kodlar artık çalışmayacaktır.",
"backtoAuthenticatorPage": "Kimlik Doğrulayıcı Sayfasına Geri Dön",
"resources": "Kaynaklar",
"sharedwithMe": "Benimle paylaştı",
"share": "Paylaşım",
"sharedwith": "İle paylaştı",
"accessPermissions": "Erişim İzinleri",
"permissionRequests": "İzin İstekleri",
"approve": "Onayla",
"approveAll": "Tümünü onayla",
"people": "İnsanlar",
"perPage": "Sayfa başına",
"currentPage": "Geçerli sayfa",
"sharetheResource": "Kaynağı paylaş",
"group": "Grup",
"selectPermission": "İzin Seç",
"addPeople": "Kaynağınızı paylaşmak için kullanıcı ekleyin",
"addTeam": "Kaynağınızı paylaşmak için ekip ekleyin",
"myPermissions": "İzinlerim",
"waitingforApproval": "Onay bekleniyor",
"anyPermission": "Herhangi bir izin"
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,155 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "保存",
"doCancel": "取消",
"doLogOutAllSessions": "登出所有会话",
"doRemove": "删除",
"doAdd": "添加",
"doSignOut": "登出",
"editAccountHtmlTitle": "编辑账户",
"federatedIdentitiesHtmlTitle": "链接的身份",
"accountLogHtmlTitle": "账户日志",
"changePasswordHtmlTitle": "更改密码",
"sessionsHtmlTitle": "会话",
"accountManagementTitle": "Keycloak账户管理",
"authenticatorTitle": "认证者",
"applicationsHtmlTitle": "应用",
"authenticatorCode": "一次性认证码",
"email": "电子邮件",
"firstName": "名",
"givenName": "姓",
"fullName": "全名",
"lastName": "姓",
"familyName": "姓",
"password": "密码",
"passwordConfirm": "确认",
"passwordNew": "新密码",
"username": "用户名",
"address": "地址",
"street": "街道",
"locality": "城市住所",
"region": "省,自治区,直辖市",
"postal_code": "邮政编码",
"country": "国家",
"emailVerified": "验证过的Email",
"gssDelegationCredential": "GSS Delegation Credential",
"role_admin": "管理员",
"role_realm-admin": "域管理员",
"role_create-realm": "创建域",
"role_view-realm": "查看域",
"role_view-users": "查看用户",
"role_view-applications": "查看应用",
"role_view-clients": "查看客户",
"role_view-events": "查看事件",
"role_view-identity-providers": "查看身份提供者",
"role_manage-realm": "管理域",
"role_manage-users": "管理用户",
"role_manage-applications": "管理应用",
"role_manage-identity-providers": "管理身份提供者",
"role_manage-clients": "管理客户",
"role_manage-events": "管理事件",
"role_view-profile": "查看用户信息",
"role_manage-account": "管理账户",
"role_read-token": "读取 token",
"role_offline-access": "离线访问",
"role_uma_authorization": "获取授权",
"client_account": "账户",
"client_security-admin-console": "安全管理终端",
"client_admin-cli": "管理命令行",
"client_realm-management": "域管理",
"client_broker": "代理",
"requiredFields": "必填项",
"allFieldsRequired": "所有项必填",
"backToApplication": "« 回到应用",
"backTo": "回到 {0}",
"date": "日期",
"event": "事件",
"ip": "IP",
"client": "客户端",
"clients": "客户端",
"details": "详情",
"started": "开始",
"lastAccess": "最后一次访问",
"expires": "过期时间",
"applications": "应用",
"account": "账户",
"federatedIdentity": "关联身份",
"authenticator": "认证方",
"sessions": "会话",
"log": "日志",
"application": "应用",
"availablePermissions": "可用权限",
"grantedPermissions": "授予权限",
"grantedPersonalInfo": "授权的个人信息",
"additionalGrants": "可授予的权限",
"action": "操作",
"inResource": "in",
"fullAccess": "所有权限",
"offlineToken": "离线 token",
"revoke": "收回授权",
"configureAuthenticators": "配置的认证者",
"mobile": "手机",
"totpStep1":
'在你的设备上安装 <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> 或者 Google Authenticator.两个应用可以从 <a href="https://play.google.com">Google Play</a> 和 Apple App Store下载。',
"totpStep2": "打开应用扫描二维码输入验证码",
"totpStep3": "输入应用提供的一次性验证码单击保存",
"missingUsernameMessage": "请指定用户名",
"missingFirstNameMessage": "请指定名",
"invalidEmailMessage": "无效的电子邮箱地址",
"missingLastNameMessage": "请指定姓",
"missingEmailMessage": "请指定邮件地址",
"missingPasswordMessage": "请输入密码",
"notMatchPasswordMessage": "密码不匹配",
"missingTotpMessage": "请指定认证者代码",
"invalidPasswordExistingMessage": "无效的旧密码",
"invalidPasswordConfirmMessage": "确认密码不相符",
"invalidTotpMessage": "无效的认证码",
"usernameExistsMessage": "用户名已经存在",
"emailExistsMessage": "电子邮箱已经存在",
"readOnlyUserMessage": "无法修改账户,因为它是只读的。",
"readOnlyPasswordMessage": "不可以更该账户因为它是只读的。",
"successTotpMessage": "手机认证者配置完毕",
"successTotpRemovedMessage": "手机认证者已删除",
"successGrantRevokedMessage": "授权成功回收",
"accountUpdatedMessage": "您的账户已经更新",
"accountPasswordUpdatedMessage": "您的密码已经修改",
"missingIdentityProviderMessage": "身份提供者未指定",
"invalidFederatedIdentityActionMessage": "无效或者缺少操作",
"identityProviderNotFoundMessage": "指定的身份提供者未找到",
"federatedIdentityLinkNotActiveMessage": "这个身份不再使用了。",
"federatedIdentityRemovingLastProviderMessage": "你不可以移除最后一个身份提供者因为你没有设置密码",
"identityProviderRedirectErrorMessage": "尝试重定向到身份提供商失败",
"identityProviderRemovedMessage": "身份提供商成功删除",
"identityProviderAlreadyLinkedMessage": "链接的身份 {0} 已经连接到已有用户。",
"staleCodeAccountMessage": "页面过期。请再试一次。",
"consentDenied": "不同意",
"accountDisabledMessage": "账户已经关闭,请联系管理员",
"accountTemporarilyDisabledMessage": "账户暂时关闭,请联系管理员或稍后再试。",
"invalidPasswordMinLengthMessage": "无效的密码:最短长度 {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "无效的密码: 至少包含 {0} 小写字母。",
"invalidPasswordMinDigitsMessage": "无效的密码: 至少包含 {0} 数字。",
"invalidPasswordMinUpperCaseCharsMessage": "无效的密码: 至少包含 {0} 大写字母",
"invalidPasswordMinSpecialCharsMessage": "无效的密码: 至少包含 {0} 个特殊字符",
"invalidPasswordNotUsernameMessage": "无效的密码: 不能与用户名相同",
"invalidPasswordRegexPatternMessage": "无效的密码: 无法与正则表达式匹配",
"invalidPasswordHistoryMessage": "无效的密码: 不能与之前的{0} 个旧密码相同",
"locale_ca": "Català",
"locale_de": "Deutsch",
"locale_en": "English",
"locale_es": "Español",
"locale_fr": "Français",
"locale_it": "Italian",
"locale_ja": "日本語",
"locale_nl": "Nederlands",
"locale_no": "Norsk",
"locale_lt": "Lietuvių",
"locale_pt-BR": "Português (Brasil)",
"locale_ru": "Русский",
"locale_zh-CN": "中文简体"
};
export default messages;
/* spell-checker: enable */

View File

@ -0,0 +1,17 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"invalidPasswordHistoryMessage": "Contrasenya incorrecta: no pot ser igual a cap de les últimes {0} contrasenyes.",
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
"invalidPasswordMinLengthMessage": "Contrasenya incorrecta: longitud mínima {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres minúscules.",
"invalidPasswordMinSpecialCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} caràcters especials.",
"invalidPasswordMinUpperCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres majúscules.",
"invalidPasswordNotUsernameMessage": "Contrasenya incorrecta: no pot ser igual al nom d'usuari.",
"invalidPasswordRegexPatternMessage": "Contrasenya incorrecta: no compleix l'expressió regular."
};
export default messages;
/* spell-checker: enable */

Some files were not shown because too many files have changed in this diff Show More