diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..858d6ae2c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "i18n-ally.localesPaths": ["src/lib/locales"], + "i18n-ally.keystyle": "nested", + "i18n-ally.extract.ignoredByFiles": { + "src\\routes\\__layout.svelte": ["Coolify", "coolLabs logo"] + }, + "i18n-ally.sourceLanguage": "en", + "i18n-ally.enabledFrameworks": ["svelte"], + "i18n-ally.enabledParsers": ["js", "ts", "json"], + "i18n-ally.extract.autoDetect": true +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dabb5e10a..a4df5233d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,14 +1,26 @@ -# Welcome +# 👋 Welcome First of all, thank you for considering contributing to my project! It means a lot 💜. -# Technical skills required +## 🙋 Want to help? -- Node.js / Javascript -- Svelte / SvelteKit -- Prisma.io / SQL +If you begin in GitHub contribution, you can find the [first contribution](https://github.com/firstcontributions/first-contributions) and follow this guide. -# Recommended Pull Request Guideline +Follow the [introduction](#introduction) to get started then start contributing! + +This is a little list of what you can do to help the project: + +- [đŸ§‘â€đŸ’» Develop your own ideas](#developer-contribution) +- [🌐 Translate the project](#translation) +- [📄 Help sorting out the issues](#help-sorting-out-the-issues) +- [🎯 Test Pull Requests](#test-pull-requests) +- [✒ Help with the documentation](#help-with-the-documentation) + +## 👋 Introduction + +🔮 At the moment, Coolify **doesn't support Windows**. You must use Linux or MacOS. + +#### Recommended Pull Request Guideline - Fork the project - Clone your fork repo to local @@ -26,7 +38,7 @@ Due to the lock file, this repository is best with [pnpm](https://pnpm.io). I re You need to have [Docker Engine](https://docs.docker.com/engine/install/) installed locally. -## Setup development environment +#### Setup a local development environment - Copy `.env.template` to `.env` and set the `COOLIFY_APP_ID` environment variable to something cool. - Install dependencies with `pnpm install`. @@ -35,13 +47,27 @@ You need to have [Docker Engine](https://docs.docker.com/engine/install/) instal - Seed the database with base entities with `pnpm db:seed` - You can start coding after starting `pnpm dev`. -## Database migrations +#### How to start after you set up your local fork? + +This repository works better with [pnpm](https://pnpm.io) due to the lock file. I recommend you to give it a try and use `pnpm` as well because it is cool and efficient! + +You need to have [Docker Engine](https://docs.docker.com/engine/install/) installed locally. + +## đŸ§‘â€đŸ’» Developer contribution + +### Technical skills required + +- **Languages**: Node.js / Javascript / Typescript +- **Framework JS/TS**: Svelte / SvelteKit +- **Database ORM**: Prisma.io + +### Database migrations During development, if you change the database layout, you need to run `pnpm db:push` to migrate the database and create types for Prisma. You also need to restart the development process. If the schema is finalized, you need to create a migration file with `pnpm db:migrate ` where `nameOfMigration` is given by you. Make it sense. :) -## Tricky parts +### Tricky parts - BullMQ, the queue system Coolify uses, cannot be hot reloaded. So if you change anything in the files related to it, you need to restart the development process. I'm actively looking for a different queue/scheduler library. I'm open to discussion! @@ -115,3 +141,42 @@ You need to add a new folder to [src/routes/services/[id]](src/routes/services/[ If you need to show more details on the frontend, such as users/passwords, you need to add Svelte component to [src/routes/services/[id]/\_Services](src/routes/services/[id]/_Services) with an underscore. For example, see other files in that folder. You also need to add the new inputs to the `index.json.ts` file of the specific service, like for MinIO here: [src/routes/services/[id]/minio/index.json.ts](src/routes/services/[id]/minio/index.json.ts) + +## 🌐 Translate the project + +The project use [sveltekit-i18n](https://github.com/sveltekit-i18n/lib) to translate the project. +It follows the [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) to name languages. + +### Installation + +You must have gone throw all the [intro](#introduction) steps before you can start translating. + +It's only an advice, but I recommend you to use: + +- Visual Studio Code +- [i18n Ally for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=Lokalise.i18n-ally): ideal to see the progress of the translation. +- [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode): to get the syntax color for the project + +### Adding a language + +If your language doesn't appear in the [locales folder list](src/lib/locales/), follow the step below: + +1. In `src/lib/locales/`, Copy paste `en.json` and rename it with your language (eg: `cz.json`). +2. In the [lang.json](src/lib/lang.json) file, add a line after the first bracket (`{`) with `"ISO of your language": "Language",` (eg: `"cz": "Czech",`). +3. Have fun translating! + +### Additionnal pull requests steps + +Please add the emoji 🌐 to your pull request title to indicate that it is a translation. + +## 📄 Help sorting out the issues + +ToDo + +## 🎯 Test Pull Requests + +ToDo + +## ✒ Help with the documentation + +ToDo diff --git a/package.json b/package.json index 592bef339..5aa393bdf 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "svelte-preprocess": "4.10.6", "svelte-select": "4.4.7", "tailwindcss": "3.0.24", + "sveltekit-i18n": "2.1.2", "ts-node": "10.7.0", "tslib": "2.3.1", "typescript": "4.6.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ac48a0a0..9c608add7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -49,6 +49,7 @@ specifiers: svelte-preprocess: 4.10.6 svelte-select: 4.4.7 tailwindcss: 3.0.24 + sveltekit-i18n: 2.1.2 tailwindcss-scrollbar: 0.1.0 ts-node: 10.7.0 tslib: 2.3.1 @@ -109,6 +110,7 @@ devDependencies: svelte-select: 4.4.7 tailwindcss: 3.0.24_ts-node@10.7.0 ts-node: 10.7.0_de7c86b0cde507c63a0402da5b982bd3 + sveltekit-i18n: 2.1.2_svelte@3.46.4 tslib: 2.3.1 typescript: 4.6.3 @@ -422,6 +424,26 @@ packages: - supports-color dev: true + /@sveltekit-i18n/base/1.1.1_svelte@3.46.4: + resolution: + { + integrity: sha512-J/sMU0OwS3dCLOuilHMBqu8vZHuuXiNV9vFJx8Nb4/b5BlR/KCZ4bCXI8wZR02GHeCOYKZxWus07CM1scxa/jw== + } + peerDependencies: + svelte: ^3.x + dependencies: + svelte: 3.46.4 + optionalDependencies: + '@sveltekit-i18n/parser-default': 1.0.3 + dev: true + + /@sveltekit-i18n/parser-default/1.0.3: + resolution: + { + integrity: sha512-HheveklTjp3hxpYQhoHfyA6B4bQaUeSV5MQf2usIv/58UF2jY/YqhCAWj9bDBjufbuZc5pSz4BXvdX3WVT+viA== + } + dev: true + /@szmarczak/http-timer/5.0.1: resolution: { @@ -4955,6 +4977,19 @@ packages: engines: { node: '>= 8' } dev: true + /sveltekit-i18n/2.1.2_svelte@3.46.4: + resolution: + { + integrity: sha512-s5YxcbNd2EWNZaZR1A4Drt8s53E4fpUkN4XIWd3VRpw1pihZVWssqmBW1qkjQ6AB0kiu1Qwule+vt1HkbQOjrg== + } + peerDependencies: + svelte: ^3.x + dependencies: + '@sveltekit-i18n/base': 1.1.1_svelte@3.46.4 + '@sveltekit-i18n/parser-default': 1.0.3 + svelte: 3.46.4 + dev: true + /table/6.7.2: resolution: { diff --git a/src/app.d.ts b/src/app.d.ts index 37f419edf..ccdea3cee 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -31,6 +31,7 @@ interface SessionData { userId?: string | null; teamId?: string | null; permission?: string; + lang?: string; isAdmin?: boolean; expires?: string | null; } diff --git a/src/hooks.ts b/src/hooks.ts index fef98375f..33774922a 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -6,6 +6,7 @@ import { getUserDetails, sentry } from '$lib/common'; import { version } from '$lib/common'; import cookie from 'cookie'; import { dev } from '$app/env'; +import { locales } from '$lib/translations'; const whiteLabeled = process.env['COOLIFY_WHITE_LABELED'] === 'true'; const whiteLabelDetails = { @@ -20,6 +21,24 @@ export const handle = handleSession( }, async function ({ event, resolve }) { let response; + + const { url, request } = event; + + // Get defined locales + const supportedLocales = locales.get(); + let locale; + + if (event.locals.cookies['lang']) { + locale = event.locals.cookies['lang']; + } else if (!locale) { + locale = `${`${request.headers.get('accept-language')}`.match( + /[a-zA-Z]+?(?=-|_|,|;)/ + )}`.toLowerCase(); + } + + // Set default locale if user preferred locale does not match + if (!supportedLocales.includes(locale)) locale = 'en'; + try { if (event.locals.cookies) { if (event.locals.cookies['kit.session']) { @@ -29,7 +48,8 @@ export const handle = handleSession( teamId, permission, isAdmin: permission === 'admin' || permission === 'owner', - expires: event.locals.session.data.expires + expires: event.locals.session.data.expires, + lang: locale }; if (JSON.stringify(event.locals.session.data) !== JSON.stringify(newSession)) { @@ -39,12 +59,14 @@ export const handle = handleSession( } response = await resolve(event, { - ssr: !event.url.pathname.startsWith('/webhooks/success') + ssr: !event.url.pathname.startsWith('/webhooks/success'), + transformPage: ({ html }) => html.replace(//, ``) }); } catch (error) { console.log(error); response = await resolve(event, { - ssr: !event.url.pathname.startsWith('/webhooks/success') + ssr: !event.url.pathname.startsWith('/webhooks/success'), + transformPage: ({ html }) => html.replace(//, ``) }); response.headers.append( 'Set-Cookie', @@ -67,14 +89,34 @@ export const handle = handleSession( expires: new Date('Thu, 01 Jan 1970 00:00:01 GMT') }) ); - } finally { - return response; } + + response.headers.append( + 'Set-Cookie', + cookie.serialize('lang', locale, { + path: '/', + sameSite: 'strict', + maxAge: 30 * 24 * 60 * 60 + }) + ); + + return response; } ); export const getSession: GetSession = function ({ locals }) { + // Get defined locales + const supportedLocales = locales.get(); + let locale; + + if (locals.cookies) { + locale = supportedLocales.find( + (l) => `${l}`.toLowerCase() === `${locals.cookies['lang']}`.toLowerCase() + ); + } + return { + lang: locale, version, whiteLabeled, whiteLabelDetails, diff --git a/src/lib/lang.json b/src/lib/lang.json new file mode 100644 index 000000000..fdfc5bf80 --- /dev/null +++ b/src/lib/lang.json @@ -0,0 +1,4 @@ +{ + "fr": "Français", + "en": "English" +} diff --git a/src/lib/locales/en.json b/src/lib/locales/en.json new file mode 100644 index 000000000..20baecc8a --- /dev/null +++ b/src/lib/locales/en.json @@ -0,0 +1,326 @@ +{ + "layout": { + "update_done": "Update completed.", + "wait_new_version_startup": "Waiting for the new version to start...", + "new_version": "New version reachable. Reloading...", + "switch_to_a_different_team": "Switch to a different team...", + "update_available": "Update available" + }, + "error": { + "you_can_find_your_way_back": "You can find your way back", + "here": "here", + "you_are_lost": "Ooops you are lost! But don't be afraid!" + }, + "index": { + "dashboard": "Dashboard", + "applications": "Applications", + "destinations": "Destinations", + "git_sources": "Git Sources", + "databases": "Databases", + "services": "Services", + "teams": "Teams", + "not_implemented_yet": "Not implemented yet", + "database": "Database", + "settings": "Settings", + "global_settings": "Global Settings", + "secret": "Secret", + "team": "Team", + "logout": "Logout" + }, + "login": { + "already_logged_in": "Already logged in...", + "authenticating": "Authenticating...", + "login": "Login" + }, + "forms": { + "password": "Password", + "email": "Email address", + "passwords_not_match": "Passwords do not match.", + "password_again": "Password again", + "save": "Save", + "saving": "Saving...", + "name": "Name", + "value": "Value", + "action": "Action", + "is_required": "is required.", + "add": "Add", + "set": "Set", + "remove": "Remove", + "path": "Path", + "confirm_continue": "Are you sure to continue?", + "must_be_stopped_to_modify": "Must be stopped to modify.", + "port": "Port", + "default": "default", + "base_directory": "Base Directory", + "publish_directory": "Publish Directory", + "generated_automatically_after_start": "Generated automatically after start", + "roots_password": "Root's Password", + "root_user": "Root User", + "eg": "eg", + "user": "User", + "loading": "Loading...", + "version": "Version", + "host": "Host", + "already_used_for": "{{type}} already used for", + "configuration": "Configuration", + "engine": "Engine", + "network": "Network", + "ip_address": "IP Address", + "ssh_private_key": "SSH Private Key", + "type": "Type", + "html_url": "HTML URL", + "api_url": "API URL", + "organization": "Organization", + "new_password": "New password", + "super_secure_new_password": "Super secure new password", + "submit": "Submit", + "default_email_address": "Default Email Address", + "default_password": "Default Password", + "username": "Username", + "root_db_user": "Root DB User", + "root_db_password": "Root DB Password", + "api_port": "API Port", + "verifying": "Verifying", + "verify_emails_without_smtp": "Verify emails without SMTP", + "extra_config": "Extra Config", + "select_a_service": "Select a Service", + "select_a_service_version": "Select a Service version", + "removing": "Removing...", + "remove_domain": "Remove domain", + "public_port_range": "Public Port Range", + "public_port_range_explainer": "Ports used to expose databases/services/internal services.
Add them to your firewall (if applicable).

