diff --git a/backend/internal/bootstrap/app_images_bootstrap.go b/backend/internal/bootstrap/app_images_bootstrap.go index 76729083..1c0ffa8c 100644 --- a/backend/internal/bootstrap/app_images_bootstrap.go +++ b/backend/internal/bootstrap/app_images_bootstrap.go @@ -44,6 +44,7 @@ func initApplicationImages(ctx context.Context, fileStorage storage.FileStorage) }, "background.webp": { mustDecodeHex("3fc436a66d6b872b01d96a4e75046c46b5c3e2daccd51e98ecdf98fd445599ab"), + mustDecodeHex("fc9ea4a7d9ac36321d9d5a34e7f0c346f5cb47254e1ee8fb75aac8b58db04230"), }, } diff --git a/backend/resources/images/background.avif b/backend/resources/images/background.avif new file mode 100644 index 00000000..204d8f58 Binary files /dev/null and b/backend/resources/images/background.avif differ diff --git a/backend/resources/images/background.webp b/backend/resources/images/background.webp deleted file mode 100644 index 6c48fa1f..00000000 Binary files a/backend/resources/images/background.webp and /dev/null differ diff --git a/frontend/messages/en.json b/frontend/messages/en.json index f234bb3c..d4578c0d 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -1,7 +1,6 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", - "my_account": "My Account", - "logout": "Logout", + "my_account": "My account", "confirm": "Confirm", "docs": "Docs", "key": "Key", @@ -11,29 +10,19 @@ "add_custom_claim": "Add custom claim", "add_another": "Add another", "select_a_date": "Select a date", - "select_file": "Select File", - "profile_picture": "Profile Picture", - "profile_picture_is_managed_by_ldap_server": "The profile picture is managed by the LDAP server and cannot be changed here.", - "click_profile_picture_to_upload_custom": "Click on the profile picture to upload a custom one from your files.", - "image_should_be_in_format": "The image should be in PNG, JPEG or WEBP format.", + "profile_picture": "Profile picture", "no_items_found": "No items found", "select_items": "Select items...", "search": "Search...", - "expand_card": "Expand card", "copied": "Copied", "something_went_wrong": "Something went wrong", "go_back_to_home": "Go back to home", - "alternative_sign_in_methods": "Alternative Sign In Methods", - "login_background": "Login background", "logo": "Logo", - "login_code": "Login Code", - "create_a_login_code_to_sign_in_without_a_passkey_once": "Create a login code that the user can use to sign in without a passkey once.", "one_hour": "1 hour", "twelve_hours": "12 hours", "one_day": "1 day", "one_week": "1 week", "one_month": "1 month", - "expiration": "Expiration", "name": "Name", "an_unknown_error_occurred": "An unknown error occurred", "authentication_process_was_aborted": "Passkey verification was canceled", @@ -53,84 +42,42 @@ "passkey_user_verification_required": "Your passkey couldn't verify you. If you're using a security key, configure a FIDO2 PIN and try again", "critical_error_occurred_contact_administrator": "A critical error occurred. Please contact your administrator.", "sign_in_to": "Sign in to {name}", - "account_selection_signin_confirmation": "Do you want to use the following account to continue to {#b}{name}{/b}?", "use_a_different_account": "Use a different account", - "client_wants_to_access_the_following_information": "{#b}{client}{/b} wants to access the following information:", - "do_you_want_to_sign_in_to_client_with_your_app_name_account": "Do you want to sign in to {#b}{client}{/b} with your {appName} account?", "email": "Email", "groups": "Groups", "cancel": "Cancel", "sign_in": "Sign in", "try_again": "Try again", - "client_logo": "Client Logo", "sign_out": "Sign out", - "do_you_want_to_sign_out_of_pocketid_with_the_account": "Do you want to sign out of {appName} with the account {#b}{username}{/b}?", "sign_in_to_appname": "Sign in to {appName}", "please_try_to_sign_in_again": "Please try to sign in again.", - "authenticate_with_passkey_to_access_account": "Authenticate yourself with your passkey to access your account.", - "authenticate": "Authenticate", "please_try_again": "Please try again.", - "alternative_sign_in": "Alternative Sign In", - "if_you_do_not_have_access_to_your_passkey_you_can_sign_in_using_one_of_the_following_methods": "If you don't have access to your passkey, you can sign in using one of the following methods.", - "use_your_passkey_instead": "Use your passkey instead?", - "email_login": "Email Login", - "enter_a_login_code_to_sign_in": "Enter a login code to sign in.", - "request_a_login_code_via_email": "Request a login code via email.", "go_back": "Go back", - "an_email_has_been_sent_to_the_provided_email_if_it_exists_in_the_system": "An email has been sent to the provided email, if it exists in the system.", - "enter_code": "Enter code", - "enter_your_email_address_to_receive_an_email_with_a_login_code": "Enter your email address to receive an email with a login code.", - "your_email": "Your email", - "submit": "Submit", - "enter_the_code_you_received_to_sign_in": "Enter the code you received to sign in.", "code": "Code", "invalid_redirect_url": "Invalid redirect URL", - "audit_log": "Audit Log", + "audit_log": "Audit log", "users": "Users", - "user_groups": "User Groups", - "oidc_clients": "OIDC Clients", - "api_keys": "API Keys", - "application_configuration": "Application Configuration", - "sqlite_storage_warning": "We detected that your SQLite database is stored on a network-attached drive like NFS, SMB, or FUSE. This is unsupported and can lead to your database getting corrupted. {#link href=|https://pocket-id.org/docs/configuration/environment-variables#sqlite|}See docs.{/link}", - "see_your_recent_account_activities": "See your account activities within the configured retention period.", + "oidc_clients": "OIDC clients", + "api_keys": "API keys", + "sqlite_network_storage_warning": "We detected that your SQLite database is stored on a network-attached drive like NFS, SMB, or FUSE. This is unsupported and can lead to your database getting corrupted.", "time": "Time", "event": "Event", "approximate_location": "Approximate Location", "ip_address": "IP Address", "device": "Device", "client": "Client", - "actor": "Actor", "unknown": "Unknown", "profile_picture_updated_successfully": "Profile picture updated successfully. It may take a few minutes to update.", - "account_settings": "Account Settings", - "passkey_missing": "Passkey missing", - "please_provide_a_passkey_to_prevent_losing_access_to_your_account": "Please add a passkey to prevent losing access to your account.", - "single_passkey_configured": "Single Passkey Configured", - "it_is_recommended_to_add_more_than_one_passkey": "It is recommended to add more than one passkey to avoid losing access to your account.", - "account_details": "Account Details", "passkeys": "Passkeys", - "configure_passkey_settings": "Control which passkeys users can register and how they verify themselves.", "user_verification": "User verification", - "user_verification_description": "Choose whether a biometric or PIN is required when registering or using a passkey.", "user_verification_required": "Required", - "user_verification_required_description": "Require biometric or PIN verification. Passkeys that cannot verify the user will not work.", "user_verification_preferred": "Preferred", - "user_verification_preferred_description": "Use biometric or PIN verification when available, but allow touch-only security keys.", "allow_synced_passkeys": "Allow synced passkeys", - "allow_synced_passkeys_description": "Allow new passkeys that can be backed up and used across multiple devices.", "allowed_authenticator_type": "Allowed authenticator type", - "allowed_authenticator_type_description": "Choose which type of authenticator users can use when registering a new passkey.", "any_authenticator": "Any passkey", - "any_authenticator_description": "Allow both device passkeys and external security keys.", "device_passkeys_only": "Device passkeys only", - "device_passkeys_only_description": "Allow only passkeys built into the user's device.", - "external_security_keys_only": "External security keys only", - "external_security_keys_only_description": "Allow only roaming authenticators such as USB or NFC security keys.", "synced_passkeys_not_allowed": "Synced passkeys are not allowed by your administrator", - "manage_your_passkeys_that_you_can_use_to_authenticate_yourself": "Manage your passkeys that you can use to authenticate yourself.", - "manage_this_users_passkeys": "Manage this user's passkeys.", - "add_passkey": "Add Passkey", - "create": "Create", + "add_passkey": "Add passkey", "first_name": "First name", "last_name": "Last name", "username": "Username", @@ -145,194 +92,68 @@ "passkey_deleted_successfully": "Passkey deleted successfully", "delete_passkey_name": "Delete {passkeyName}", "passkey_name_updated_successfully": "Passkey name updated successfully", - "name_passkey": "Name Passkey", + "name_passkey": "Name passkey", "name_your_passkey_to_easily_identify_it_later": "Name your passkey to easily identify it later.", - "create_api_key": "Create API Key", - "add_a_new_api_key_for_programmatic_access": "Add a new API key for programmatic access to the {#link href=|https://pocket-id.org/docs/api|}Pocket ID API{/link}.", - "add_api_key": "Add API Key", - "manage_api_keys": "Manage API Keys", - "api_key_created": "API Key Created", - "for_security_reasons_this_key_will_only_be_shown_once": "For security reasons, this key will only be shown once. Please store it securely.", + "create_api_key": "Create API key", + "api_key_created": "API key created", "description": "Description", - "api_key": "API Key", + "api_key": "API key", "close": "Close", - "name_to_identify_this_api_key": "Name to identify this API key.", - "expires_at": "Expires At", - "when_this_api_key_will_expire": "When this API key will expire.", - "optional_description_to_help_identify_this_keys_purpose": "Optional description to help identify this key's purpose.", "expiration_date_must_be_in_the_future": "Expiration date must be in the future", - "revoke_api_key": "Revoke API Key", "never": "Never", "revoke": "Revoke", - "api_key_revoked_successfully": "API key revoked successfully", - "are_you_sure_you_want_to_revoke_the_api_key_apikeyname": "Are you sure you want to revoke the API key \"{apiKeyName}\"? This will break any integrations using this key.", - "last_used": "Last Used", - "actions": "Actions", + "last_used": "Last used", "general": "General", - "configure_smtp_to_send_emails": "Enable email notifications to alert users when a login is detected from a new device or location.", "ldap": "LDAP", - "configure_ldap_settings_to_sync_users_and_groups_from_an_ldap_server": "Configure LDAP settings to sync users and groups from an LDAP server.", - "images": "Images", "save_changes_question": "Save changes?", "you_have_to_save_the_changes_before_sending_a_test_email_do_you_want_to_save_now": "You have to save the changes before sending a test email. Do you want to save now?", "save_and_send": "Save and send", "test_email_sent_successfully": "Test email sent successfully to your email address.", "failed_to_send_test_email": "Failed to send test email. Check the server logs for more information.", - "smtp_configuration": "SMTP Configuration", - "smtp_host": "SMTP Host", - "smtp_port": "SMTP Port", - "smtp_user": "SMTP User", - "smtp_password": "SMTP Password", - "smtp_from": "SMTP From", - "smtp_tls_option": "SMTP TLS Option", - "email_tls_option": "Email TLS Option", - "skip_certificate_verification": "Skip Certificate Verification", - "skip_consent": "Skip Consent Screen", - "skip_consent_description": "Don't ask users to approve this client and the scopes it requests. Only enable this for trusted first-party clients.", - "this_can_be_useful_for_selfsigned_certificates": "This can be useful for self-signed certificates.", - "enabled_emails": "Enabled Emails", - "email_login_notification": "Email Login Notification", - "email_login_notification_description": "Choose when users receive an email after signing in.", - "login_notification_disabled_description": "Do not send sign-in notification emails or use browser recognition cookies.", + "skip_certificate_verification": "Skip certificate verification", + "skip_consent": "Skip consent screen", "login_notification_always": "Every sign-in", - "login_notification_always_description": "Send an email after every successful sign-in. No browser recognition cookie is used.", "login_notification_ip_and_user_agent": "New IP address or browser", - "login_notification_ip_and_user_agent_description": "Send an email when the exact combination of IP address and browser User-Agent is not in the user's sign-in history. No browser recognition cookie is used.", "login_notification_browser_recognition": "Unrecognized browser", - "login_notification_browser_recognition_description": "Send an email unless the browser is recognized by its cookie or a matching IP address and User-Agent in the user's sign-in history.", - "send_an_email_to_the_user_when_they_log_in_from_a_new_device": "Send an email to the user when they log in from a new device.", - "emai_login_code_requested_by_user": "Email Login Code Requested by User", - "allow_users_to_sign_in_with_a_login_code_sent_to_their_email": "Allows users to bypass passkeys by requesting a login code sent to their email. This significantly reduces security as anyone with access to the user's email can gain entry.", - "email_login_code_from_admin": "Email Login Code from Admin", - "allows_an_admin_to_send_a_login_code_to_the_user": "Allows an admin to send a login code to the user via email.", "send_test_email": "Send test email", - "application_name": "Application Name", - "session_duration": "Session Duration", - "the_duration_of_a_session_in_minutes_before_the_user_has_to_sign_in_again": "The duration of a session in minutes before the user has to sign in again.", - "enable_self_account_editing": "Enable Self-Account Editing", - "whether_the_users_should_be_able_to_edit_their_own_account_details": "Whether the users should be able to edit their own account details.", - "ldap_configuration_updated_successfully": "LDAP configuration updated successfully", - "ldap_disabled_successfully": "LDAP disabled successfully", + "application_name": "Application name", + "session_duration": "Session duration", "ldap_sync_finished": "LDAP sync finished", - "client_configuration": "Client Configuration", - "ldap_url": "LDAP URL", - "ldap_bind_dn": "LDAP Bind DN", - "ldap_bind_password": "LDAP Bind Password", - "ldap_base_dn": "LDAP Base DN", - "user_search_filter": "User Search Filter", - "the_search_filter_to_use_to_search_or_sync_users": "The Search filter to use to search/sync users.", - "groups_search_filter": "Groups Search Filter", - "the_search_filter_to_use_to_search_or_sync_groups": "The Search filter to use to search/sync groups.", - "attribute_mapping": "Attribute Mapping", - "user_unique_identifier_attribute": "User Unique Identifier Attribute", - "the_value_of_this_attribute_should_never_change": "The value of this attribute should never change.", - "username_attribute": "Username Attribute", - "user_mail_attribute": "User Mail Attribute", - "user_first_name_attribute": "User First Name Attribute", - "user_last_name_attribute": "User Last Name Attribute", - "user_profile_picture_attribute": "User Profile Picture Attribute", - "the_value_of_this_attribute_can_either_be_a_url_binary_or_base64_encoded_image": "The value of this attribute can either be a URL, a binary or a base64 encoded image.", - "group_members_attribute": "Group Members Attribute", - "the_attribute_to_use_for_querying_members_of_a_group": "The attribute to use for querying members of a group.", - "group_unique_identifier_attribute": "Group Unique Identifier Attribute", - "group_rdn_attribute": "Group RDN Attribute (in DN)", - "admin_group_name": "Admin Group Name", - "members_of_this_group_will_have_admin_privileges_in_pocketid": "Members of this group will have Admin Privileges in Pocket ID.", - "disable": "Disable", + "attribute_mapping": "Attribute mapping", "sync_now": "Sync now", "enable": "Enable", - "user_created_successfully": "User created successfully", - "create_user": "Create User", - "add_a_new_user_to_appname": "Add a new user to {appName}", - "add_user": "Add User", - "manage_users": "Manage Users", - "admin_privileges": "Admin Privileges", + "add_user": "Add user", + "admin_privileges": "Admin privileges", "admins_have_full_access_to_the_admin_panel": "Admins have full access to the admin panel.", - "delete_firstname_lastname": "Delete {firstName} {lastName}", - "are_you_sure_you_want_to_delete_this_user": "Are you sure you want to delete this user?", - "user_deleted_successfully": "User deleted successfully", "role": "Role", - "source": "Source", "admin": "Admin", "user": "User", - "local": "Local", "toggle_menu": "Toggle menu", - "edit": "Edit", "back": "Back", - "user_details_firstname_lastname": "User Details {firstName} {lastName}", - "manage_which_groups_this_user_belongs_to": "Manage which groups this user belongs to.", - "custom_claims": "Custom Claims", - "custom_claims_are_key_value_pairs_that_can_be_used_to_store_additional_information_about_a_user": "Custom claims are key-value pairs that can be used to store additional information about a user. These claims will be included in the ID token if the scope 'profile' is requested.", - "user_group_created_successfully": "User group created successfully", - "create_user_group": "Create User Group", - "create_a_new_group_that_can_be_assigned_to_users": "Create a new group that can be assigned to users.", - "add_group": "Add Group", - "manage_user_groups": "Manage User Groups", - "friendly_name": "Friendly Name", - "name_that_will_be_displayed_in_the_ui": "Name that will be displayed in the UI", - "name_that_will_be_in_the_groups_claim": "Name that will be in the \"groups\" claim", - "delete_name": "Delete {name}", - "are_you_sure_you_want_to_delete_this_user_group": "Are you sure you want to delete this user group?", - "user_group_deleted_successfully": "User group deleted successfully", - "user_count": "User Count", - "user_group_details_name": "User Group Details {name}", - "assign_users_to_this_group": "Assign users to this group.", - "custom_claims_are_key_value_pairs_that_can_be_used_to_store_additional_information_about_a_user_prioritized": "Custom claims are key-value pairs that can be used to store additional information about a user. These claims will be included in the ID token if the scope 'profile' is requested. Custom claims defined on the user will be prioritized if there are conflicts.", - "oidc_client_created_successfully": "OIDC client created successfully", - "create_oidc_client": "Create OIDC Client", - "add_a_new_oidc_client_to_appname": "Add a new OIDC client to {appName}.", - "add_oidc_client": "Add OIDC Client", - "manage_oidc_clients": "Manage OIDC Clients", + "custom_claims": "Custom claims", + "add_group": "Add group", + "friendly_name": "Friendly name", + "add_oidc_client": "Add OIDC client", "add": "Add", "callback_urls": "Callback URLs", - "logout_callback_urls": "Logout Callback URLs", - "backchannel_logout_url": "Back-Channel Logout URL", - "backchannel_logout_url_description": "URL that receives a logout token when a user's access is revoked, for example when they are disabled or lose group access.", - "public_client": "Public Client", - "public_clients_description": "Public clients do not have a client secret. They are designed for mobile, web, and native applications where secrets cannot be securely stored.", + "logout_callback_urls": "Logout callback URLs", + "backchannel_logout_url": "Back-channel logout URL", "pkce": "PKCE", - "proof_key_code_exchange_is_a_security_feature_to_prevent_csrf_and_authorization_code_interception_attacks": "Proof Key for Code Exchange is a security feature to prevent CSRF and authorization code interception attacks.", - "requires_reauthentication": "Requires Re-Authentication", - "requires_users_to_authenticate_again_on_each_authorization": "Requires users to authenticate again on each authorization, even if already signed in", + "requires_reauthentication": "Requires re-authentication", "par": "PAR", - "requires_pushed_authorization_requests": "Requires Pushed Authorization Requests", - "requires_pushed_authorization_requests_description": "Requires clients to use the PAR endpoint to pre-register authorization parameters before initiating the flow.", + "requires_pushed_authorization_requests": "Requires pushed authorization requests", "name_logo": "{name} logo", - "are_you_sure_you_want_to_delete_this_oidc_client": "Are you sure you want to delete this OIDC client?", - "oidc_client_deleted_successfully": "OIDC client deleted successfully", - "authorization_url": "Authorization URL", - "oidc_discovery_url": "OIDC Discovery URL", - "token_url": "Token URL", - "userinfo_url": "Userinfo URL", - "logout_url": "Logout URL", - "certificate_url": "Certificate URL", - "enabled": "Enabled", "disabled": "Disabled", "client_type": "Client type", - "client_type_standard": "Standard", - "client_type_metadata_document": "Metadata Document", - "cimd_client_managed_fields_title": "Some properties are read-only", - "cimd_client_managed_fields_description": "This client is managed through a Client ID Metadata Document (CIMD), so properties supplied by the document can't be edited here.", - "client_id_metadata_documents": "Client ID Metadata Documents", - "client_id_metadata_documents_description": "Client ID Metadata Documents (CIMD) let OAuth clients identify themselves using a URL. No preregistration necessary.", + "client_id_metadata_documents": "Client ID metadata documents", "cimd_url_allowlist": "Allowed metadata document URLs", - "cimd_url_allowlist_description": "Restrict which client ID metadata document URLs are accepted. {#link href=|https://pocket-id.org/docs/advanced/callback-url-wildcards|}Wildcards{/link} are supported. An empty list blocks all URLs.", - "refresh": "Refresh", - "oidc_client_metadata_refreshed_successfully": "Client metadata document refreshed successfully", - "new_client_secret_created_successfully": "New client secret created successfully", "oidc_client_name": "OIDC Client {name}", "client_id": "Client ID", "client_secret": "Client secret", - "show_more_details": "Show more details", - "allowed_user_groups": "Allowed User Groups", - "allowed_user_groups_description": "Select the user groups whose members are allowed to sign in to this client.", "unrestrict": "Unrestrict", "user_groups_restriction_updated_successfully": "User groups restriction updated successfully", "favicon": "Favicon", - "light_mode_logo": "Light Mode Logo", - "dark_mode_logo": "Dark Mode Logo", - "email_logo": "Email Logo", - "background_image": "Background Image", + "email_logo": "Email logo", "language": "Language", "reset_profile_picture_question": "Reset profile picture?", "this_will_remove_the_uploaded_image_and_reset_the_profile_picture_to_default": "This will remove the uploaded image and reset the profile picture to default. Do you want to continue?", @@ -345,54 +166,25 @@ "count_changed": "{count} changed", "breadcrumb": "Breadcrumb", "profile_picture_has_been_reset": "Profile picture has been reset. It may take a few minutes to update.", - "select_the_language_you_want_to_use": "Select the language you want to use. Please note that some text may be automatically translated and could be inaccurate.", - "contribute_to_translation": "If you find an issue you're welcome to contribute to the translation on {#link href=|https://crowdin.com/project/pocket-id|}Crowdin{/link}.", - "personal": "Personal", - "global": "Global", - "all_users": "All Users", - "all_events": "All Events", - "all_clients": "All Clients", - "all_locations": "All Locations", - "global_audit_log": "Global Audit Log", - "see_all_recent_account_activities": "View the account activities of all users during the set retention period.", + "all_users": "All users", + "allow_all_users": "Allow all users", + "allow_all_users_question": "Allow all users to sign in to {name}?", + "allow_all_users_consequence": "Its allowed groups are removed, and anyone with a {appName} account can sign in.", "audit_event_sign_in": "Sign in", "audit_event_token_sign_in": "Token Sign In", "audit_event_remote_sign_in": "Remote sign in", - "audit_event_client_authorization": "Client Authorization", - "audit_event_new_client_authorization": "New Client Authorization", - "audit_event_account_created": "Account Created", - "audit_event_device_code_authorization": "Device Code Authorization", - "audit_event_new_device_code_authorization": "New Device Code Authorization", - "audit_event_passkey_added": "Passkey Added", - "audit_event_passkey_removed": "Passkey Removed", - "disable_animations": "Disable Animations", - "turn_off_ui_animations": "Turn off animations throughout the UI.", - "user_disabled": "Account Disabled", - "disabled_users_cannot_log_in_or_use_services": "Disabled users cannot log in or use services.", - "user_disabled_successfully": "User has been disabled successfully.", - "user_enabled_successfully": "User has been enabled successfully.", + "audit_event_client_authorization": "Client authorization", + "audit_event_new_client_authorization": "New client authorization", + "audit_event_account_created": "Account created", + "audit_event_device_code_authorization": "Device code authorization", + "audit_event_new_device_code_authorization": "New device code authorization", + "audit_event_passkey_added": "Passkey added", + "audit_event_passkey_removed": "Passkey removed", + "disable_animations": "Disable animations", "status": "Status", - "disable_firstname_lastname": "Disable {firstName} {lastName}", - "are_you_sure_you_want_to_disable_this_user": "Are you sure you want to disable this user? They will not be able to log in or access any services.", - "ldap_soft_delete_users": "Keep disabled users from LDAP.", - "ldap_soft_delete_users_description": "When enabled, users removed from LDAP will be disabled rather than deleted from the system.", - "login_code_email_success": "The login code has been sent to the user.", - "send_email": "Send Email", - "show_code": "Show Code", - "callback_url_description": "URL(s) provided by your client. {#link href=|https://pocket-id.org/docs/advanced/callback-url-wildcards|}Wildcards{/link} are supported.", - "logout_callback_url_description": "URL(s) provided by your client for logout. {#link href=|https://pocket-id.org/docs/advanced/callback-url-wildcards|}Wildcards{/link} are supported.", - "api_key_expiration": "API Key Expiration", - "send_an_email_to_the_user_when_their_api_key_is_about_to_expire": "Send an email to the user when their API key is about to expire.", - "authorize_device": "Authorize Device", - "the_device_has_been_authorized": "The device has been authorized.", - "enter_code_displayed_in_previous_step": "Enter the code that was displayed in the previous step.", - "authorize": "Authorize", - "federated_client_credentials": "Federated Client Credentials", - "federated_client_credentials_description": "Federated client credentials allow authenticating OIDC clients without managing long-lived secrets. They leverage JWT tokens issued by third-party authorities for client assertions, e.g. workload identity tokens.", - "add_federated_client_credential": "Add Federated Client Credential", - "add_another_federated_client_credential": "Add another federated client credential", - "federated_identity_number": "Identity {number}", - "remove_federated_identity": "Remove federated identity", + "send_email": "Send email", + "callback_url_description": "{#link href=|https://pocket-id.org/docs/advanced/callback-url-wildcards|}Wildcards{/link} are supported.", + "federated_client_credentials": "Federated client credentials", "issuer": "Issuer", "subject": "Subject", "defaults_to_the_client_id": "Defaults to the client ID", @@ -412,17 +204,11 @@ "the_value_is_not_a_jwk_or_a_jwks": "The value is not a JWK or a JWKS", "the_jwks_does_not_contain_any_key": "The JWKS does not contain any key", "jwks_key_is_not_a_jwk": "Key {number} of the JWKS is not a JWK", - "oidc_allowed_group_count": "Allowed Group Count", - "oidc_data_preview": "OIDC Data Preview", - "preview_the_oidc_data_that_would_be_sent_for_different_users": "Preview the OIDC data that would be sent for different users", - "id_token": "ID Token", - "access_token": "Access Token", + "id_token": "ID token", + "access_token": "Access token", "token_lifetimes": "Token lifetimes", - "token_lifetimes_description": "Change the lifetimes of the access and refresh tokens issued to this client. Shorter lifetimes improve security, but may require users to re-authenticate more frequently.", "access_token_lifetime": "Access token lifetime", - "access_token_lifetime_description": "Controls how long the application credential remains usable. Refresh tokens may keep the application session active.", "refresh_token_inactivity_timeout": "Refresh token inactivity timeout", - "refresh_token_inactivity_timeout_description": "Controls how long a refresh token remains usable. The timeout restarts whenever the application successfully refreshes its tokens.", "token_lifetime_minimum": "Token lifetime must be at least 1 minute.", "token_lifetime_maximum": "Token lifetime cannot exceed 365 days.", "token_lifetime_whole_minutes": "Token lifetime must use whole-minute increments.", @@ -432,211 +218,87 @@ "days": "Days", "userinfo": "Userinfo", "copy": "Copy", - "copy_all": "Copy All", "select_an_option": "Select an option", - "select_user": "Select User", - "error": "Error", - "signup_token": "Signup Token", - "create_a_signup_token_to_allow_new_user_registration": "Create a signup token to allow new user registration.", - "usage_limit": "Usage Limit", - "number_of_times_token_can_be_used": "Number of times the signup token can be used.", + "select_user": "Select user", + "usage_limit": "Usage limit", "expires": "Expires", "signup": "Sign Up", - "user_creation": "User Creation", - "configure_user_creation": "Manage user creation settings, including signup methods and default permissions for new users.", - "user_creation_groups_description": "Assign these groups automatically to new users upon signup.", - "user_creation_claims_description": "Assign these custom claims automatically to new users upon signup.", - "signup_disabled_description": "User signups are completely disabled. Only administrators can create new user accounts.", - "signup_requires_valid_token": "A valid signup token is required to create an account", - "go_to_login": "Go to login", - "signup_to_appname": "Sign Up to {appName}", - "create_your_account_to_get_started": "Create your account to get started.", - "initial_account_creation_description": "Please create your account to get started. You will be able to set up a passkey later.", - "setup_your_passkey": "Set up your passkey", - "create_a_passkey_to_securely_access_your_account": "Create a passkey to securely access your account. This will be your primary way to sign in.", "skip_for_now": "Skip for now", - "enable_user_signups": "Enable User Signups", - "enable_user_signups_description": "Decide how users can sign up for new accounts in Pocket ID.", - "user_signups_are_disabled": "User signups are currently disabled", - "create_signup_token": "Create Signup Token", - "view_active_signup_tokens": "View Active Signup Tokens", - "manage_signup_tokens": "Manage Signup Tokens", - "view_and_manage_active_signup_tokens": "View and manage active signup tokens.", - "signup_token_deleted_successfully": "Signup token deleted successfully.", + "create_signup_token": "Create signup token", "expired": "Expired", - "used_up": "Used Up", + "used_up": "Used up", "active": "Active", - "usage": "Usage", "created": "Created", "token": "Token", - "delete_signup_token": "Delete Signup Token", - "are_you_sure_you_want_to_delete_this_signup_token": "Are you sure you want to delete this signup token? This action cannot be undone.", - "signup_with_token": "Signup with token", - "signup_with_token_description": "Users can only sign up using a valid signup token created by an administrator.", - "signup_open": "Open Signup", - "signup_open_description": "Anyone can create a new account without restrictions.", - "of": "of", - "skip_passkey_setup": "Skip Passkey Setup", - "skip_passkey_setup_description": "It's highly recommended to set up a passkey because without one, you will be locked out of your account as soon as the session expires.", - "my_apps": "My Apps", - "no_apps_available": "No apps available", - "contact_your_administrator_for_app_access": "Contact your administrator to get access to applications.", - "launch": "Launch", - "client_launch_url": "Client Launch URL", - "client_launch_url_description": "The URL that will be opened when a user launches the app from the My Apps page.", - "client_name_description": "The name of the client that shows in the Pocket ID UI.", - "client_description": "Description", - "show_all_apps": "Show all apps", - "show_hidden_apps": "Show hidden apps", - "hide_all_apps": "Hide all apps", + "my_apps": "My apps", "sort_by": "Sort by", "recently_used": "Recently used", - "name_a_to_z": "Name (A-Z)", - "name_z_to_a": "Name (Z-A)", - "search_apps": "Search apps", - "no_apps_match_your_search": "No apps match your search", - "try_a_different_search_term": "Try a different search term.", - "revoke_access": "Revoke Access", - "revoke_access_description": "Revoke access to {#b}{clientName}{/b}. {#b}{clientName}{/b} will no longer be able to access your account information.", - "revoke_access_successful": "The access to {clientName} has been successfully revoked.", - "last_signed_in_ago": "Last signed in {time} ago", + "name_a_to_z": "Name A–Z", + "name_z_to_a": "Name Z–A", + "search_apps": "Search apps…", + "revoke_access_description": "{#b}{clientName}{/b} can no longer read your account details until you sign in to it again.", "invalid_client_id": "Client ID can only contain letters, numbers, underscores, and hyphens", - "custom_client_id_description": "Set a custom client ID if this is required by your application. Otherwise, leave it blank to generate a random one.", "generated": "Generated", "administration": "Administration", - "group_rdn_attribute_description": "The attribute used in the groups distinguished name (DN).", - "display_name_attribute": "Display Name Attribute", - "display_name": "Display Name", - "configure_application_images": "Configure Application Images", - "ui_config_disabled_info_title": "UI Configuration Disabled", - "ui_config_disabled_info_description": "The UI configuration is disabled because the application configuration settings are managed through environment variables. Some settings may not be editable.", - "invalid_uri": "Invalid URI", - "require_user_email": "Require Email Address", - "require_user_email_description": "Requires users to have an email address. If disabled, the users without an email address won't be able to use features that require an email address.", + "display_name": "Display name", "locale": "Locale", "ldap_id": "LDAP ID", "reauthentication": "Re-authentication", - "default_profile_picture": "Default Profile Picture", + "default_profile_picture": "Default profile picture", "light": "Light", "dark": "Dark", "system": "System", - "signup_token_user_groups_description": "Automatically assign these groups to users who sign up using this token.", - "allowed_oidc_clients": "Allowed OIDC Clients", - "allowed_oidc_clients_description": "Select the OIDC clients that members of this user group are allowed to sign in to.", "unrestrict_oidc_client": "Unrestrict {clientName}", "confirm_unrestrict_oidc_client_description": "Are you sure you want to unrestrict the OIDC client {#b}{clientName}{/b}? This will remove all group assignments for this client and any user will be able to sign in.", "yes": "Yes", "no": "No", - "restricted": "Restricted", - "scim_provisioning": "SCIM Provisioning", - "scim_provisioning_description": "SCIM provisioning allows you to automatically provision and deprovision users and groups from your OIDC client. Learn more in the {#link href=|https://pocket-id.org/docs/configuration/scim|}docs{/link}.", - "scim_endpoint": "SCIM Endpoint", - "scim_token": "SCIM Token", - "last_successful_sync_at": "Last successful sync: {time}", - "scim_enabled_successfully": "SCIM enabled successfully.", - "scim_disabled_successfully": "SCIM disabled successfully.", - "disable_scim_provisioning": "Disable SCIM Provisioning", - "disable_scim_provisioning_confirm_description": "Are you sure you want to disable SCIM provisioning for {#b}{clientName}{/b}? This will stop all automatic user and group provisioning and deprovisioning.", + "scim_provisioning": "SCIM provisioning", + "scim_endpoint": "SCIM endpoint", + "scim_token": "SCIM token", "scim_sync_failed": "SCIM sync failed. Check the server logs for more information.", "scim_sync_successful": "The SCIM sync has been completed successfully.", "save_and_sync": "Save and Sync", "scim_save_changes_description": "You have to save the changes before starting a SCIM sync. Do you want to save now?", "scopes": "Scopes", - "issuer_url": "Issuer URL", - "smtp_field_required_when_other_provided": "Required when any SMTP setting is provided", - "smtp_field_required_when_email_enabled": "Required when email notifications are enabled", - "renew": "Renew", - "renew_api_key": "Renew API Key", - "renew_api_key_description": "Renewing the API key will generate a new key. Make sure to update any integrations using this key.", "api_key_renewed": "API key renewed", - "app_config_home_page": "Home Page", - "app_config_home_page_description": "The page users are redirected to after signing in.", + "app_config_home_page": "Home page", "email_verification_warning": "Verify your email address", - "email_verification_warning_description": "Your email address is not verified yet. Please verify it as soon as possible.", - "email_verification": "Email Verification", - "email_verification_description": "Choose whether users verify their email address and whether unverified accounts are restricted.", - "email_verification_disabled_description": "Users are not asked to verify their email address.", + "email_verification": "Email verification", "email_verification_optional": "Optional", - "email_verification_optional_description": "Users can verify their email address, but unverified accounts keep full access.", "email_verification_required": "Required", - "email_verification_required_description": "Users can't sign in to applications until they verify their email address. Self-signed-up accounts that aren't verified within 24 hours are deleted.", "email_verification_required_needs_user_email": "Requiring email verification also requires users to have an email address.", "email_verification_required_warning": "Verify your email address to use your account", - "email_verification_required_warning_description": "Your access is limited until you verify your email address.", "email_verification_deadline_description": "If you don't, your account will be deleted {time}.", "verify_email_address": "Verify email address", "email_verification_required_error": "You must verify your email address before you can sign in to applications", "email_change_not_started": "Your other changes were saved, but the email to confirm your new email address couldn't be sent. Please try again.", - "email_change_pending_title": "Confirm your new email address", - "email_change_pending_description": "We sent a confirmation link to {email}. Your current email address stays in use until you confirm the change.", "cancel_email_change": "Cancel change", "email_change_canceled": "Email change canceled.", - "email_verification_success_title": "Email Verified Successfully", - "email_verification_success_description": "Your email address has been verified successfully.", - "email_verification_error_title": "Email Verification Failed", - "mark_as_unverified": "Mark as unverified", - "mark_as_verified": "Mark as verified", "email_verification_sent": "Verification email sent successfully.", - "emails_verified_by_default": "Emails verified by default", - "emails_verified_by_default_description": "When enabled, users' email addresses will be marked as verified by default upon signup or when their email address is changed.", - "user_has_no_passkeys_yet": "This user has no passkeys yet.", - "replay_protection": "Replay Protection", - "replay_protection_description": "If enabled the provided token can only be used once. If your provider uses the same token multiple times, you may need to disable this option.", + "replay_protection": "Replay protection", "apis": "APIs", - "create_api": "Create API", - "create_a_new_api_description": "Create a new API that clients can request access tokens for. You can learn more about APIs in the {#link href=|https://pocket-id.org/docs/guides/apis|}docs{/link}.", "add_api": "Add API", - "manage_apis": "Manage APIs", "api_resource": "Resource", "api_resource_description": "A unique URI that identifies this API resource. Clients request it with the resource parameter. It can't be changed later.", "api_permissions": "Permissions", "api_permissions_description": "The permissions (scopes) that clients can request for this API.", - "api_permission_key": "Permission", "add_permission": "Add permission", - "api_created_successfully": "API created successfully", - "api_deleted_successfully": "API deleted successfully", - "are_you_sure_you_want_to_delete_this_api": "Are you sure you want to delete this API? Clients will lose access to its permissions.", "api_access": "API access", - "api_access_description": "Select which APIs this client may request tokens for on behalf of users (user-delegated access) and for itself via the client credentials grant (client access), and which permissions it may ask for.", "api_access_updated_successfully": "API access updated successfully", "access_an_api_on_your_behalf": "Access an API on your behalf", - "api_name": "API Name", - "user_delegated_access": "User-delegated access", - "client_access": "Client access (M2M)", - "client_access_unavailable_for_public_clients": "Public clients can't use the client credentials grant, so client access is not available.", - "permissions_granted_count": "{granted} / {total} permissions granted", - "select_the_access_this_client_may_request": "Choose whether this client may request tokens for this API on behalf of the signed-in user (user-delegated access) and for itself without a user via the client credentials grant (client access), and which permissions it may ask for.", - "user_delegated_access_description": "The client may request tokens for this API on behalf of the signed-in user.", - "client_access_description": "The client may request tokens for this API for itself, without a user.", - "no_access": "No access", - "metadata_document_client_access": "Metadata document clients", "allow_all_metadata_document_clients": "Allow all metadata document clients", "allow_all_metadata_document_clients_description": "Every client registered through a metadata document can request tokens for this API on behalf of users.", "granted_permissions": "Granted permissions", "granted_through_cimd_access": "Granted because this API allows all metadata document clients", "access_granted_through_cimd_access": "This API allows all metadata document clients, so the access granted that way is shown here but can only be changed on the API itself.", "access": "Access", - "api_access_card_description": "Choose which clients may request tokens for this API and which permissions they may ask for.", - "add_client": "Add client", "select_a_client_to_grant_access_to_this_api": "Select the client that should get access to this API.", - "revoke_access_for_name": "Revoke access for {name}", - "are_you_sure_you_want_to_revoke_the_api_access_of_this_client": "Are you sure you want to revoke this client's access to the API? Its permissions on other APIs are kept.", "revoke_access_to_name": "Revoke access to {name}", "are_you_sure_you_want_to_revoke_the_access_of_this_client_to_the_api": "Are you sure you want to revoke this client's access to the API? Its access to other APIs is kept.", - "this_client_has_no_api_access_yet": "This client has not been granted access to any API yet.", "select_an_api_this_client_should_access": "Select the API this client should get access to.", "granted_to_all_cimd_clients": "Granted to all metadata document clients", - "i_have_a_longer_code": "I have a longer code", - "pkce_supported_client_title": "This client supports PKCE", - "pkce_supported_client_description": "This client supports Proof Key for Code Exchange (PKCE). PKCE is a security feature that helps protect against certain attacks during the OAuth 2.0 authorization process. It's recommended to enable it.", "sign_in_with_another_device": "Sign in with another device", - "sign_in_with_another_device_description": "Scan a QR code with a device that has your passkey.", - "creating_device_login_request": "Creating sign-in request...", "device_login_qr_code": "Device login QR code", - "waiting_for_approval": "Waiting for approval", - "the_requesting_device_has_been_signed_in": "The requesting device has been signed in.", - "the_sign_in_request_was_denied": "The sign-in request was denied.", - "review_the_request_before_approving_it": "Review the requesting device before approving this sign in.", "deny": "Deny", "approve": "Approve", "visit_and_enter": "Visit {url} and enter:", @@ -644,18 +306,9 @@ "device_login_request_expired": "Your QR-Code has expired. Please start the sign-in process again.", "credentials": "Credentials", "client_secrets": "Client secrets", - "client_secrets_description": "Secrets that the app uses to authenticate itself. Multiple secrets can be active at the same time, so one can be rotated without downtime.", - "auto_create_client_secret": "Automatically create client secrets", - "auto_create_client_secret_description": "Create a secret without an expiration date when a confidential OIDC client is added. The secret is shown once after creation.", - "no_client_secrets_yet": "This app has no client secrets yet.", - "public_clients_cannot_have_secrets": "Public clients cannot have client secrets.", - "add_client_secret": "Add client secret", "no_expiration": "No expiration", "days_count": "{count} days", "client_secrets_limit_reached": "An app cannot have more than 20 client secrets.", - "delete_client_secret": "Delete client secret", - "are_you_sure_you_want_to_delete_this_client_secret": "Are you sure you want to delete this client secret? Apps using it will no longer be able to authenticate.", - "client_secret_deleted_successfully": "Client secret deleted successfully", "unsaved_changes": "Unsaved changes", "one_unsaved_change": "1 unsaved change", "count_unsaved_changes": "{count} unsaved changes", @@ -665,17 +318,8 @@ "could_not_save_check_your_connection": "Couldn’t save. Check your connection.", "save_or_discard_before_leaving": "Save or discard before leaving", "application": "Application", - "oidc_client_application_description": "How the client is shown to users on the consent screen and the My Apps page.", "redirects": "Redirects", - "oidc_client_redirects_description": "Where users can be sent back to after signing in and signing out.", "security": "Security", - "oidc_client_security_description": "How the client authenticates and how users authorize it.", - "show_less_details": "Show less details", - "all_users_can_sign_in_to_this_client": "Any user can sign in to this client.", - "selected_user_groups": "Selected user groups", - "only_members_of_the_selected_groups_can_sign_in": "Only members of the selected groups can sign in.", - "add_callback_url": "Add callback URL", - "add_logout_url": "Add logout URL", "remove_logo": "Remove logo", "choose_logo": "Choose logo", "current_logo": "Current logo", @@ -687,12 +331,8 @@ "use_as_dark_logo": "Use as dark logo", "icons_provided_by_selfhst": "Icons by {#link href=|https://selfh.st/icons|}selfh.st{/link}, licensed under {#link href=|https://creativecommons.org/licenses/by/4.0/|}CC BY 4.0{/link}.", "claims": "Claims", - "set_custom_client_id": "Set custom client ID", - "confidential_client": "Confidential Client", - "confidential_client_description": "Authenticates with a client secret. For apps with a backend that can keep the secret safe.", - "public_client_type_description": "Has no client secret and uses PKCE instead. For single-page, mobile and desktop apps.", + "public_client_type_description": "Uses PKCE instead of a secret. For browser, mobile and desktop apps.", "color_theme": "Color theme", - "color_theme_description": "Colors for everyone, on sign-in pages and in the app, in light and dark mode.", "color_theme_mono": "Mono", "color_theme_mist": "Mist", "color_theme_sage": "Sage", @@ -713,7 +353,6 @@ "custom": "Custom", "account": "Account", "activity": "Activity", - "claim_mappings": "Claim mappings", "configuration": "Configuration", "main": "Main", "update_available": "Update available", @@ -761,5 +400,706 @@ "raw_data": "Raw data", "copy_json": "Copy JSON", "copy_ip_address": "Copy IP address", - "copy_event_id": "Copy event ID" + "copy_event_id": "Copy event ID", + "account_page_description": "Your profile, passkeys and language.", + "profile": "Profile", + "profile_section_description": "How you appear to apps you sign in to with {appName}.", + "profile_managed_by_ldap": "Managed by LDAP. Changes come from your directory.", + "profile_editing_disabled": "Your administrator manages your profile.", + "display_name_helper": "Filled in from your first and last name until you change it.", + "email_helper_purpose": "Apps you sign in to receive this address.", + "email_status_verified": "Verified", + "email_status_unverified": "Unverified", + "email_status_verification_required": "Verification required", + "email_change_waiting": "Waiting for you to confirm {email}. We sent a link there.", + "profile_picture_formats": "PNG, JPEG, WebP, AVIF or HEIC.", + "profile_picture_from_directory": "Your directory provides this picture.", + "upload": "Upload", + "upload_profile_picture": "Upload profile picture", + "adjust_your_photo": "Adjust your photo", + "adjust_your_photo_description": "Drag to reposition the photo and zoom to frame it.", + "photo_crop_area": "Crop area, use the arrow keys to move the photo", + "zoom": "Zoom", + "zoom_in": "Zoom in", + "zoom_out": "Zoom out", + "passkeys_section_description": "They live on your devices or in your password manager.", + "add_backup_passkey": "Add a backup passkey", + "add_backup_passkey_description": "If you lose access to {name}, you’ll need an admin to give you a login code.", + "no_passkeys_yet": "No passkeys yet", + "preferences": "Preferences", + "language_crowdin_description": "Some translations are automatic. Help improve them on {#link href=|https://crowdin.com/project/pocket-id|}Crowdin{/link}", + "email_verification_send_hint": "Send a verification link from the Email field below.", + "email_verification_only_page": "Until you do, this is the only page you can use.", + "email_verified_title": "{email} is verified", + "email_verification_failed_title": "We couldn’t verify your email", + "my_apps_description": "Apps you can open with your {appName} account.", + "apps_count_one": "1 app", + "apps_count": "{count} apps", + "apps_without_launch_link_count": "{count} without a launch link", + "app_matches_query": "1 app matches “{query}”", + "apps_match_query": "{count} apps match “{query}”", + "no_apps_match_query": "No apps match “{query}”", + "clear_search": "Clear search", + "clear_filters": "Clear filters", + "app_used_ago": "Used {time}", + "not_signed_in_yet": "Not signed in yet", + "apps_without_launch_link": "Apps without a launch link", + "apps_without_launch_link_note": "You’ve signed in to these, but they have no link to open.", + "all_apps": "All apps", + "no_apps_yet": "No apps yet", + "no_apps_yet_description": "Apps appear here after your administrator gives you access.", + "no_apps_yet_admin_description": "Connect your first app, like Immich or Jellyfin. It shows up here for everyone you give access.", + "connect_an_app": "Connect an app", + "open_in_new_tab": "Open in new tab", + "edit_client": "Edit client", + "revoke_access_ellipsis": "Revoke access…", + "revoke_app_access_title": "Revoke {name}’s access to your {appName} account?", + "app_access_revoked": "{name}’s access revoked", + "link_to_name_copied": "Link to {name} copied", + "activity_description": "Sign-ins and changes to your account.", + "configuration_categories": "Configuration categories", + "config_tab_signup": "Sign-up", + "config_tab_features": "Features", + "config_features_intro": "Turn on what your setup needs. {appName} shows the pages and tabs of a feature only while it is on.", + "features_directory": "Directory", + "features_directory_description": "Bring in people from other systems and add your own claims to their tokens.", + "features_developers": "Developers", + "features_developers_description": "Let your own services and scripts work with {appName}.", + "feature_ldap_description": "Sync people and groups from an LDAP directory.", + "feature_ldap_adds": "Adds LDAP to Configuration.", + "feature_ldap_in_use": "Stays on while LDAP sync is turned on.", + "feature_scim_description": "Send people and groups to apps that support SCIM.", + "feature_scim_adds": "Adds a SCIM tab to OIDC clients.", + "feature_scim_in_use": "Stays on while an OIDC client syncs with SCIM.", + "feature_custom_claims_description": "Add your own claims to the tokens of people and groups.", + "feature_custom_claims_adds": "Adds a Custom claims tab to people and groups.", + "feature_custom_claims_in_use": "Stays on while people, groups or sign-up defaults have custom claims.", + "features_clients": "Clients", + "features_clients_description": "Other ways for apps to register with {appName} and prove who they are.", + "feature_cimd_description": "Let apps register themselves with a metadata document instead of an admin adding them.", + "feature_cimd_adds": "Adds metadata document settings to OIDC and APIs.", + "feature_cimd_in_use": "Stays on while the allowlist has an entry, a metadata document client exists or an API admits these clients.", + "feature_federated_credentials_description": "Let OIDC clients authenticate with a token from another identity provider, like a CI system, instead of a secret.", + "feature_federated_credentials_adds": "Adds federated credentials to OIDC clients.", + "feature_federated_credentials_in_use": "Stays on while any OIDC client has federated credentials.", + "feature_apis_description": "Let OIDC clients request access tokens for your own APIs.", + "feature_apis_adds": "Adds APIs to the sidebar.", + "feature_apis_in_use": "Stays on while any API exists.", + "feature_api_keys_description": "Manage {appName} from scripts and other tools.", + "feature_api_keys_adds": "Adds API keys to the sidebar.", + "feature_api_keys_in_use": "Stays on while any API key exists.", + "feature_turned_on": "{feature} turned on", + "feature_turned_off": "{feature} turned off", + "config_tab_branding": "Branding", + "config_general_intro": "Name, sessions and how {appName} looks and moves for everyone.", + "config_signup_intro": "Whether people can create their own accounts and what new accounts start with.", + "config_passkeys_intro": "Which passkeys people can add and how they confirm it’s them.", + "config_email_intro": "Email addresses, verification and the emails {appName} sends.", + "config_ldap_intro": "Sync people and groups from an LDAP directory.", + "config_oidc_intro": "Endpoints, defaults for new clients and which metadata documents {appName} accepts.", + "config_oidc_intro_basic": "Endpoints and defaults for new clients.", + "config_branding_intro": "Logos and images people see on sign-in pages, in apps and in emails.", + "config_env_managed_title": "Configuration is managed by environment variables", + "config_env_managed_description": "Settings here are read-only because UI_CONFIG_DISABLED is set. Change them in your environment and restart Pocket ID.", + "config_app_name_description": "Shown on sign-in pages, in emails and in browser tabs.", + "config_session_duration_description": "How long people stay signed in. Up to 30 days.", + "config_session_duration_maximum": "Enter 30 days or less", + "config_session_duration_whole_minutes": "Use whole minutes", + "config_home_page_description": "Where people land after signing in.", + "config_self_account_editing": "Self-service account editing", + "config_self_account_editing_description": "Let people change their own name, username and email.", + "config_disable_animations_description": "Applies to everyone. Devices set to reduce motion already get this.", + "config_who_can_sign_up": "Who can sign up", + "config_who_can_sign_up_description": "Admins can always add people on the Users page.", + "config_signup_disabled": "Only admins", + "config_signup_with_token": "People with a signup token", + "config_signup_with_token_description": "People need an invite link from an admin.", + "config_signup_open": "Anyone", + "config_signup_open_description": "Anyone who can reach {appName} can create an account.", + "config_manage_signup_tokens": "Manage signup tokens", + "config_new_accounts": "New accounts", + "config_default_groups": "Default groups", + "config_default_groups_description": "Also preselected for new signup tokens.", + "config_default_custom_claims": "Default custom claims", + "config_registration": "Registration", + "config_registration_description": "Applies to new passkeys. Passkeys people already have keep working unless noted.", + "config_user_verification_description": "Whether a fingerprint, face or PIN is needed to use a passkey.", + "config_uv_required_description": "Require a fingerprint, face or PIN. Security keys without one won’t work.", + "config_uv_preferred_description": "Ask for one when available, and allow touch-only security keys.", + "config_allow_synced_passkeys_description": "Like iCloud Keychain or Google Password Manager.", + "config_synced_passkeys_off_note": "People whose only passkey is synced won’t be able to sign in.", + "config_any_passkey_description": "Device passkeys and security keys.", + "config_device_passkeys_description": "Passkeys built into phones and computers.", + "config_security_keys_only": "Security keys only", + "config_security_keys_only_description": "USB or NFC keys such as YubiKeys.", + "config_email_addresses": "Email addresses", + "config_require_email": "Require an email address", + "config_require_email_description": "Without it, people without an email can’t use login codes or notifications.", + "config_off": "Off", + "config_verification_optional_description": "People are asked, but keep full access.", + "config_verification_required_description": "Unverified people can’t sign in to apps.", + "config_verification_required_note": "Admins are exempt. Self-signed-up accounts that stay unverified for 24 hours are deleted.", + "config_treat_as_verified": "Treat new addresses as verified", + "config_treat_as_verified_locked": "Not available while verification is required.", + "config_mail_server": "Mail server", + "config_mail_server_description": "Login codes, verification links and notifications go out through this server.", + "config_smtp_missing_title": "Emails can’t be sent yet", + "config_smtp_missing_description": "Fill in the host, port and from address.", + "config_smtp_host": "Host", + "config_smtp_port": "Port", + "config_password": "Password", + "config_from_address": "From address", + "config_required_while_emails_on": "Required while email features are on.", + "config_required_with_mail_server": "Required when a mail server setting is filled in.", + "config_encryption": "Encryption", + "config_self_signed_only": "Only for servers with self-signed certificates.", + "config_emails_app_sends": "Emails {appName} sends", + "config_signin_notifications": "Sign-in notifications", + "config_signin_notifications_description": "When people get an email after signing in.", + "config_notify_ip_description": "From a new IP address and browser combination.", + "config_notify_browser_description": "From a browser that hasn’t signed in before.", + "config_admin_login_codes": "Login codes sent by admins", + "config_admin_login_codes_description": "Lets admins email a login code from a person’s page.", + "config_api_key_expiry": "API key expiry warnings", + "config_user_login_codes": "Login codes requested by people", + "config_user_login_codes_description": "People can get a sign-in code by email instead of using a passkey.", + "config_user_login_codes_note": "Anyone with access to someone’s inbox can sign in as them.", + "config_ldap_connected": "Connected", + "config_ldap_not_connected": "Not connected", + "config_ldap_syncing": "Syncing…", + "config_ldap_sync_failed": "Last sync failed", + "config_enable_ldap": "Enable LDAP…", + "config_disable_ldap": "Disable LDAP…", + "config_enable_ldap_title": "Connect to {host}?", + "config_enable_ldap_message": "{appName} saves these settings and syncs people and groups now. Synced people and groups become read-only here.", + "config_enable_ldap_confirm": "Enable LDAP", + "config_disable_ldap_title": "Disable LDAP sync?", + "config_disable_ldap_message": "People and groups that came from LDAP stay in {appName} and become editable. Nothing is deleted.", + "config_disable_ldap_confirm": "Disable LDAP", + "config_ldap_enabled_and_synced": "LDAP enabled and synced", + "config_ldap_disabled": "LDAP disabled", + "config_connection": "Connection", + "config_connection_description": "{appName} reads people and groups as this account and never writes to your directory.", + "config_search": "Search", + "config_search_description": "Only entries that match these filters sync to {appName}.", + "config_server_url": "Server URL", + "config_bind_dn": "Bind DN", + "config_bind_password": "Bind password", + "config_base_dn": "Base DN", + "config_user_search_filter": "User search filter", + "config_group_search_filter": "Group search filter", + "config_keep_removed_ldap_users": "Keep people removed from LDAP", + "config_keep_removed_ldap_users_description": "They’re disabled instead of deleted.", + "config_pocket_id_field": "Pocket ID field", + "config_ldap_attribute": "LDAP attribute", + "config_notes": "Notes", + "config_map_user_id": "User ID", + "config_map_group_members": "Group members", + "config_map_group_id": "Group ID", + "config_map_group_name": "Group name", + "config_map_admin_group": "Admin group", + "config_note_required": "Required.", + "config_note_required_never_change": "Required. Must never change.", + "config_note_picture": "A URL, binary or base64 image.", + "config_note_group_name": "Required. The attribute in the group’s DN.", + "config_note_admin_group": "Members become Pocket ID admins.", + "config_endpoints": "Endpoints", + "config_discovery_url": "Discovery URL", + "config_jwks": "JWKS", + "config_copy_endpoint": "Copy {name}", + "config_new_clients": "New clients", + "config_auto_secret": "Create a secret for new clients", + "config_auto_secret_description": "Confidential clients get a secret without an expiry when they’re added. It’s shown once.", + "config_cimd_description": "Apps can identify themselves with a URL to a metadata document instead of being registered here.", + "config_cimd_allowlist_description": "{#link href=|https://pocket-id.org/docs/advanced/callback-url-wildcards|}Wildcards{/link} are supported. An empty list blocks every metadata document client.", + "config_logo_description": "Shown on sign-in pages, in the sidebar and in My apps.", + "config_logo_spec": "SVG, PNG or WebP. At least 256 px wide.", + "config_signin_background": "Sign-in background", + "config_signin_background_description": "The photo behind the sign-in card.", + "config_signin_background_spec": "JPEG, PNG or WebP. At least 1920 px wide.", + "config_no_background": "No background. Sign-in pages use a plain background.", + "config_email_logo_description": "Shown at the top of every email {appName} sends.", + "config_email_logo_spec": "PNG or JPEG.", + "config_favicon_spec": "SVG, PNG or ICO.", + "config_default_profile_picture_description": "Shown for people who haven’t uploaded a picture. Without one, initials are shown.", + "config_default_profile_picture_spec": "PNG, JPEG or WebP. Square works best.", + "config_replace": "Replace", + "config_reset_image": "Reset to default, {image}", + "config_remove": "Remove", + "config_remove_image": "Remove {image}", + "config_image_light_logo": "light logo", + "config_image_dark_logo": "dark logo", + "config_image_background": "sign-in background", + "config_image_email_logo": "email logo", + "config_image_favicon": "favicon", + "config_image_default_profile_picture": "default profile picture", + "config_image_too_small": "This image is {width} px wide and may look blurry.", + "config_preview_button": "Sign in with passkey", + "auth_signed_in": "Signed in", + "auth_taking_you_to_your_account": "Taking you to your account…", + "auth_passkey_prompt_closed": "The passkey prompt was closed before you finished.", + "auth_sign_in_subtitle": "Use the passkey saved on this device or in your password manager.", + "auth_new_here": "New here?", + "auth_create_an_account": "Create an account", + "auth_sign_in_with_passkey": "Sign in with passkey", + "auth_other_ways_to_sign_in": "Other ways to sign in", + "auth_taking_you_back": "Taking you back…", + "auth_use_another_device": "Use another device", + "auth_use_another_device_description": "Approve this sign-in on a phone or computer that has your passkey.", + "auth_enter_a_login_code": "Enter a login code", + "auth_enter_a_login_code_description": "Use a code from your email or from your administrator.", + "auth_enter_an_admin_login_code_description": "Use a code from your administrator.", + "auth_email_me_a_login_code": "Email me a login code", + "auth_email_me_a_login_code_description": "Get a 6-character code at your email address.", + "auth_other_ways_to_sign_in_description": "Use one of these if your passkey isn’t on this device.", + "auth_use_your_passkey_instead": "Use your passkey instead", + "auth_enter_your_login_code": "Enter your login code", + "auth_login_code_short_description": "Enter the 6-character code from your email.", + "auth_login_code_long_description": "Enter the 12-character code your administrator gave you.", + "auth_login_code": "Login code", + "auth_checking_code": "Checking code…", + "auth_code_did_not_work": "That code didn’t work. Check it and try again.", + "auth_spaces_and_dashes_are_ignored": "Spaces and dashes are ignored.", + "auth_i_have_a_6_character_code": "I have a 6-character code", + "auth_i_have_a_12_character_code": "I have a 12-character code", + "auth_check_your_email": "Check your email", + "auth_email_code_sent": "If {#b}{email}{/b} belongs to a {appName} account, we sent it a code.", + "auth_resend_code_in": "Resend code in {time}", + "auth_resend_code": "Resend code", + "auth_get_a_login_code_by_email": "Get a login code by email", + "auth_email_login_description": "Enter the email address of your {appName} account.", + "auth_email_login_helper": "We’ll send a code and a sign-in link.", + "auth_enter_a_full_email_address": "Enter a full email address, like name@example.com.", + "auth_email_me_a_code": "Email me a code", + "auth_another_device_expired": "Codes only work for a few minutes. Get a new one to try again.", + "auth_another_device_denied": "The sign-in was denied on your other device.", + "auth_get_a_new_code": "Get a new code", + "auth_waiting_for_approval": "Waiting for approval on your other device…", + "auth_creating_sign_in_request": "Creating sign-in request…", + "oidc_clients_page_description": "Apps that sign people in with {appName}.", + "who_can_sign_in": "Who can sign in", + "who_can_sign_in_updated": "Who can sign in updated", + "selected_groups": "Selected groups", + "type": "Type", + "registration": "Registration", + "metadata_document": "Metadata document", + "registered_by_an_admin": "By an admin", + "on": "On", + "off": "Off", + "launch_url": "Launch URL", + "none": "None", + "open": "Open", + "no_groups": "No groups", + "no_one_can_sign_in_until_you_allow_a_group": "No one can sign in until you allow a group", + "one_group": "1 group", + "group_count": "{count} groups", + "public": "Public", + "confidential": "Confidential", + "copy_client_id": "Copy client ID", + "name_client_id_copied": "{name}’s client ID copied", + "could_not_copy_the_client_id": "Couldn’t copy the client ID", + "refresh_metadata": "Refresh metadata", + "metadata_document_for_name_refreshed": "Metadata document for {name} refreshed", + "delete_ellipsis": "Delete…", + "delete_client_ellipsis": "Delete client…", + "delete_name_question": "Delete {name}?", + "delete_name_action": "Delete {name}", + "delete_oidc_client_consequence": "Everyone loses access to {name} immediately, and its client ID and secrets stop working.", + "name_deleted": "{name} deleted", + "client_name_helper": "Shown on the sign-in and consent screens and in My apps.", + "confidential_client_tile_description": "Uses a client secret. For apps with a backend, like Grafana.", + "callback_url": "Callback URL", + "create_callback_url_helper": "Provided by your app. You can add more later.", + "set_a_custom_client_id": "Set a custom client ID", + "custom_client_id_helper": "It can’t be changed later.", + "new_clients_start_with_no_allowed_groups": "New clients start with no allowed groups. You’ll choose who can sign in next.", + "create_client": "Create client", + "name_created": "{name} created", + "details": "Details", + "endpoints": "Endpoints", + "discovery_url": "Discovery URL", + "authorization": "Authorization", + "end_session": "End session", + "jwks": "JWKS", + "copy_discovery_url": "Copy discovery URL", + "copy_issuer_url": "Copy issuer URL", + "copy_authorization_url": "Copy authorization URL", + "copy_token_url": "Copy token URL", + "copy_userinfo_url": "Copy userinfo URL", + "copy_end_session_url": "Copy end session URL", + "copy_jwks_url": "Copy JWKS URL", + "no_one_can_sign_in": "No one can sign in", + "limited_to_one_group": "Limited to 1 group", + "limited_to_count_groups": "Limited to {count} groups", + "data_preview": "Data preview", + "more_actions": "More actions", + "name_sections": "{name} sections", + "scim": "SCIM", + "some_settings_come_from_a_metadata_document": "Some settings come from a metadata document", + "cimd_client_read_only_description": "{name} describes itself through a Client ID Metadata Document, so the name, redirects and client type can’t be edited here.", + "copy_it_now_you_wont_see_it_again": "Copy it now. You won’t be able to see it again after you leave this page.", + "copy_label": "Copy {label}", + "oidc_client_logo_spec": "SVG, PNG or WebP. Square works best.", + "name_dark_logo": "{name} dark logo", + "using_light_logo": "Using light logo", + "set_by_the_metadata_document": "Set by the metadata document.", + "count_of_max": "{count} of {max}", + "opened_when_someone_launches_name_from_my_apps": "Opened from My apps.", + "no_callback_urls": "No callback URLs", + "no_logout_callback_urls": "No logout callback URLs", + "this_url_is_already_in_the_list": "This URL is already in the list", + "show_all_count": "Show all ({count})", + "show_less": "Show less", + "added_url": "Added {url}", + "oidc_client_advanced_summary": "Sign-out URLs, consent, PAR and token lifetimes", + "sign_out_section": "Sign-out", + "backchannel_logout_url_helper": "Notified when someone loses access, for example when they’re disabled.", + "pkce_row_description": "Protects the sign-in against intercepted authorization codes.", + "public_clients_always_use_pkce": "Public clients always use PKCE.", + "name_supports_pkce": "{name} supports PKCE. Turning it on is recommended.", + "requires_reauthentication_row_description": "People confirm with their passkey on every sign-in.", + "skip_consent_row_description": "Don’t ask people to approve {name}. Only for apps you trust.", + "requires_par_row_description": "The app must register its sign-in parameters with the PAR endpoint first.", + "token_lifetimes_section_description": "Shorter lifetimes are safer but make people sign in again more often.", + "refresh_token_lifetime_helper": "Restarts whenever {name} refreshes its tokens.", + "anyone_with_an_appname_account_can_sign_in": "Anyone with a {appName} account can sign in.", + "only_members_of_the_groups_below_can_sign_in": "Only members of the groups below can sign in.", + "allowed_groups": "Allowed groups", + "apis_name_may_request_tokens_for": "APIs {name} may request tokens for", + "apis_name_may_request_tokens_for_description": "APIs {name} may request tokens for, on behalf of people or as itself.", + "add_api_ellipsis": "Add API…", + "count_of_total": "{count} of {total}", + "no_access_lowercase": "no access", + "on_behalf_of_people_summary": "On behalf of people: {access}", + "as_itself_summary": "As itself: {access}", + "edit_access_ellipsis": "Edit access…", + "remove_access_ellipsis": "Remove access…", + "name_cant_request_tokens_for_any_api": "{name} can’t request tokens for any API.", + "client_secrets_section_description": "Several secrets can be active, so you can rotate one without downtime.", + "generate_secret_ellipsis": "Generate secret…", + "new_client_secret": "New client secret", + "public_clients_dont_have_client_secrets": "Public clients don’t have client secrets.", + "created_date": "Created {date}", + "expires_date": "Expires {date}", + "never_expires": "Never expires", + "delete_secret_prefix": "Delete secret {prefix}…", + "name_has_no_client_secrets_yet": "{name} has no client secrets yet.", + "generate_a_client_secret": "Generate a client secret", + "expires_after": "Expires after", + "generate_secret": "Generate secret", + "new_client_secret_generated": "New client secret generated", + "delete_client_secret_question": "Delete this client secret?", + "delete_secret": "Delete secret", + "delete_client_secret_consequence": "Apps using it can no longer authenticate.", + "client_secret_deleted": "Client secret deleted", + "federated_credentials_summary": "Federated credentials from another issuer, like a CI system", + "federated_credentials_section_description": "Let {name} authenticate with tokens from another issuer, like a CI system, instead of a secret.", + "add_credential": "Add credential", + "managed_by_the_clients_metadata_document": "Managed by the client’s metadata document.", + "credential_number": "Credential {number}", + "remove_credential_number": "Remove credential {number}", + "replay_protection_row_description": "A token can be used only once. Turn this off if your provider reuses tokens.", + "no_federated_credentials": "No federated credentials.", + "scim_section_description": "Sync {appName} users and groups to {name} automatically.", + "never_synced": "Never synced", + "synced_date": "Synced {date}", + "scim_endpoint_helper": "The SCIM 2.0 base URL {name} gives you.", + "enable_provisioning": "Enable provisioning", + "disable_provisioning_ellipsis": "Disable provisioning…", + "disable_provisioning_question": "Disable provisioning?", + "disable_provisioning": "Disable provisioning", + "disable_provisioning_consequence": "{appName} stops creating, updating and removing people and groups in {name}.", + "provisioning_enabled": "Provisioning enabled", + "provisioning_disabled": "Provisioning disabled", + "name_synced": "{name} synced", + "person": "Person", + "always_included": "Always included", + "only_openid": "Only openid", + "view": "View", + "couldnt_load_the_preview": "Couldn’t load the preview", + "audit_log_page_description": "Sign-ins and account changes for everyone at {appName}. Events older than 90 days are deleted.", + "app": "App", + "network": "Network", + "external_networks": "External networks", + "internal_networks": "Internal networks", + "add_count_users_to_groups": "Add {count} users to groups", + "add_name_to_groups": "Add {name} to groups", + "add_to_count_groups": "Add to {count} groups", + "add_to_one_group": "Add to 1 group", + "add_to_groups_ellipsis": "Add to groups…", + "admins": "Admins", + "all": "All", + "clear_selection": "Clear selection", + "copy_signup_link": "Copy signup link", + "copy_user_id": "Copy user ID", + "count_of_users": "{count} of {total} users", + "count_selected": "{count} selected", + "count_users_deleted": "{count} users deleted", + "count_users_disabled": "{count} users disabled", + "count_users_enabled": "{count} users enabled", + "create_code": "Create code", + "delete_count_users": "Delete {count} users", + "delete_count_users_question": "Delete {count} users?", + "delete_last_user_passkey_consequence": "{name} won’t be able to sign in with a passkey until they add a new one. Give them a login code so they can.", + "delete_user_passkey_consequence": "{name} can no longer sign in with it.", + "delete_last_own_passkey_consequence": "You won’t be able to sign in with a passkey until you add a new one.", + "delete_own_passkey_consequence": "You can still sign in with your other passkeys.", + "delete_signup_token_consequence": "The link {token} stops working. People who already signed up with it keep their accounts.", + "delete_signup_token_name": "Delete signup token {token}", + "delete_this_signup_token": "Delete this signup token?", + "delete_token": "Delete token", + "delete_user_ellipsis": "Delete user…", + "delete_users_consequence": "{names} can no longer sign in, and their passkeys and group memberships are deleted.", + "disable_count_users": "Disable {count} users", + "disable_count_users_question": "Disable {count} users?", + "disable_ellipsis": "Disable…", + "disable_name_action": "Disable {name}", + "disable_name_question": "Disable {name}?", + "disable_user_consequence": "{name} won’t be able to sign in until you enable the account again.", + "disable_user_ellipsis": "Disable user…", + "disable_users_consequence": "{names} won’t be able to sign in until you enable them again.", + "display_name_follows_names": "Filled in from the first and last name until you change it.", + "done": "Done", + "email_address_is_verified": "Email address is verified", + "email_used_for_login_codes": "Used for login codes and notifications.", + "enable_user": "Enable user", + "expired_on_date": "Expired {date}", + "expires_in_duration_at_time": "Expires in {duration} · {time}", + "expires_on_date": "Expires {date}", + "group_memberships_come_from_ldap": "Group memberships come from LDAP.", + "group_memberships_updated": "Group memberships updated", + "login_code_description": "{name} can use it once to sign in without a passkey.", + "login_code_ellipsis": "Login code…", + "login_code_for_name": "Login code for {name}", + "login_code_sent_to_email": "Login code sent to {email}", + "login_code_share_note": "Share it only with {name}. It works once.", + "members_count": "{count} members", + "members_count_one": "1 member", + "name_added": "{name} added", + "name_disabled": "{name} disabled", + "name_enabled": "{name} enabled", + "name_removed_from_group": "{name} removed from {group}", + "names_and_last": "{names} and {last}", + "names_and_others": "{names} and {count} others", + "new_signup_token": "New signup token", + "no_custom_claims": "No custom claims", + "no_email_address": "No email address", + "no_matches_for_query": "No matches for “{query}”", + "no_matches_for_filters": "No matches for these filters", + "no_events_match_filters": "No events match these filters", + "no_signup_tokens": "No signup tokens", + "not_in_any_group": "Not in any group", + "nothing_left_to_add": "Everything is already added.", + "only_you_so_far": "Only you so far", + "only_you_so_far_description": "Add people, or create a signup token so they can create their own account.", + "remove_name_from_group": "Remove {name} from {group}", + "search_groups": "Search groups…", + "search_users": "Search users…", + "send_by_email": "Send by email", + "set_by_the_ldap_admin_group": "Set by the LDAP admin group.", + "signup_link": "Signup link", + "signup_link_consequence": "Anyone with this link can create an account until it expires or is used up.", + "signup_token_created": "Signup token created", + "signup_token_deleted": "Signup token deleted", + "signup_token_groups_description": "Added to everyone who signs up with this token.", + "signup_tokens": "Signup tokens", + "signup_tokens_description": "Invite links that let people create their own account.", + "undo": "Undo", + "unverified": "Unverified", + "up_to_count_claims": "Up to {count} claims.", + "usage_of_limit_used": "{used} of {limit} used", + "user_custom_claims_description": "Added to the ID token with the profile scope. They override claims from groups.", + "user_has_no_passkeys_yet_give_login_code": "{name} has no passkeys yet. Give them a login code so they can add one.", + "user_id": "User ID", + "user_id_copied": "User ID copied", + "user_passkeys_section_description": "Admins can delete passkeys but can’t add them.", + "user_scopes": "User scopes", + "user_sections": "User sections", + "username_rule": "1 to 30 characters: letters, numbers, dots, dashes, underscores and @. Start and end with a letter or number.", + "users_count": "{count} users", + "users_count_one": "1 user", + "users_page_description": "People who can sign in with {appName}.", + "you": "You", + "auth_you_are_signed_out": "You’re signed out", + "auth_signed_out_description": "Some apps may keep you signed in until you sign out of them too.", + "auth_sign_in_again": "Sign in again", + "auth_sign_out_question": "Sign out of {appName} as {name}?", + "auth_sign_out_failed": "We couldn’t sign you out. Try again.", + "auth_sign_out_requested": "An app asked {appName} to sign you out on this device.", + "auth_stay_signed_in": "Stay signed in", + "auth_signups_invite_only": "Sign-ups are invite-only", + "auth_signups_invite_only_description": "You need an invite link to create an account. Ask your administrator for one.", + "auth_signups_closed": "Sign-ups are closed", + "auth_signups_closed_description": "{appName} isn’t accepting new accounts. If you need one, ask your administrator.", + "auth_go_to_sign_in": "Go to sign in", + "auth_create_your_account": "Create your {appName} account", + "auth_signup_description": "You’ll add a passkey next.", + "auth_create_account": "Create account", + "auth_invited_to": "You’ve been invited to", + "auth_set_up_pocket_id": "Set up Pocket ID", + "auth_setup_description": "This account becomes the administrator.", + "auth_create_admin_account": "Create admin account", + "auth_skip_passkey_title": "Skip adding a passkey?", + "auth_skip_passkey_description": "Without a passkey, you’ll need an admin’s help to sign in once this session ends.", + "auth_add_a_passkey": "Add a passkey", + "auth_passkey_added": "Passkey added", + "auth_add_passkey_description": "You’ll use it to sign in to {appName}. Your device will ask for Face ID, Touch ID, Windows Hello or your security key.", + "welcome_question": "Which features do you want to enable?", + "welcome_description": "Advanced features can overwhelm new users. Enable only what you need for now.", + "welcome_change_later": "You can change this any time later under Configuration.", + "auth_taking_you_to": "Taking you to {name}…", + "auth_continue_to": "Continue to {name}", + "auth_choose_an_account": "Choose an account", + "auth_confirm_its_you": "Confirm it’s you", + "auth_to_continue_to": "to continue to {name}", + "auth_confirm_with_passkey": "{name} asks you to confirm with your passkey before continuing.", + "auth_wants_to_sign_you_in": "wants to sign you in with your {appName} account", + "auth_continue_with_passkey": "Continue with passkey", + "auth_remove_access_anytime": "You can remove access anytime in My apps.", + "auth_not_registered_by_admin": "Not registered by your administrator", + "auth_not_registered_by_admin_description": "It describes itself at {host}. Continue only if you trust that site.", + "auth_registered_by_admin": "Registered by your administrator", + "auth_could_not_sign_you_in": "We couldn’t sign you in", + "auth_missing_interaction": "This sign-in link is incomplete. Start again from the app.", + "auth_connect_a_device": "Connect a device", + "auth_authorized": "Authorized", + "auth_device_authorized_description": "You can close this tab and go back to {name}.", + "auth_other_device_signed_in": "{device} is now signed in as {name}. You can close this tab.", + "auth_request_denied": "Request denied", + "auth_other_device_denied": "{device} wasn’t signed in.", + "auth_device_wants_to_sign_in": "{device} wants to sign in as you", + "auth_does_this_code_match": "Does this code match the one on that device?", + "auth_location": "Location", + "auth_ip_address": "IP address", + "auth_approving_asks_for_passkey": "Approving asks for your passkey.", + "auth_wants_to_use_account_on_device": "wants to use your {appName} account on another device", + "auth_device_code_expired_description": "Start again on the device to get a new code.", + "auth_enter_device_code": "Enter the code shown on your TV or other device.", + "auth_device_code": "Device code", + "auth_code_has_expired": "This code has expired.", + "auth_enter_a_new_code": "Enter a new code", + "auth_continue": "Continue", + "access_for_name": "Access for {name}", + "add_client_ellipsis": "Add client…", + "add_client_to_name": "Add a client to {name}", + "add_count_members": "Add {count} members", + "add_one_member": "Add 1 member", + "add_members_ellipsis": "Add members…", + "add_members_to_group": "Add members to {group}", + "allow_clients_ellipsis": "Allow clients…", + "allow_clients_for_group": "Allow clients for {group}", + "allow_count_clients": "Allow {count} clients", + "allow_one_client": "Allow 1 client", + "allowed": "Allowed", + "allowed_lowercase": "allowed", + "allowed_clients": "Allowed clients", + "allowed_clients_section_description": "Apps limited to selected groups that {group} can sign in to.", + "allowed_clients_updated": "Allowed clients updated", + "api": "API", + "api_access_updated": "API access updated", + "api_client_access_summary": "On behalf of people: {people} · As itself: {itself}", + "api_has_no_permissions_yet": "{name} has no permissions yet. Add them on the Permissions tab.", + "api_id": "API ID", + "api_id_copied": "API ID copied", + "api_key_description_placeholder": "What uses this key", + "api_key_expires_on_helper": "The key stops working at the start of this day.", + "api_key_renew_expires_on_helper": "The new key stops working at the start of this day.", + "api_key_name_helper": "So you can tell keys apart. 3 to 50 characters.", + "api_keys_page_description": "Keys for scripts and tools, like Terraform, that use the Pocket ID API.", + "api_name_helper": "Shown on the consent screen next to its permissions.", + "api_name_placeholder": "Bookings API", + "api_permissions_helper": "Removing a permission also removes it from every client.", + "api_resource_uri": "Resource URI", + "api_sections": "API sections", + "apis_page_description": "Your own services that clients can request access tokens for.", + "as_itself": "As itself", + "as_itself_description": "The client may request tokens for this API without a person, through the client credentials grant.", + "choose_what_the_client_may_ask_for": "Choose what the client may ask for.", + "clients": "Clients", + "clients_open_to_everyone": "{clients} are open to all users, so they aren’t listed.", + "copy_api_id": "Copy API ID", + "copy_api_key": "Copy API key", + "copy_group_id": "Copy group ID", + "copy_name": "Copy {name}", + "copy_resource_uri": "Copy resource URI", + "copy_key_now_before_closing": "Copy it now. You won’t be able to see it again after you close this.", + "delete_api_consequence_cimd": "Metadata document clients lose the permissions they were granted. No other clients have access to it.", + "delete_api_consequence_clients": "{clients} lose their access to its permissions right away. Tokens already issued for it stay valid until they expire.", + "delete_api_consequence_no_clients": "No clients have access to it.", + "delete_api_consequence_one_client": "{client} loses its access to its permissions right away. Tokens already issued for it stay valid until they expire.", + "delete_api_ellipsis": "Delete API…", + "delete_empty_group_consequence": "No one is in this group, so no one loses access.", + "delete_group_consequence": "Its {count} members lose the access and claims this group gives them right away. Their accounts stay.", + "delete_group_ellipsis": "Delete group…", + "every_client_already_has_access": "Every client already has access to {name}.", + "expires_in_days": "Expires in {count} days", + "expires_tomorrow": "Expires tomorrow", + "expires_on": "Expires on", + "group": "Group", + "group_custom_claims_description": "Added to members’ ID tokens with the profile scope. Claims set on a user override them.", + "group_friendly_name_placeholder": "Photographers", + "group_id": "Group ID", + "group_id_copied": "Group ID copied", + "group_members_come_from_ldap": "Members come from LDAP.", + "group_name_change_helper": "Changing it can break apps that check for this group.", + "group_name_helper": "The value apps receive in the groups claim.", + "group_name_placeholder": "photographers", + "group_removed_from_client": "{group} removed from {client}", + "group_sections": "Group sections", + "groups_page_description": "Groups decide who can sign in to which app and add claims to tokens.", + "identifiers": "Identifiers", + "loading": "Loading…", + "members": "Members", + "members_added": "Members added", + "metadata_document_clients": "Metadata document clients", + "metadata_document_clients_description": "Clients that identify themselves with a metadata document instead of being registered here.", + "metadata_documents": "Metadata documents", + "name_and_identifiers": "Name and identifiers", + "name_revoked": "{name} revoked", + "names": "Names", + "no_api_keys_yet_description": "Create a key to manage {appName} from scripts or Terraform through the Pocket ID API.", + "no_apis_yet_description": "Register an API when a client needs access tokens for one of your own services.", + "no_clients_can_request_tokens_for_this_api": "No clients can request tokens for this API yet.", + "no_clients_limited_to_this_group": "No apps are limited to this group.", + "no_members_yet": "No members yet", + "not_allowed": "Not allowed", + "not_available_public_client": "not available (public client)", + "on_behalf_of_people": "On behalf of people", + "on_behalf_of_people_description": "The client may request tokens for this API for the signed-in person.", + "permission": "Permission", + "permissions": "Permissions", + "public_clients_cant_act_as_themselves": "Public clients can’t use the client credentials grant, so they can’t act as themselves.", + "remove_access": "Remove access", + "remove_client_access_consequence": "{client} can no longer request tokens for this API. Its access to other APIs stays.", + "remove_client_access_question": "Remove {client}’s access to {api}?", + "remove_group": "Remove group", + "remove_group_from_client": "Remove {group} from {client}", + "remove_last_group_from_client_consequence": "No one will be able to sign in to {client} until you allow another group.", + "remove_last_group_from_client_question": "Remove the last group from {client}?", + "remove_permission_name": "Remove permission {name}", + "renew_api_key_consequence": "Renewing creates a new key. Update every integration that uses the old one.", + "renew_ellipsis": "Renew…", + "renew_key": "Renew key", + "renew_name_question": "Renew {name}?", + "resource_no_whitespace_or_fragment": "The resource can’t contain spaces or a # fragment", + "resource_uri_copied": "Resource URI copied", + "resource_uris_cant_be_changed": "Resource URIs can’t be changed after the API is created.", + "revoke_api_key_consequence": "Scripts and tools using this API key stop working immediately.", + "revoke_ellipsis": "Revoke…", + "revoke_name_action": "Revoke {name}", + "revoke_name_question": "Revoke {name}?", + "save_access": "Save access", + "search_clients": "Search clients…", + "search_members": "Search members…", + "search_people": "Search people…", + "add_to_groups": "Add to groups", + "add_members": "Add members", + "allow_clients": "Allow clients", + "client_open_to_everyone": "{client} is open to all users, so it isn’t listed.", + "count_more": "{count} more", + "expires_today": "Expires today", + "register_your_first_api": "Register your first API", + "create_your_first_api_key": "Create your first API key", + "add_your_first_group": "Add your first group", + "add_your_first_oidc_client": "Add your first OIDC client" } diff --git a/frontend/src/app.css b/frontend/src/app.css index b34eacda..1c14432a 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -10,7 +10,6 @@ --h-xs: 26px; --h-sm: 34px; --h-md: 42px; - --h-lg: 48px; --h-xl: 48px; --row-dense: 36px; --row: 44px; @@ -463,20 +462,64 @@ html.theme-switching *::after { transition-timing-function: var(--ease-out); } -.animate-fade-in { - animation: fadeIn 0.3s ease-out forwards; - opacity: 0; +/* A scroller fades at the edge that has more content; without overflow its scroll timeline is inactive, so nothing fades */ +@property --fade-start { + syntax: ''; + inherits: false; + initial-value: 0px; } -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(10px); +@property --fade-end { + syntax: ''; + inherits: false; + initial-value: 0px; +} + +@supports (animation-timeline: scroll()) { + .scroll-fade-x, + .scroll-fade-y { + animation: + scroll-fade-start linear both, + scroll-fade-end linear both; + animation-range: + 0 24px, + calc(100% - 24px) 100%; } + .scroll-fade-x { + mask-image: linear-gradient( + to right, + transparent, + #000 var(--fade-start), + #000 calc(100% - var(--fade-end)), + transparent + ); + animation-timeline: scroll(self inline), scroll(self inline); + } + + .scroll-fade-y { + mask-image: linear-gradient( + transparent, + #000 var(--fade-start), + #000 calc(100% - var(--fade-end)), + transparent + ); + animation-timeline: scroll(self), scroll(self); + } +} + +@keyframes scroll-fade-start { to { - opacity: 1; - transform: translateY(0); + --fade-start: 24px; + } +} + +@keyframes scroll-fade-end { + from { + --fade-end: 24px; + } + to { + --fade-end: 0px; } } @@ -489,11 +532,15 @@ html.theme-switching *::after { animation: none; } -::view-transition-group(settings-content) { +/* The phone bars swap instantly and stay above the content, whose new snapshot is otherwise painted last because only the new page has its name */ +::view-transition-group(phone-top-bar), +::view-transition-group(phone-nav), +::view-transition-group(phone-search) { + z-index: 1; animation: none; } -::view-transition-old(settings-content) { +::view-transition-old(settings-content-old) { animation: 120ms ease-out both vt-fade-out; } @@ -513,37 +560,6 @@ html.theme-switching *::after { } } -@keyframes bg-zoom { - 0% { - transform: scale(1.3); - } - - 100% { - transform: scale(1); - } -} - -.animate-bg-zoom { - transform-origin: center; - will-change: transform; - animation: bg-zoom 0.7s cubic-bezier(0.25, 0.1, 0.25, 1) forwards; -} - -@keyframes delayed-fade { - 0%, - 40% { - opacity: 0; - } - - 100% { - opacity: 1; - } -} - -.animate-delayed-fade { - animation: delayed-fade 0.5s ease-out forwards; -} - @keyframes shake { 0%, 100% { diff --git a/frontend/src/lib/components/api-access-cell.svelte b/frontend/src/lib/components/api-access-cell.svelte deleted file mode 100644 index 416aa42e..00000000 --- a/frontend/src/lib/components/api-access-cell.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -{#if !hasAccess} - {m.no_access()} -{:else} - - {m.permissions_granted_count({ granted: String(granted), total: String(total) })} - -{/if} diff --git a/frontend/src/lib/components/api-permissions-modal.svelte b/frontend/src/lib/components/api-permissions-modal.svelte index 88d3c65e..4f3ef1f1 100644 --- a/frontend/src/lib/components/api-permissions-modal.svelte +++ b/frontend/src/lib/components/api-permissions-modal.svelte @@ -1,14 +1,14 @@ -{#snippet KeyCell({ item }: { item: ApiPermission })} - {item.key} -{/snippet} - -{#snippet UserDelegatedCell({ item }: { item: ApiPermission })} - {@const implicit = implicitUserIds.includes(item.id)} - toggleUserPermission(item.id, checked)} - /> -{/snippet} - -{#snippet ClientAccessCell({ item }: { item: ApiPermission })} - toggleClientPermission(item.id, checked)} - /> -{/snippet} - - + + {title ?? api.name} - {m.select_the_access_this_client_may_request()} - {#if !showClientAccess} - {m.client_access_unavailable_for_public_clients()} - {/if} - {#if implicitUserAccess} - {m.access_granted_through_cimd_access()} - {/if} + {m.choose_what_the_client_may_ask_for()} + {#if !showClientAccess}{m.public_clients_cant_act_as_themselves()}{/if} -
- { - workingUserAccess = checked; - if (!checked) { - workingUser = []; - } - }} - /> +
+ + {#snippet control(props)} + { + userAccess = checked; + if (!checked) userIds = []; + }} + /> + {/snippet} + {#if showClientAccess} - { - if (!checked) { - workingClient = []; - } - }} - /> + + {#snippet control(props)} + { + clientAccess = checked; + if (!checked) clientIds = []; + }} + /> + {/snippet} + {/if}
- {#if api.permissions.length > 0} -
- -
+ {#if implicitUserAccess} +

+ + {m.access_granted_through_cimd_access()} +

{/if} -
- - -
+ {#if api.permissions.length > 0} + + + + + + {#if showClientAccess} + + {/if} + + + + {#each api.permissions as permission (permission.id)} + {@const implicit = implicitUserIds.includes(permission.id)} + + + + {#if showClientAccess} + + {/if} + + {/each} + +
{m.permission()} + {m.on_behalf_of_people()} + + {m.as_itself()} +
+ {permission.name} + + {permission.key} + + + { + userIds = toggle(userIds, permission.id, checked); + if (checked) userAccess = true; + }} + /> + + { + clientIds = toggle(clientIds, permission.id, checked); + if (checked) clientAccess = true; + }} + /> +
+ {:else} +

+ + {m.api_has_no_permissions_yet({ name: api.name })} +

+ {/if} + + + + + + diff --git a/frontend/src/lib/components/audit-log-list.svelte b/frontend/src/lib/components/audit-log-list.svelte index 0b44f1ca..8ce148a3 100644 --- a/frontend/src/lib/components/audit-log-list.svelte +++ b/frontend/src/lib/components/audit-log-list.svelte @@ -51,7 +51,7 @@ }); }); - export async function refresh() { + async function refresh() { isRefreshing = true; try { logs = isAdmin @@ -355,7 +355,6 @@ { requestOptions.pagination!.page = page; refresh(); diff --git a/frontend/src/lib/components/auth/action-panel.svelte b/frontend/src/lib/components/auth/action-panel.svelte new file mode 100644 index 00000000..d7cd4320 --- /dev/null +++ b/frontend/src/lib/components/auth/action-panel.svelte @@ -0,0 +1,14 @@ + + + + +
+ {@render children()} +
diff --git a/frontend/src/lib/components/auth/auth-heading.svelte b/frontend/src/lib/components/auth/auth-heading.svelte new file mode 100644 index 00000000..43449d57 --- /dev/null +++ b/frontend/src/lib/components/auth/auth-heading.svelte @@ -0,0 +1,35 @@ + + +

+ {#if typeof title === 'string'} + {title} + {:else} + {@render title()} + {/if} +

+

+ {#if tone === 'danger'} + + {:else if tone === 'info'} + + {/if} + {@render children?.()} +

diff --git a/frontend/src/lib/components/auth/auth-tile.svelte b/frontend/src/lib/components/auth/auth-tile.svelte new file mode 100644 index 00000000..429f772f --- /dev/null +++ b/frontend/src/lib/components/auth/auth-tile.svelte @@ -0,0 +1,26 @@ + + + + diff --git a/frontend/src/lib/components/auth/auth-util.ts b/frontend/src/lib/components/auth/auth-util.ts new file mode 100644 index 00000000..12349efa --- /dev/null +++ b/frontend/src/lib/components/auth/auth-util.ts @@ -0,0 +1,41 @@ +import { WebAuthnError } from '@simplewebauthn/browser'; +import { page } from '$app/state'; +import { m } from '#lib/paraglide/messages.js'; +import { cssMs } from '#lib/utils/style.ts'; + +// The one filled button of an auth step, full width and taller in the desktop card +export const authPrimaryClass = 'h-(--h-xl) w-full text-heading lg:h-13'; + +// Every other path on an auth step is a quiet text action with a full-height hit area +export const authQuietClass = + 'h-(--h-xl) px-3 text-body font-normal text-fg-muted hover:text-fg aria-busy:text-fg-muted'; + +// Other ways to sign in carries the current page, so signing in there returns to the app that asked +export function alternativeSignInHref() { + return page.url.pathname === '/login' + ? `/login/alternative${page.url.search}` + : `/login/alternative?redirect=${encodeURIComponent(page.url.pathname + page.url.search)}`; +} + +// Closing the browser's passkey prompt is the person's choice, so it reads as information rather than an error +export function isPasskeyPromptClosed(e: unknown) { + return ( + e instanceof WebAuthnError && + (e.code === 'ERROR_CEREMONY_ABORTED' || + (e.cause as Error | undefined)?.name === 'NotAllowedError') + ); +} + +export function takingYouMessage(redirect?: string | null) { + return !redirect || redirect.startsWith('/settings') + ? m.auth_taking_you_to_your_account() + : m.auth_taking_you_back(); +} + +// The success tile shows for a moment before the page moves on +export function successHold() { + return new Promise((resolve) => setTimeout(resolve, cssMs('--t-success-hold'))); +} + +// The address a login code was just emailed to, so the code page can say where to look and resend it +export const emailedLoginCode = { sentTo: '' }; diff --git a/frontend/src/routes/authorize/components/client-provider-images.svelte b/frontend/src/lib/components/auth/authorize-tiles.svelte similarity index 63% rename from frontend/src/routes/authorize/components/client-provider-images.svelte rename to frontend/src/lib/components/auth/authorize-tiles.svelte index 651dccfb..34205be5 100644 --- a/frontend/src/routes/authorize/components/client-provider-images.svelte +++ b/frontend/src/lib/components/auth/authorize-tiles.svelte @@ -1,13 +1,11 @@ -
+