From 7d7d75d7e5c6fa3b968196377ecb18d25bcc3a26 Mon Sep 17 00:00:00 2001 From: qwerty287 <80460567+qwerty287@users.noreply.github.com> Date: Mon, 16 May 2022 21:18:48 +0200 Subject: [PATCH] Support localized web UI (#912) * Add support for localization * Add docs & format code * Add lib to docs --- docs/docs/92-development/07-translations.md | 17 ++ web/.eslintignore | 1 + web/package.json | 2 + web/src/assets/locales/en.json | 208 ++++++++++++++++++ .../components/build-feed/BuildFeedItem.vue | 4 +- .../build-feed/BuildFeedSidebar.vue | 2 +- web/src/components/layout/header/Navbar.vue | 6 +- web/src/components/repo/build/BuildList.vue | 2 +- web/src/components/repo/build/BuildLog.vue | 6 +- .../components/repo/settings/ActionsTab.vue | 18 +- web/src/components/repo/settings/BadgeTab.vue | 8 +- .../components/repo/settings/GeneralTab.vue | 117 +++++----- .../repo/settings/RegistriesTab.vue | 45 ++-- .../components/repo/settings/SecretsTab.vue | 79 ++++--- web/src/compositions/useBuild.ts | 6 +- web/src/compositions/useDate.ts | 6 +- web/src/compositions/useI18n.ts | 12 + web/src/main.ts | 2 + web/src/utils/duration.ts | 28 +-- web/src/utils/timeAgo.ts | 2 +- web/src/views/Login.vue | 18 +- web/src/views/NotFound.vue | 8 +- web/src/views/RepoAdd.vue | 16 +- web/src/views/Repos.vue | 4 +- web/src/views/ReposOwner.vue | 4 +- web/src/views/User.vue | 19 +- web/src/views/repo/RepoBranch.vue | 2 +- web/src/views/repo/RepoSettings.vue | 12 +- web/src/views/repo/RepoWrapper.vue | 8 +- web/src/views/repo/build/Build.vue | 20 +- .../views/repo/build/BuildChangedFiles.vue | 2 +- web/src/views/repo/build/BuildWrapper.vue | 20 +- web/vite.config.ts | 4 + web/yarn.lock | 144 +++++++++++- 34 files changed, 660 insertions(+), 192 deletions(-) create mode 100644 docs/docs/92-development/07-translations.md create mode 100644 web/src/assets/locales/en.json create mode 100644 web/src/compositions/useI18n.ts diff --git a/docs/docs/92-development/07-translations.md b/docs/docs/92-development/07-translations.md new file mode 100644 index 000000000..9b39c40a0 --- /dev/null +++ b/docs/docs/92-development/07-translations.md @@ -0,0 +1,17 @@ +# Translations + +Woodpecker uses [Vue I18n](https://vue-i18n.intlify.dev/) as translation library, thus you can easily translate the web UI into your language. Therefore, copy the file `web/src/assets/locales/en.json` to the same path with your language's code and `.json` as name. +Then, translate content of this file, but only the values: + +```json +{ + "dont_translate": "Only translate this text" +} +``` + +To add support for time formatting, import the language into two files: + +1. `web/src/compositions/useDate.ts`: Just add a line like `import 'dayjs/locale/en';` to the first block of `import` statements and replace `en` with your language's code. +2. `web/src/utils/timeAgo.ts`: Add a line like `import en from 'javascript-time-ago/locale/en.json';` to the other `import`-statements and replace both `en`s with your language's code. Then, add the line `TimeAgo.addDefaultLocale(en);` to the other lines of them, and replace `en` with your language's code. + +Then, the web UI should be available in your language. You should open a pull request to our repository to get your changes into the next release. diff --git a/web/.eslintignore b/web/.eslintignore index 67bb21d0a..94e770c8b 100644 --- a/web/.eslintignore +++ b/web/.eslintignore @@ -4,3 +4,4 @@ coverage/ package.json tsconfig.eslint.json tsconfig.json +src/assets/locales/ diff --git a/web/package.json b/web/package.json index 69e8cc868..e1acc2556 100644 --- a/web/package.json +++ b/web/package.json @@ -17,6 +17,7 @@ "test": "echo 'No tests configured' && exit 0" }, "dependencies": { + "@intlify/vite-plugin-vue-i18n": "^3.4.0", "@kyvg/vue3-notification": "2.3.4", "@meforma/vue-toaster": "1.2.2", "ansi-to-html": "0.7.2", @@ -29,6 +30,7 @@ "node-emoji": "1.11.0", "pinia": "2.0.0", "vue": "v3.2.20", + "vue-i18n": "9", "vue-router": "4.0.10" }, "devDependencies": { diff --git a/web/src/assets/locales/en.json b/web/src/assets/locales/en.json new file mode 100644 index 000000000..260dea266 --- /dev/null +++ b/web/src/assets/locales/en.json @@ -0,0 +1,208 @@ +{ + "login": "Login", + "welcome": "Welcome to Woodpecker", + "repos": "Repos", + "repositories": "Repositories", + "logout": "Logout", + "search": "Search...", + "username": "Username", + "password": "Password", + "url": "URL", + + "not_found": { + "not_found": "Whoa 404, either we broke something or you had a typing mishap :-/", + "back_home": "Back to home" + }, + + "time": { + "tmpl": "MMM D, YYYY, HH:mm z", + "weeks_short": "w", + "days_short": "d", + "hours_short": "h", + "min_short": "min", + "sec_short": "sec", + "not_started": "not started yet" + }, + + "repo": { + "activity": "Activity", + "branches": "Branches", + "add": "Add repository", + "user_none": "This organization / user does not have any projects yet.", + "not_allowed": "Not allowed to access this repository", + + "enable": { + "reload": "Reload repositories", + "enable": "Enable", + "enabled": "Already enabled", + "success": "Repository enabled", + "list_reloaded": "Repository list reloaded" + }, + + "settings": { + "settings": "Settings", + + "general": { + "general": "General", + "project": "Project settings", + "save": "Save settings", + "success": "Repository settings updated", + + "pipeline_path": { + "path": "Pipeline path", + "default": "By default: .woodpecker/*.yml -> .woodpecker.yml -> .drone.yml", + "desc": "Path to your pipeline config (for examplemy/path/). Folders should end with a/." + }, + + "allow_pr": { + "allow": "Allow Pull Requests", + "desc": "Pipelines can run on pull requests." + }, + + "protected": { + "protected": "Protected", + "desc": "Every pipeline needs to be approved before being executed." + }, + "trusted": { + "trusted": "Trusted", + "desc": "Underlying pipeline containers get access to escalated capabilities like mounting volumes." + }, + "visibility": { + "visibility": "Project visibility", + + "public": { + "public": "Public", + "desc": "Every user can see your project without being logged in." + }, + "private": { + "private": "Private", + "desc": "Only authenticated users of the Woodpecker instance can see this project." + }, + "internal": { + "internal": "Internal", + "desc": "Only you and other owners of the repository can see this project." + } + }, + + "timeout": { + "timeout": "Timeout", + "minutes": "minutes" + }, + + "cancel_prev": { + "cancel": "Cancel previous pipelines", + "desc": "Enable to cancel running pipelines of the same event and context before starting the newly triggered one." + } + }, + + "secrets": { + "secrets": "Secrets", + "desc": "Secrets can be passed to individual pipeline steps at runtime as environmental variables.", + "none": "There are no secrets yet.", + "add": "Add secret", + "save": "Save secret", + "show": "Show secrets", + "name": "Name", + "value": "Value", + "deleted": "Secret deleted", + "created": "Secret created", + "saved": "Secret saved", + + "images": { + "images": "Available for following images", + "desc": "Comma separated list of images where this secret is available, leave empty to allow all images" + }, + "events": { + "events": "Available at following events", + "pr_warning": "Please be careful with this option as a bad actor can submit a malicious pull request that exposes your secrets." + } + }, + + "registries": { + "registries": "Registries", + "creds": "Registry credentials", + "desc": "Registries credentials can be added to use private images for your pipeline.", + "show": "Show registries", + "add": "Add registry", + "none": "There are no registry credentials yet.", + "save": "Save registry", + "created": "Registry credentials created", + "saved": "Registry credentials saved", + "deleted": "Registry credentials deleted", + + "address": { + "address": "Address", + "placeholder": "Registry Address (e.g. docker.io)" + } + }, + + "badge": { + "badge": "Badge", + "url_branch": "URL for specific branch", + "markdown": "Markdown" + }, + + "actions": { + "actions": "Actions", + + "repair": { + "repair": "Repair repository", + "success": "Repository repaired" + }, + "disable": { + "disable": "Disable repository", + "success": "Repository disabled" + }, + "delete": { + "delete": "Delete repository", + "confirm": "All data will be lost after this action!!!\n\nDo you really want to proceed?", + "success": "Repository deleted" + } + } + }, + + "build": { + "created": "Created", + "tasks": "Tasks", + "config": "Config", + "files": "Changed files ({0})", + "no_files": "No files have been changed.", + "execution_error": "Execution error", + "no_pipelines": "No pipelines have been started yet.", + "step_not_started": "This step hasn't started yet.", + "pipelines_for": "Pipelines for branch \"{0}\"", + + "actions": { + "cancel": "Cancel", + "restart": "Restart", + "canceled": "This step has been canceled.", + "cancel_success": "Pipeline canceled", + "restart_success": "Pipeline restarted" + }, + "protected": { + "awaits": "This pipeline is awaiting approval by some maintainer!", + "approve": "Approve", + "decline": "Decline", + "declined": "This pipeline has been declined!" + }, + "event": { + "push": "Push", + "tag": "Tag", + "pr": "Pull Request", + "deploy": "Deploy" + } + } + }, + + "user": { + "oauth_error": "Error while authenticating against OAuth provider", + "internal_error": "Some internal error occurred", + "access_denied": "You are not allowed to login", + "token": "Your Personal Token", + "shell_setup": "Shell setup", + "api_usage": "Example API Usage", + "cli_usage": "Example CLI Usage", + "dl_cli": "Download CLI", + "shell_setup_before": "do shell setup steps before" + } +} diff --git a/web/src/components/build-feed/BuildFeedItem.vue b/web/src/components/build-feed/BuildFeedItem.vue index c3a8ddbcf..c6795af26 100644 --- a/web/src/components/build-feed/BuildFeedItem.vue +++ b/web/src/components/build-feed/BuildFeedItem.vue @@ -9,7 +9,9 @@ {{ since }} - +
diff --git a/web/src/components/build-feed/BuildFeedSidebar.vue b/web/src/components/build-feed/BuildFeedSidebar.vue index 38957f14c..e28a31896 100644 --- a/web/src/components/build-feed/BuildFeedSidebar.vue +++ b/web/src/components/build-feed/BuildFeedSidebar.vue @@ -21,7 +21,7 @@ - No pipelines have been started yet. + {{ $t('repo.build.no_pipelines') }}
diff --git a/web/src/components/layout/header/Navbar.vue b/web/src/components/layout/header/Navbar.vue index 7e822a457..7ca0fe1d0 100644 --- a/web/src/components/layout/header/Navbar.vue +++ b/web/src/components/layout/header/Navbar.vue @@ -10,8 +10,8 @@ :to="{ name: 'repos' }" class="mx-4 hover:bg-lime-700 dark:hover:bg-gray-600 px-4 py-1 rounded-md" > - Repos - + {{ $t('repos') }} +
@@ -29,7 +29,7 @@ -
diff --git a/web/src/components/repo/build/BuildList.vue b/web/src/components/repo/build/BuildList.vue index 82b817744..64458c5cc 100644 --- a/web/src/components/repo/build/BuildList.vue +++ b/web/src/components/repo/build/BuildList.vue @@ -9,7 +9,7 @@ - No pipelines have been started yet. + {{ $t('repo.build.no_pipelines') }} diff --git a/web/src/components/repo/build/BuildLog.vue b/web/src/components/repo/build/BuildLog.vue index eb18dcae0..18f7394c0 100644 --- a/web/src/components/repo/build/BuildLog.vue +++ b/web/src/components/repo/build/BuildLog.vue @@ -22,10 +22,10 @@
{{ proc.error }} - This step has been canceled. + >{{ $t('repo.build.actions.canceled') }} - This step hasn't started yet. + {{ $t('repo.build.step_not_started') }}
diff --git a/web/src/components/repo/settings/ActionsTab.vue b/web/src/components/repo/settings/ActionsTab.vue index 36f955ed1..95af9e58f 100644 --- a/web/src/components/repo/settings/ActionsTab.vue +++ b/web/src/components/repo/settings/ActionsTab.vue @@ -1,7 +1,7 @@