You can specify a range of ports, eg: 9000-9100", + "no_actions_available": "No actions available", + "admin_api_key": "Admin API key" + }, + "register": { + "register": "Register", + "registering": "Registering...", + "first_user": "You are registering the first user. It will be the administrator of your Coolify instance." + }, + "reset": { + "reset_password": "Reset", + "invalid_secret_key": "Invalid secret key.", + "secret_key": "Secret Key", + "find_path_secret_key": "You can find it in ~/coolify/.env (COOLIFY_SECRET_KEY)" + }, + "application": { + "configuration": { + "buildpack": { + "choose_this_one": "Choose this one..." + }, + "branch_already_in_use": "This branch is already used by another application. Webhooks won't work in this case for both applications. Are you sure you want to use it?", + "no_repositories_configured": "No repositories configured for your Git Application.", + "configure_it_now": "Configure it now", + "loading_repositories": "Loading repositories ...", + "select_a_repository": "Please select a repository", + "loading_branches": "Loading branches ...", + "select_a_repository_first": "Please select a repository first", + "select_a_branch": "Please select a branch", + "loading_groups": "Loading groups...", + "select_a_group": "Please select a group", + "loading_projects": "Loading projects...", + "select_a_project": "Please select a project", + "no_projects_found": "No projects found", + "no_branches_found": "No branches found", + "configure_build_pack": "Configure Build Pack", + "scanning_repository_suggest_build_pack": "Scanning repository to suggest a build pack for you...", + "found_lock_file": "Found lock file for {{packageManager}}. Using it for predefined commands commands.", + "configure_destination": "Configure Destination", + "no_configurable_destination": "No configurable Destination found", + "select_a_repository_project": "Select a Repository / Project", + "select_a_git_source": "Select a Git Source", + "no_configurable_git": "No configurable Git Source found", + "configuration_missing": "Configuration missing" + }, + "build": { + "queued_waiting_exec": "Queued and waiting for execution.", + "build_logs_of": "Build logs of", + "running": "Running", + "queued": "Queued", + "finished_in": "Finished in", + "load_more": "Load More", + "no_logs": "No logs found", + "waiting_logs": "Waiting for the logs..." + }, + "preview": { + "need_during_buildtime": "Need during buildtime?", + "setup_secret_app_first": "You can add secrets to PR/MR deployments. Please add secrets to the application first.
Useful for creating staging environments.", + "values_overwriting_app_secrets": "These values overwrite application secrets in PR/MR deployments. Useful for creating staging environments.", + "redeploy": "Redeploy", + "no_previews_available": "No previews available" + }, + "secrets": { + "secret_saved": "Secret saved.", + "use_isbuildsecret": "Use isBuildSecret", + "secrets_for": "Secrets for" + }, + "storage": { + "path_is_required": "Path is required.", + "storage_saved": "Storage saved.", + "storage_updated": "Storage updated.", + "storage_deleted": "Storage deleted.", + "persistent_storage_explainer": "You can specify any folder that you want to be persistent across deployments.
This is useful for storing data such as a database (SQLite) or a cache." + }, + "deployment_queued": "Deployment queued.", + "confirm_to_delete": "Are you sure you would like to delete '{{name}}'?", + "stop_application": "Stop application", + "permission_denied_stop_application": "You do not have permission to stop the application.", + "rebuild_application": "Rebuild application", + "permission_denied_rebuild_application": "You do not have permission to rebuild application.", + "build_and_start_application": "Build and start application", + "permission_denied_build_and_start_application": "You do not have permission to Build and start application.", + "configurations": "Configurations", + "secret": "Secrets", + "persistent_storage": "Persistent Storage", + "previews": "Previews", + "logs": "Application Logs", + "build_logs": "Build Logs", + "delete_application": "Delete application", + "permission_denied_delete_application": "You do not have permission to delete this application", + "domain_already_in_use": "Domain {{domain}} is already used.", + "dns_not_set_error": "DNS not set or propogated for {{domain}}.

Please check your DNS settings.", + "settings_saved": "Settings saved.", + "dns_not_set_partial_error": "DNS not set", + "git_source": "Git Source", + "git_repository": "Git Repository", + "build_pack": "Build Pack", + "destination": "Destination", + "application": "Application", + "url_fqdn": "URL (FQDN)", + "domain_fqdn": "Domain (FQDN)", + "https_explainer": "If you specify https, the application will be accessible only over https. SSL certificate will be generated for you.
If you specify www, the application will be redirected (302) from non-www and vice versa.

To modify the domain, you must first stop the application.

You must set your DNS to point to the server IP in advance.", + "ssl_www_and_non_www": "Generate SSL for www and non-www?", + "ssl_explainer": "It will generate certificates for both www and non-www.
You need to have both DNS entries set in advance.

Useful if you expect to have visitors on both.", + "install_command": "Install Command", + "build_command": "Build Command", + "start_command": "Start Command", + "directory_to_use_explainer": "Directory to use as the base for all commands.
Could be useful with monorepos.", + "publish_directory_explainer": "Directory containing all the assets for deployment.
For example: dist,_site or public.", + "features": "Features", + "enable_automatic_deployment": "Enable Automatic Deployment", + "enable_auto_deploy_webhooks": "Enable automatic deployment through webhooks.", + "enable_mr_pr_previews": "Enable MR/PR Previews", + "enable_preview_deploy_mr_pr_requests": "Enable preview deployments from pull or merge requests.", + "debug_logs": "Debug Logs", + "enable_debug_log_during_build": "Enable debug logs during build phase.
Sensitive information could be visible and saved in logs.", + "cant_activate_auto_deploy_without_repo": "Cannot activate automatic deployments until only one application is defined for this repository / branch.", + "no_applications_found": "No applications found", + "secret__batch_dot_env": "Paste .env file", + "batch_secrets": "Batch add secrets" + }, + "general": "General", + "database": { + "default_database": "Default Database", + "generated_automatically_after_set_to_public": "Generated automatically after set to public", + "connection_string": "Connection String", + "set_public": "Set it public", + "warning_database_public": "Your database will be reachable over the internet.
Take security seriously in this case!", + "change_append_only_mode": "Change append only mode", + "warning_append_only": "Useful if you would like to restore redis data from a backup.
Database restart is required.", + "select_database_type": "Select a Database type", + "select_database_version": "Select a Database version", + "confirm_stop": "Are you sure you would like to stop {{name}}?", + "stop_database": "Stop database", + "permission_denied_stop_database": "You do not have permission to stop the database.", + "start_database": "Start database", + "permission_denied_start_database": "You do not have permission to start the database.", + "delete_database": "Delete Database", + "permission_denied_delete_database": "You do not have permission to delete a Database", + "no_databases_found": "No databases found" + }, + "destination": { + "delete_destination": "Delete Destination", + "permission_denied_delete_destination": "You do not have permission to delete this destination", + "add_to_coolify": "Add to Coolify", + "coolify_proxy_stopped": "Coolify Proxy stopped!", + "coolify_proxy_started": "Coolify Proxy started!", + "confirm_restart_proxy": "Are you sure you want to restart the proxy? Everything will be reconfigured in ~10 secs.", + "coolify_proxy_restarting": "Coolify Proxy restarting...", + "restarting_please_wait": "Restarting... please wait...", + "force_restart_proxy": "Force restart proxy", + "use_coolify_proxy": "Use Coolify Proxy?", + "no_destination_found": "No destination found", + "new_error_network_already_exists": "Network {{network}} already configured for another team!", + "new": { + "saving_and_configuring_proxy": "Saving and configuring proxy...", + "install_proxy": "This will install a proxy on the destination to allow you to access your applications and services without any manual configuration (recommended for Docker).

Databases will have their own proxy.", + "add_new_destination": "Add New Destination", + "predefined_destinations": "Predefined destinations" + } + }, + "sources": { + "local_docker": "Local Docker", + "remote_docker": "Remote Docker", + "organization_explainer": "Fill it if you would like to use an organization's as your Git Source. Otherwise your user will be used." + }, + "source": { + "new": { + "git_source": "Add New Git Source", + "official_providers": "Official providers" + }, + "no_git_sources_found": "No git sources found", + "delete_git_source": "Delete Git Source", + "permission_denied": "You do not have permission to delete a Git Source", + "create_new_app": "Create new {{name}} App", + "change_app_settings": "Change {{name}} App Settings", + "install_repositories": "Install Repositories", + "application_id": "Application ID", + "group_name": "Group Name", + "oauth_id": "OAuth ID", + "oauth_id_explainer": "The OAuth ID is the unique identifier of the GitLab application.
You can find it in the URL of your GitLab OAuth Application.", + "register_oauth_gitlab": "Register new OAuth application on GitLab", + "gitlab": { + "self_hosted": "Instance-wide application (self-hosted)", + "user_owned": "User owned application", + "group_owned": "Group owned application", + "gitlab_application_type": "GitLab Application Type", + "already_configured": "GitLab App is already configured." + }, + "github": { + "redirecting": "Redirecting to Github..." + } + }, + "services": { + "all_email_verified": "All email verified. You can login now.", + "generate_www_non_www_ssl": "It will generate certificates for both www and non-www.
You need to have both DNS entries set in advance.

Service needs to be restarted." + }, + "service": { + "stop_service": "Stop Service", + "permission_denied_stop_service": "You do not have permission to stop the service.", + "start_service": "Start Service", + "permission_denied_start_service": "You do not have permission to start the service.", + "delete_service": "Delete Service", + "permission_denied_delete_service": "You do not have permission to delete a service.", + "no_service": "No services found" + }, + "setting": { + "permission_denied": "You do not have permission to do this. \\nAsk an admin to modify your permissions.", + "domain_removed": "Domain removed", + "ssl_explainer": "If you specify https, Coolify will be accessible only over https. SSL certificate will be generated for you.
If you specify www, Coolify will be redirected (302) from non-www and vice versa.", + "must_remove_domain_before_changing": "Must remove the domain before you can change this setting.", + "registration_allowed": "Registration allowed?", + "registration_allowed_explainer": "Allow further registrations to the application.
It's turned off after the first registration.", + "coolify_proxy_settings": "Coolify Proxy Settings", + "credential_stat_explainer": "Credentials for stats page." + }, + "team": { + "pending_invitations": "Pending invitations", + "accept": "Accept", + "delete": "Delete", + "member": "member(s)", + "root": "(root)", + "invited_with_permissions": "Invited to {{teamName}} with {{permission}} permission.", + "members": "Members", + "root_team_explainer": "This is the root team. That means members of this group can manage instance wide settings and have all the priviliges in Coolify (imagine like root user on Linux).", + "permission": "Permission", + "you": "(You)", + "promote_to": "Promote to {{grade}}", + "revoke_invitation": "Revoke invitation", + "pending_invitation": "Pending invitation", + "invite_new_member": "Invite new member", + "send_invitation": "Send invitation", + "invite_only_register_explainer": "You can only invite registered users at the moment - will be extended soon.", + "admin": "Admin", + "read": "Read" + } +} diff --git a/src/lib/locales/fr.json b/src/lib/locales/fr.json new file mode 100644 index 000000000..36f10dfb4 --- /dev/null +++ b/src/lib/locales/fr.json @@ -0,0 +1,322 @@ +{ + "application": { + "application": "Application", + "build": { + "build_logs_of": "CrĂ©er des journaux de", + "finished_in": "Fini en", + "load_more": "Charger plus", + "no_logs": "Aucun journal trouvĂ©", + "queued": "En file d'attente", + "queued_waiting_exec": "En file d'attente et en attente d'exĂ©cution.", + "running": "Fonctionnement", + "waiting_logs": "En attente des logs..." + }, + "build_and_start_application": "Build et dĂ©marrer l'application", + "build_command": "Commande Build", + "build_logs": "CrĂ©er des journaux", + "build_pack": "Pack de Build", + "cant_activate_auto_deploy_without_repo": "Impossible d'activer les dĂ©ploiements automatiques tant qu'une seule application n'est pas dĂ©finie pour ce dĂ©pĂŽt/branche.", + "configuration": { + "branch_already_in_use": "Cette branche est dĂ©jĂ  utilisĂ©e par une autre application. \nLes webhooks ne fonctionneront pas dans ce cas pour les deux applications. \nÊtes-vous sĂ»r de vouloir l'utiliser ?", + "buildpack": { + "choose_this_one": "Choisir celui-ci..." + }, + "configuration_missing": "Configuration manquante", + "configure_build_pack": "Configurer le pack de build", + "configure_destination": "Configurer la destination", + "configure_it_now": "Configurez-le maintenant", + "found_lock_file": "Fichier .lock trouvĂ© pour {{packageManager}}. \nL'utiliser pour les commandes prĂ©dĂ©finies.", + "loading_branches": "Chargement des branches...", + "loading_groups": "Chargement des groupes...", + "loading_projects": "Chargement des projets...", + "loading_repositories": "Chargement des dĂ©pĂŽts Git...", + "no_branches_found": "Aucune branche trouvĂ©e", + "no_configurable_destination": "Aucune destination configurable trouvĂ©e", + "no_configurable_git": "Aucune source Git configurable trouvĂ©e", + "no_projects_found": "Aucun projet trouvĂ©", + "no_repositories_configured": "Aucun dĂ©pĂŽt Git configurĂ© pour votre application.", + "scanning_repository_suggest_build_pack": "Analyse du dĂ©pĂŽt pour vous suggĂ©rer un pack de Build...", + "select_a_branch": "Veuillez sĂ©lectionner une branche", + "select_a_git_source": "SĂ©lectionnez une source Git", + "select_a_group": "Veuillez sĂ©lectionner un groupe", + "select_a_project": "Veuillez sĂ©lectionner un projet", + "select_a_repository": "Veuillez sĂ©lectionner un dĂ©pĂŽt", + "select_a_repository_first": "Veuillez d'abord sĂ©lectionner un dĂ©pĂŽt", + "select_a_repository_project": "SĂ©lectionnez un dĂ©pĂŽt / projet" + }, + "configurations": "Configurations", + "confirm_to_delete": "Voulez-vous vraiment supprimer '{{name}}'?", + "debug_logs": "Journaux de dĂ©bogage", + "delete_application": "Supprimer l'application", + "deployment_queued": "DĂ©ploiement en file d'attente.", + "destination": "Destination", + "directory_to_use_explainer": "RĂ©pertoire Ă  utiliser comme base pour toutes les commandes.
Pourrait ĂȘtre utile avec monorepos.", + "dns_not_set_error": "DNS non dĂ©fini ou propagĂ© pour {{domain}}.

Veuillez vérifier vos paramÚtres DNS.", + "dns_not_set_partial_error": "DNS non défini", + "domain_already_in_use": "Le domaine {{domain}} est déjà utilisé.", + "domain_fqdn": "Domaine (FQDN)", + "url_fqdn": "URL (FQDN)", + "enable_auto_deploy_webhooks": "Activez le déploiement automatique via des webhooks.", + "enable_automatic_deployment": "Activer le déploiement automatique", + "enable_debug_log_during_build": "Activez les journaux de débogage pendant la phase de build.
Les informations sensibles peuvent ĂȘtre visibles et enregistrĂ©es dans les journaux.", + "enable_mr_pr_previews": "Activer les aperçus MR/PR", + "enable_preview_deploy_mr_pr_requests": "Activez les dĂ©ploiements de prĂ©visualisation Ă  partir de demandes d'extraction ou de fusion.", + "features": "CaractĂ©ristiques", + "git_repository": "DĂ©pĂŽt Git", + "git_source": "Source Git", + "https_explainer": "Si vous spĂ©cifiez https, l'application sera accessible uniquement via https. \nUn certificat SSL sera gĂ©nĂ©rĂ© pour vous.
Si vous spécifiez www, l'application sera redirigée (302) à partir de non-www et vice versa \n.

Pour modifier le domaine, vous devez d'abord arrĂȘter l'application.

Vous devez configurer, en avance, votre DNS pour pointer vers l'IP du serveur.", + "install_command": "Commande d'installation", + "logs": "Journaux des applications", + "no_applications_found": "Aucune application trouvĂ©e", + "permission_denied_build_and_start_application": "Vous n'ĂȘtes pas autorisĂ© Ă  crĂ©er et Ă  dĂ©marrer l'application.", + "permission_denied_delete_application": "Vous n'ĂȘtes pas autorisĂ© Ă  supprimer cette application", + "permission_denied_rebuild_application": "Vous n'ĂȘtes pas autorisĂ© Ă  re-build l'application.", + "permission_denied_stop_application": "Vous n'ĂȘtes pas autorisĂ© Ă  arrĂȘter l'application.", + "persistent_storage": "Stockage persistant", + "preview": { + "need_during_buildtime": "Besoin pendant la build ?", + "no_previews_available": "Aucun aperçu disponible", + "redeploy": "RedĂ©ployer", + "setup_secret_app_first": "Vous pouvez ajouter des secrets aux dĂ©ploiements PR/MR. \nVeuillez d'abord ajouter des secrets Ă  l'application. \n
Utile pour créer des environnements de mise en scÚne.", + "values_overwriting_app_secrets": "Ces valeurs remplacent les secrets d'application dans les déploiements PR/MR. \nUtile pour créer des environnements de mise en scÚne." + }, + "previews": "Aperçus", + "publish_directory_explainer": "Répertoire contenant tous les actifs à déployer. \n
Par exemple : dist,_site ou public.", + "rebuild_application": "Re-build l'application", + "secret": "secrets", + "secrets": { + "secret_saved": "Secret enregistré.", + "secrets_for": "secrets pour", + "use_isbuildsecret": "Utiliser isBuildSecret" + }, + "settings_saved": "ParamÚtres sauvegardés.", + "ssl_explainer": "Il générera des certificats pour www et non-www. \n
Vous devez avoir les deux entrées DNS définies à l'avance.

Utile si vous prĂ©voyez d'avoir des visiteurs sur les deux.", + "ssl_www_and_non_www": "GĂ©nĂ©rer SSL pour www et non-www ?", + "start_command": "DĂ©marrer la commande", + "stop_application": "ArrĂȘter l'application", + "storage": { + "path_is_required": "Le chemin est requis.", + "persistent_storage_explainer": "Vous pouvez spĂ©cifier n'importe quel dossier que vous souhaitez conserver dans les dĂ©ploiements. \n
Ceci est utile pour stocker des donnĂ©es telles qu'une base de donnĂ©es (SQLite) ou un cache.", + "storage_deleted": "Stockage supprimĂ©.", + "storage_saved": "Stockage enregistrĂ©.", + "storage_updated": "Stockage mis Ă  jour." + } + }, + "database": { + "change_append_only_mode": "Changer le mode d'ajout uniquement", + "confirm_stop": "Êtes-vous sĂ»r de vouloir arrĂȘter {{name}} ?", + "connection_string": "Connexion string", + "default_database": "Base de donnĂ©es par dĂ©faut", + "delete_database": "Supprimer la base de donnĂ©es", + "generated_automatically_after_set_to_public": "GĂ©nĂ©rĂ© automatiquement aprĂšs avoir Ă©tĂ© dĂ©fini sur public", + "no_databases_found": "Aucune base de donnĂ©es trouvĂ©e", + "permission_denied_delete_database": "Vous n'ĂȘtes pas autorisĂ© Ă  supprimer une base de donnĂ©es", + "permission_denied_start_database": "Vous n'ĂȘtes pas autorisĂ© Ă  dĂ©marrer la base de donnĂ©es.", + "permission_denied_stop_database": "Vous n'ĂȘtes pas autorisĂ© Ă  arrĂȘter la base de donnĂ©es.", + "select_database_type": "SĂ©lectionnez un type de base de donnĂ©es", + "select_database_version": "SĂ©lectionnez une version de la base de donnĂ©es", + "set_public": "Rendre public", + "start_database": "DĂ©marrer la base de donnĂ©es", + "stop_database": "ArrĂȘter la base de donnĂ©es", + "warning_append_only": "Utile si vous souhaitez restaurer des donnĂ©es Redis Ă  partir d'une sauvegarde.
Le redémarrage de la base de données est nécessaire.", + "warning_database_public": "Votre base de données sera accessible depuis Internet. \n
Prenez la sĂ©curitĂ© au sĂ©rieux dans ce cas!" + }, + "destination": { + "add_to_coolify": "Ajouter Ă  Coolify", + "confirm_restart_proxy": "Voulez-vous vraiment redĂ©marrer le proxy? \nTout sera reconfigurĂ© en ~10 secondes.", + "coolify_proxy_restarting": "RedĂ©marrage du Proxy Coolify...", + "coolify_proxy_started": "Proxy Coolify dĂ©marrĂ©!", + "coolify_proxy_stopped": "Proxy Coolify arrĂȘtĂ©!", + "delete_destination": "Supprimer le destinataire", + "force_restart_proxy": "Forcer le redĂ©marrage du proxy", + "new": { + "add_new_destination": "Ajouter une nouvelle destination", + "install_proxy": "Cela installera un proxy sur la destination pour vous permettre d'accĂ©der Ă  vos applications et services sans aucune configuration manuelle (recommandĂ© pour Docker).

Les bases de donnĂ©es auront leur propre proxy.", + "predefined_destinations": "Destinations prĂ©dĂ©finies", + "saving_and_configuring_proxy": "Enregistrement et configuration du proxy..." + }, + "new_error_network_already_exists": "RĂ©seau {{network}} dĂ©jĂ  configurĂ© pour une autre Ă©quipe !", + "no_destination_found": "Aucune destination trouvĂ©e", + "permission_denied_delete_destination": "Vous n'ĂȘtes pas autorisĂ© Ă  supprimer cette destination", + "restarting_please_wait": "RedĂ©marrage... veuillez patienter...", + "use_coolify_proxy": "Utiliser le Proxy Coolify ?" + }, + "error": { + "here": "ici", + "you_are_lost": "Oups vous ĂȘtes perdu ! \nMais n'ayez pas peur !", + "you_can_find_your_way_back": "Tu peux retrouver ton chemin" + }, + "forms": { + "action": "action", + "add": "Ajouter", + "already_used_for": "{{type}} dĂ©jĂ  utilisĂ© pour", + "api_port": "Port API", + "api_url": "URL de l'API", + "base_directory": "RĂ©pertoire de base", + "configuration": "Configuration", + "confirm_continue": "Êtes-vous sĂ»r de continuer ?", + "default": "dĂ©faut", + "default_email_address": "Adresse e-mail par dĂ©faut", + "default_password": "Mot de passe par dĂ©faut", + "eg": "ex", + "email": "Adresse e-mail", + "engine": "Moteur", + "extra_config": "Configuration supplĂ©mentaire", + "generated_automatically_after_start": "GĂ©nĂ©rĂ© automatiquement aprĂšs le dĂ©marrage", + "host": "HĂŽte", + "html_url": "URL HTML", + "ip_address": "Adresse IP", + "is_required": "est requis.", + "loading": "Chargement...", + "must_be_stopped_to_modify": "Doit ĂȘtre arrĂȘtĂ© pour ĂȘtre modifiĂ©.", + "name": "Nom", + "network": "RĂ©seau", + "new_password": "Nouveau mot de passe", + "no_actions_available": "Aucune action disponible", + "organization": "Organisation", + "password": "Mot de passe", + "password_again": "Mot de passe Ă  nouveau", + "passwords_not_match": "Les mots de passe ne correspondent pas.", + "path": "Chemin", + "port": "Port", + "public_port_range": "Gamme de ports publics", + "public_port_range_explainer": "Ports utilisĂ©s pour exposer les bases de donnĂ©es/services/services internes.
Ajoutez-les à votre pare-feu (le cas échéant).

Vous pouvez spĂ©cifier une plage de ports, par exemple : 9000-9100", + "publish_directory": "Publier le rĂ©pertoire", + "remove": "Retirer", + "remove_domain": "Supprimer le domaine", + "removing": "Suppression...", + "root_db_password": "Mot de passe root de la base de donnĂ©es", + "root_db_user": "Utilisateur root de la base de donnĂ©es", + "root_user": "Utilisateur root", + "roots_password": "Mot de passe de l'utilisateur root", + "save": "sauvegarder", + "saving": "Sauvegarde...", + "select_a_service": "SĂ©lectionnez un service", + "select_a_service_version": "SĂ©lectionnez une version de service", + "set": "RĂ©gler", + "ssh_private_key": "ClĂ© privĂ©e SSH", + "submit": "Nous faire parvenir", + "super_secure_new_password": "Nouveau mot de passe super sĂ©curisĂ©", + "type": "Taper", + "user": "Utilisateur", + "username": "Nom d'utilisateur", + "value": "Valeur", + "verify_emails_without_smtp": "VĂ©rifier les e-mails sans SMTP", + "verifying": "VĂ©rification", + "version": "Version" + }, + "general": "GĂ©nĂ©ral", + "index": { + "applications": "Applications", + "dashboard": "Tableau de bord", + "database": "Base de donnĂ©es", + "databases": "Bases de donnĂ©es", + "destinations": "Destinations", + "git_sources": "Sources Git", + "global_settings": "ParamĂštres globaux", + "logout": "Se dĂ©connecter", + "not_implemented_yet": "Pas encore implĂ©mentĂ©", + "secret": "Secret", + "services": "Services", + "settings": "RĂ©glages", + "team": "Équipe", + "teams": "Équipes" + }, + "layout": { + "new_version": "Nouvelle version accessible. \nRechargement...", + "switch_to_a_different_team": "Changer d'Ă©quipe...", + "update_available": "Mise Ă  jour disponible", + "update_done": "Mise Ă  jour terminĂ©e.", + "wait_new_version_startup": "En attendant le lancement de la nouvelle version..." + }, + "login": { + "already_logged_in": "DĂ©jĂ  connectĂ©...", + "authenticating": "Authentification...", + "login": "Connexion" + }, + "register": { + "first_user": "Vous enregistrez le premier utilisateur. \nCe sera l'administrateur de votre instance Coolify.", + "register": "S'inscrire" + }, + "reset": { + "find_path_secret_key": "Vous pouvez le trouver dans ~/coolify/.env (COOLIFY_SECRET_KEY)", + "invalid_secret_key": "ClĂ© secrĂšte invalide.", + "reset_password": "RĂ©initialiser", + "secret_key": "Clef secrĂšte" + }, + "service": { + "delete_service": "Supprimer le service", + "no_service": "Aucun service trouvĂ©", + "permission_denied_delete_service": "Vous n'ĂȘtes pas autorisĂ© Ă  supprimer un service.", + "permission_denied_start_service": "Vous n'ĂȘtes pas autorisĂ© Ă  dĂ©marrer le service.", + "permission_denied_stop_service": "Vous n'ĂȘtes pas autorisĂ© Ă  arrĂȘter le service.", + "start_service": "DĂ©marrer le service", + "stop_service": "Stopper le service" + }, + "services": { + "all_email_verified": "Tous les e-mails sont vĂ©rifiĂ©s. \nVous pouvez vous connecter maintenant.", + "generate_www_non_www_ssl": "Il gĂ©nĂ©rera des certificats pour www et non-www. \n
Vous devez avoir les deux entrées DNS définies à l'avance.

Le service devra ĂȘtre redĂ©marrĂ©." + }, + "setting": { + "coolify_proxy_settings": "ParamĂštres du proxy Coolify", + "credential_stat_explainer": "Identifiants pour la page statistiques.", + "domain_removed": "Domaine supprimĂ©", + "must_remove_domain_before_changing": "Vous devez supprimer le domaine avant de pouvoir modifier ce paramĂštre.", + "permission_denied": "Vous n'avez pas la permission de faire cela. \n\\nDemandez Ă  un administrateur de modifier vos autorisations.", + "registration_allowed": "Inscription autorisĂ©e ?", + "registration_allowed_explainer": "Autoriser d'autres inscriptions Ă  l'application. \n
Il est désactivé aprÚs la premiÚre inscription.", + "ssl_explainer": "Si vous spécifiez https, Coolify sera accessible uniquement via https. \nUn certificat SSL sera généré pour vous.
Si vous spécifiez www, Coolify sera redirigé (302) à partir de non-www et vice versa." + }, + "source": { + "application_id": "ID d'application", + "change_app_settings": "Modifier les paramÚtres de l'application {{name}}", + "create_new_app": "Créer une nouvelle application {{name}}", + "delete_git_source": "Supprimer la source Git", + "github": { + "redirecting": "Redirection vers Github..." + }, + "gitlab": { + "already_configured": "L'application GitLab est déjà configurée.", + "gitlab_application_type": "Type d'application GitLab", + "group_owned": "Application détenue par le groupe", + "self_hosted": "Application à l'échelle de l'instance (auto-hébergée)", + "user_owned": "Application appartenant à l'utilisateur" + }, + "group_name": "Nom de groupe", + "install_repositories": "Installer les dépÎts", + "new": { + "git_source": "Ajouter une nouvelle source Git", + "official_providers": "Fournisseurs officiels" + }, + "no_git_sources_found": "Aucune source git trouvée", + "oauth_id": "ID OAuth", + "oauth_id_explainer": "L'identifiant OAuth est l'identifiant unique de l'application GitLab. \n
Vous pouvez le trouver dans l'URL de votre application GitLab OAuth.", + "permission_denied": "Vous n'ĂȘtes pas autorisĂ© Ă  supprimer une source Git", + "register_oauth_gitlab": "Enregistrer une nouvelle application OAuth sur GitLab" + }, + "sources": { + "local_docker": "Docker local", + "organization_explainer": "Remplissez-le si vous souhaitez utiliser une organisation comme source Git. \nSinon, votre utilisateur sera utilisĂ©.", + "remote_docker": "Station d'accueil Ă  distance" + }, + "team": { + "accept": "J'accepte", + "admin": "Administrateur", + "delete": "Supprimer", + "invite_new_member": "Inviter un nouveau membre", + "invite_only_register_explainer": "Vous ne pouvez inviter que des utilisateurs enregistrĂ©s pour le moment - sera bientĂŽt prolongĂ©.", + "invited_with_permissions": "InvitĂ© Ă  {{teamName}} avec {{permission}} autorisation.", + "member": "membre(s)", + "members": "Membres", + "pending_invitation": "Invitation en attente", + "pending_invitations": "Invitations en attente", + "permission": "Autorisation", + "promote_to": "Promouvoir Ă  {{grade}}", + "read": "Lire", + "revoke_invitation": "RĂ©voquer l'invitation", + "root": "(suprĂȘme)", + "root_team_explainer": "Il s'agit de l'Ă©quipe suprĂȘme. \nCela signifie que les membres de ce groupe peuvent gĂ©rer les paramĂštres Ă  l'Ă©chelle de l'instance et avoir tous les privilĂšges dans Coolify (imaginez comme un utilisateur root sous Linux).", + "send_invitation": "Envoyer une invitation", + "you": "(Toi)" + } +} diff --git a/src/lib/translations.ts b/src/lib/translations.ts new file mode 100644 index 000000000..dcda99dcb --- /dev/null +++ b/src/lib/translations.ts @@ -0,0 +1,26 @@ +import i18n from 'sveltekit-i18n'; +import lang from './lang.json'; + +/** @type {import('sveltekit-i18n').Config} */ +export const config = { + fallbackLocale: 'en', + translations: { + en: { lang }, + fr: { lang } + }, + loaders: [ + { + locale: 'en', + key: '', + loader: async () => (await import('./locales/en.json')).default + }, + { + locale: 'fr', + key: '', + loader: async () => (await import('./locales/fr.json')).default + } + ] +}; + +export const { t, loading, locales, locale, loadTranslations } = new i18n(config); +loading.subscribe(($loading) => $loading && console.log('Loading translations...')); diff --git a/src/routes/__error.svelte b/src/routes/__error.svelte index 6e9d9f184..1ac61d6bf 100644 --- a/src/routes/__error.svelte +++ b/src/routes/__error.svelte @@ -12,15 +12,18 @@
{status}
-
Ooops you are lost! But don't be afraid!
+
{$t('error.you_are_lost')}
- You can find your way back here + {$t('error.you_can_find_your_way_back')} + {$t('error.here')}
 				
 			
diff --git a/src/routes/applications/[id]/check.json.ts b/src/routes/applications/[id]/check.json.ts
index dc1b8ea98..c15fd8bab 100644
--- a/src/routes/applications/[id]/check.json.ts
+++ b/src/routes/applications/[id]/check.json.ts
@@ -4,6 +4,7 @@ import * as db from '$lib/database';
 import { ErrorHandler } from '$lib/database';
 import type { RequestHandler } from '@sveltejs/kit';
 import { promises as dns } from 'dns';
+import { t } from '$lib/translations';
 
 export const post: RequestHandler = async (event) => {
 	const { status, body } = await getUserDetails(event);
@@ -18,7 +19,9 @@ export const post: RequestHandler = async (event) => {
 		const found = await db.isDomainConfigured({ id, fqdn });
 		if (found) {
 			throw {
-				message: `Domain ${getDomain(fqdn).replace('www.', '')} is already used.`
+				message: t.get('application.domain_already_in_use', {
+					domain: getDomain(fqdn).replace('www.', '')
+				})
 			};
 		}
 		if (!dev && !forceSave) {
@@ -36,7 +39,7 @@ export const post: RequestHandler = async (event) => {
 			if (localIp?.length > 0) {
 				if (ip?.length === 0 || !ip.includes(localIp[0])) {
 					throw {
-						message: `DNS not set or propogated for ${domain}.

Please check your DNS settings.` + message: t.get('application.dns_not_set_error', { domain: domain }) }; } } diff --git a/src/routes/applications/[id]/configuration/_BuildPack.svelte b/src/routes/applications/[id]/configuration/_BuildPack.svelte index e8088ece8..80dac5766 100644 --- a/src/routes/applications/[id]/configuration/_BuildPack.svelte +++ b/src/routes/applications/[id]/configuration/_BuildPack.svelte @@ -5,6 +5,7 @@ import { post } from '$lib/api'; import { findBuildPack } from '$lib/components/templates'; import { errorNotification } from '$lib/form'; + import { t } from '$lib/translations'; const { id } = $page.params; const from = $page.url.searchParams.get('from'); @@ -43,7 +44,9 @@ buildPack.name && buildPack.color}" >{buildPack.fancyName} {#if !scanning && foundConfig?.name === buildPack.name} - Choose this one... + {$t('application.configuration.buildpack.choose_this_one')} {/if} diff --git a/src/routes/applications/[id]/configuration/_GithubRepositories.svelte b/src/routes/applications/[id]/configuration/_GithubRepositories.svelte index 08ee1298f..80a4d246e 100644 --- a/src/routes/applications/[id]/configuration/_GithubRepositories.svelte +++ b/src/routes/applications/[id]/configuration/_GithubRepositories.svelte @@ -7,6 +7,7 @@ import { errorNotification } from '$lib/form'; import { onMount } from 'svelte'; import { gitTokens } from '$lib/store'; + import { t } from '$lib/translations'; const { id } = $page.params; const from = $page.url.searchParams.get('from'); @@ -95,9 +96,7 @@ `/applications/${id}/configuration/repository.json?repository=${selected.repository}&branch=${selected.branch}` ); if (data.used) { - const sure = confirm( - `This branch is already used by another application. Webhooks won't work in this case for both applications. Are you sure you want to use it?` - ); + const sure = confirm($t('application.configuration.branch_already_in_use')); if (sure) { selected.autodeploy = false; showSave = true; @@ -171,8 +170,10 @@ {#if repositories.length === 0 && loading.repositories === false}
-
No repositories configured for your Git Application.
- +
{$t('application.configuration.no_repositories_configured')}
+
{:else}
@@ -181,8 +182,8 @@
Save{$t('forms.save')}
diff --git a/src/routes/applications/[id]/configuration/_GitlabRepositories.svelte b/src/routes/applications/[id]/configuration/_GitlabRepositories.svelte index d83f56d17..a836b3507 100644 --- a/src/routes/applications/[id]/configuration/_GitlabRepositories.svelte +++ b/src/routes/applications/[id]/configuration/_GitlabRepositories.svelte @@ -9,6 +9,7 @@ import { goto } from '$app/navigation'; import { del, get, post, put } from '$lib/api'; import { gitTokens } from '$lib/store'; + import { t } from '$lib/translations'; const { id } = $page.params; const from = $page.url.searchParams.get('from'); @@ -139,9 +140,7 @@ `/applications/${id}/configuration/repository.json?repository=${selected.project.path_with_namespace}&branch=${selected.branch.name}` ); if (data.used) { - const sure = confirm( - `This branch is already used by another application. Webhooks won't work in this case for both applications. Are you sure you want to use it?` - ); + const sure = confirm($t('application.configuration.branch_already_in_use')); if (sure) { autodeploy = false; showSave = true; @@ -270,11 +269,11 @@
{#if loading.base} {:else} - + {:else if !loading.projects && projects.length > 0} {:else} {/if} {#if loading.branches} {:else if !loading.branches && branches.length > 0} {:else} {/if}
@@ -334,7 +340,7 @@ disabled={!showSave || loading.save} class:bg-orange-600={showSave && !loading.save} class:hover:bg-orange-500={showSave && !loading.save} - >{loading.save ? 'Saving...' : 'Save'}{loading.save ? $t('forms.saving') : $t('forms.save')}
diff --git a/src/routes/applications/[id]/configuration/buildpack.svelte b/src/routes/applications/[id]/configuration/buildpack.svelte index a2f15e5e8..437c65cfa 100644 --- a/src/routes/applications/[id]/configuration/buildpack.svelte +++ b/src/routes/applications/[id]/configuration/buildpack.svelte @@ -36,6 +36,7 @@ import { errorNotification } from '$lib/form'; import { gitTokens } from '$lib/store'; import { browser } from '$app/env'; + import { t } from '$lib/translations'; const { id } = $page.params; @@ -204,18 +205,21 @@
-
Configure Build Pack
+
+ {$t('application.configuration.configure_build_pack')} +
{#if scanning}
-
Scanning repository to suggest a build pack for you...
+
+ {$t('application.configuration.scanning_repository_suggest_build_pack')} +
{:else} {#if packageManager === 'yarn' || packageManager === 'pnpm'}
- Found lock file for {packageManager}. - Using it for predefined commands commands. + {@html $t('application.configuration.found_lock_file', { packageManager })}
{/if}
diff --git a/src/routes/applications/[id]/configuration/destination.svelte b/src/routes/applications/[id]/configuration/destination.svelte index 1b0c6df5c..2e68d658c 100644 --- a/src/routes/applications/[id]/configuration/destination.svelte +++ b/src/routes/applications/[id]/configuration/destination.svelte @@ -33,6 +33,7 @@ import { errorNotification } from '$lib/form'; import { goto } from '$app/navigation'; import { post } from '$lib/api'; + import { t } from '$lib/translations'; const { id } = $page.params; const from = $page.url.searchParams.get('from'); @@ -60,12 +61,14 @@
-
Configure Destination
+
+ {$t('application.configuration.configure_destination')} +
{#if !destinations || ownDestinations.length === 0}
-
No configurable Destination found
+
{$t('application.configuration.no_configurable_destination')}
-
Select a Repository / Project
+
+ {$t('application.configuration.select_a_repository_project')} +
{#if application.gitSource.type === 'github'} diff --git a/src/routes/applications/[id]/configuration/source.svelte b/src/routes/applications/[id]/configuration/source.svelte index e5c05ebdd..7ddc382cc 100644 --- a/src/routes/applications/[id]/configuration/source.svelte +++ b/src/routes/applications/[id]/configuration/source.svelte @@ -33,6 +33,7 @@ import { errorNotification } from '$lib/form'; import { goto } from '$app/navigation'; import { post } from '$lib/api'; + import { t } from '$lib/translations'; const { id } = $page.params; const from = $page.url.searchParams.get('from'); @@ -72,12 +73,14 @@
-
Select a Git Source
+
+ {$t('application.configuration.select_a_git_source')} +
{#if !filteredSources || ownSources.length === 0}
-
No configurable Git Source found
+
{$t('application.configuration.no_configurable_git')}
{#if source.gitlabApp && !source.gitlabAppId}
- Configuration missing + {$t('application.configuration.configuration_missing')}
{/if} diff --git a/src/routes/applications/[id]/index.svelte b/src/routes/applications/[id]/index.svelte index 4a14c719d..359cdc5a3 100644 --- a/src/routes/applications/[id]/index.svelte +++ b/src/routes/applications/[id]/index.svelte @@ -49,6 +49,7 @@ import cuid from 'cuid'; import { browser } from '$app/env'; import { disabledButton } from '$lib/store'; + import { t } from '$lib/translations'; const { id } = $page.params; let domainEl: HTMLInputElement; @@ -101,7 +102,7 @@ branch: application.branch, projectId: application.projectId }); - return toast.push('Settings saved.'); + return toast.push($t('application.settings_saved')); } catch ({ error }) { if (name === 'debug') { debug = !debug; @@ -126,7 +127,7 @@ $disabledButton = false; return toast.push('Configurations saved.'); } catch ({ error }) { - if (error?.startsWith('DNS not set')) { + if (error?.startsWith($t('application.dns_not_set_partial_error'))) { forceSave = true; } return errorNotification(error); @@ -216,7 +217,7 @@
-
General
+
{$t('general')}
{#if $session.isAdmin} {loading + ? $t('forms.saving') + : forceSave + ? $t('forms.confirm_continue') + : $t('forms.save')} {/if}
- +
- +
-
Application
+
{$t('application.application')}
- + {#if browser && window.location.hostname === 'demo.coolify.io'} {/if} - +
!isRunning && changeSettings('dualCerts')} />
@@ -372,13 +385,13 @@ {/if} {#if !staticDeployments.includes(application.buildPack)}
- +
{/if} @@ -386,34 +399,38 @@ {#if !notNodeDeployments.includes(application.buildPack)}
{$t('application.install_command')}
- +
- +
{/if} @@ -462,29 +479,25 @@
{$t('forms.base_directory')} - +
{#if !notNodeDeployments.includes(application.buildPack)}
{$t('forms.publish_directory')} - +
{/if}
-
Features
+
{$t('application.features')}
@@ -507,8 +520,8 @@ isCenter={false} bind:setting={autodeploy} on:click={() => changeSettings('autodeploy')} - title="Enable Automatic Deployment" - description="Enable automatic deployment through webhooks." + title={$t('application.enable_automatic_deployment')} + description={$t('application.enable_auto_deploy_webhooks')} />
@@ -516,8 +529,8 @@ isCenter={false} bind:setting={previews} on:click={() => changeSettings('previews')} - title="Enable MR/PR Previews" - description="Enable preview deployments from pull or merge requests." + title={$t('application.enable_mr_pr_previews')} + description={$t('application.enable_preview_deploy_mr_pr_requests')} />
@@ -525,8 +538,8 @@ isCenter={false} bind:setting={debug} on:click={() => changeSettings('debug')} - title="Debug Logs" - description="Enable debug logs during build phase.
Sensitive information could be visible and saved in logs." + title={$t('application.debug_logs')} + description={$t('application.enable_debug_log_during_build')} />
diff --git a/src/routes/applications/[id]/logs/build/_BuildLog.svelte b/src/routes/applications/[id]/logs/build/_BuildLog.svelte index 4379d2d27..9a8dea4cc 100644 --- a/src/routes/applications/[id]/logs/build/_BuildLog.svelte +++ b/src/routes/applications/[id]/logs/build/_BuildLog.svelte @@ -10,6 +10,7 @@ import LoadingLogs from '../_Loading.svelte'; import { get } from '$lib/api'; import { errorNotification } from '$lib/form'; + import { t } from '$lib/translations'; let logs = []; let loading = true; @@ -84,7 +85,7 @@ {/if} {#if currentStatus === 'queued'} -
Queued and waiting for execution.
+
{$t('application.build.queued_waiting_exec')}
{:else}
@@ -197,7 +202,8 @@ {#if !noMoreBuilds} {#if buildCount > 5}
- {$t('application.build.load_more')}
{/if} @@ -212,5 +218,5 @@
{#if buildCount === 0} -
No logs found
+
{$t('application.build.no_logs')}
{/if} diff --git a/src/routes/applications/[id]/logs/index.svelte b/src/routes/applications/[id]/logs/index.svelte index 08d69a1ff..e770f7e1d 100644 --- a/src/routes/applications/[id]/logs/index.svelte +++ b/src/routes/applications/[id]/logs/index.svelte @@ -26,6 +26,7 @@ import LoadingLogs from './_Loading.svelte'; import { get } from '$lib/api'; import { errorNotification } from '$lib/form'; + import { t } from '$lib/translations'; let loadLogsInterval = null; let allLogs = { @@ -176,7 +177,7 @@
{#if logs.length === 0} -
Waiting for the logs...
+
{$t('application.build.waiting_logs')}
{:else}
diff --git a/src/routes/applications/[id]/previews/index.svelte b/src/routes/applications/[id]/previews/index.svelte index 9cc16c80c..152aa00d2 100644 --- a/src/routes/applications/[id]/previews/index.svelte +++ b/src/routes/applications/[id]/previews/index.svelte @@ -30,6 +30,7 @@ import Explainer from '$lib/components/Explainer.svelte'; import { errorNotification } from '$lib/form'; import { toast } from '@zerodevx/svelte-toast'; + import { t } from '$lib/translations'; const { id } = $page.params; async function refreshSecrets() { @@ -134,10 +135,12 @@ - - - - + + + + @@ -171,13 +174,15 @@
{$t('application.preview.redeploy')}
{/each} {:else}
-
No previews available
+
+ {$t('application.preview.no_previews_available')} +
{/if} diff --git a/src/routes/applications/[id]/secrets/_BatchSecrets.svelte b/src/routes/applications/[id]/secrets/_BatchSecrets.svelte index 8d0a65aee..d7c23938f 100644 --- a/src/routes/applications/[id]/secrets/_BatchSecrets.svelte +++ b/src/routes/applications/[id]/secrets/_BatchSecrets.svelte @@ -6,6 +6,7 @@ import { saveSecret } from './utils'; import pLimit from 'p-limit'; import { createEventDispatcher } from 'svelte'; + import { t } from '$lib/translations'; const dispatch = createEventDispatcher(); let batchSecrets = ''; @@ -38,11 +39,11 @@ } -

Paste .env file

+

{$t('application.secret__batch_dot_env')}

NameValueNeed during buildtime?Action{$t('forms.name')}{$t('forms.value')}{$t('application.preview.need_during_buildtime')}{$t('forms.action')}