diff --git a/.github/language-maintainers.yml b/.github/language-maintainers.yml new file mode 100644 index 00000000000..b69eb1147cb --- /dev/null +++ b/.github/language-maintainers.yml @@ -0,0 +1,88 @@ +# Language maintainers for Keycloak translations +# This file maps language codes to their responsible maintainers. It is used to notify them once a translation changes. +# Format: language_code: [list of GitHub usernames] + +languages: + ca: + name: Catalan + maintainers: + - jmallach + - Ecron + cs: + name: Czech + maintainers: + - pionl + - pschiffe + de: + name: German + maintainers: + - robson90 + - ahus1 + el: + name: Greek + maintainers: + - infl00p + - knaiskes + es: + name: Spanish + maintainers: + - herver1971 + - anthieni + fr: + name: French + maintainers: + - Dodouce + - GitSpoon + it: + name: Italian + maintainers: + - GioviQ + - EdoardoTona + ja: + name: Japanese + maintainers: + - y-tabata + - wadahiro + - tnorimat + - k-tamura + nl: + name: Dutch + maintainers: + - janher + - edewit + pt_BR: + name: Portuguese (Brazil) + maintainers: + - rafaelrddc + - felipebz + - julianorodrigox + ro: + name: Romanian + maintainers: + - edwint88 + - liviuroman + ru: + name: Russian + maintainers: + - petrov9 + - pasternake + sl: + name: Slovenian + maintainers: + - mathmul + - SaraPristovnik + tr: + name: Turkish + maintainers: + - spctr + - ariferol + zh_Hans: + name: Simplified Chinese + maintainers: + - jasonqsong + - charliedcc + zh_Hant: + name: Traditional Chinese + maintainers: + - allen0099 + - benwater12 diff --git a/.github/workflows/translation-notify.yml b/.github/workflows/translation-notify.yml new file mode 100644 index 00000000000..e67e1929e2b --- /dev/null +++ b/.github/workflows/translation-notify.yml @@ -0,0 +1,92 @@ +name: Translation Maintainer Notification + +on: + pull_request: + types: [closed] + paths: + - 'themes/**/messages_*.properties' + - 'js/**/messages_*.properties' + +defaults: + run: + shell: bash + +permissions: + pull-requests: write + +jobs: + notify-maintainers: + name: Notify language maintainers of changes + runs-on: ubuntu-latest + if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main' + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Yq + run: sudo snap install yq + + - name: Get changed language files and notify maintainers + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.pull_request.number }} + # language=bash + run: | + # Get the list of changed files in this PR + CHANGED_FILES=$(gh pr view "$PR_NUMBER" --json files --jq '.files[].path' | grep 'messages_.*\.properties' || true) + + if [ -z "$CHANGED_FILES" ]; then + echo "No translation files changed" + exit 0 + fi + + echo "Changed files:" + echo "$CHANGED_FILES" + + # Extract unique language codes from filenames + declare -A languages + + while IFS= read -r file; do + # Extract language code from messages_XX.properties or messages_XX_YY.properties + if [[ $file =~ messages_([a-z]{2}(_[A-Z][a-z]+)?)\.properties ]]; then + lang_code="${BASH_REMATCH[1]}" + # Skip English language + if [ "$lang_code" != "en" ]; then + languages["$lang_code"]=1 + fi + fi + done <<< "$CHANGED_FILES" + + if [ ${#languages[@]} -eq 0 ]; then + echo "No non-English language codes found in changed files" + exit 0 + fi + + echo "Languages changed: ${!languages[@]}" + + # Read maintainers file and build comment + COMMENT="## Translation Changes Notification\n\n" + COMMENT+="*Language maintainers:* Please review the translation changes in your language in [Weblate](https://hosted.weblate.org/projects/keycloak/).\n\n" + COMMENT+="The following languages have been updated in this PR:\n\n" + + # Sort languages for deterministic output + for lang_code in $(printf '%s\n' "${!languages[@]}" | sort); do + # Look up maintainers in the YAML file + yaml_key="$lang_code" + + LANG_NAME=$(yq eval ".languages.\"${yaml_key}\".name // \"Unknown\"" .github/language-maintainers.yml) + MAINTAINERS=$(yq eval ".languages.\"${yaml_key}\".maintainers // [] | join(\", @\")" .github/language-maintainers.yml) + + if [ "$MAINTAINERS" != "" ] && [ "$MAINTAINERS" != "null" ]; then + COMMENT+="### ${LANG_NAME} (\`${lang_code}\`)\n" + COMMENT+="@${MAINTAINERS}\n\n" + else + COMMENT+="### \`${lang_code}\`\n" + COMMENT+="No maintainers configured for this language.\n\n" + fi + COMMENT+="[Changes waiting for approval.](https://hosted.weblate.org/translate/keycloak/-/${lang_code}/?q=state:translated)\n\n" + done + + # Post comment to PR + echo -e "$COMMENT" | gh pr comment "$PR_NUMBER" --body-file - \ No newline at end of file diff --git a/docs/translation.md b/docs/translation.md index b9c5eeee4b1..fe29849d773 100644 --- a/docs/translation.md +++ b/docs/translation.md @@ -78,31 +78,16 @@ Keycloak uses [Weblate](https://hosted.weblate.org/projects/keycloak/), a web-ba It allows for notifications when the original string changes, and keeps track of missing translations. It also allows contributors without knowledge of Git to contribute to the translations. -The following translations are available in Weblate. If you have any questions or need assistance, feel free to reach out to the language maintainers listed below: +See here for a [list of all translations available in Weblate and their status](https://www.keycloak.org/translations). -* German: [Robin Meese](https://github.com/robson90) && [Alexander Schwartz](https://github.com/ahus1) -* Dutch: [janher](https://github.com/janher) && [Erik Jan de wit](https://github.com/edewit) -* Japanese: [y-tabata](https://github.com/y-tabata) && [wadahiro](https://github.com/wadahiro) && [tnorimat](https://github.com/tnorimat) && [k-tamura](https://github.com/k-tamura) -* Catalan: [jmallach](https://github.com/jmallach) && [Ecron](https://github.com/Ecron) -* Spanish: [herver1971](https://github.com/herver1971) && [anthieni](https://github.com/anthieni) -* Slovenian: [mathmul](https://github.com/mathmul) && [SaraPristovnik](https://github.com/SaraPristovnik) -* Italian: [GioviQ](https://github.com/GioviQ) && [EdoardoTona](https://github.com/EdoardoTona) -* Romanian: [edwint88](https://github.com/edwint88) && [liviuroman](https://github.com/liviuroman) -* Portuguese (Brazil): [rafaelrddc](https://github.com/rafaelrddc) && [felipebz](https://github.com/felipebz) && [julianorodrigox](https://github.com/julianorodrigox) -* French: [Dodouce](https://github.com/Dodouce) && [GitSpoon](https://github.com/GitSpoon) -* Russian: [petrov9](https://github.com/petrov9) && [pasternake](https://github.com/pasternake) -* Traditional Chinese: [allen0099](https://github.com/allen0099) && [benwater12](https://github.com/benwater12) -* Greek: [infl00p](https://github.com/infl00p) && [knaiskes](https://github.com/knaiskes) -* Turkish: [spctr](https://github.com/spctr) && [ariferol](https://github.com/ariferol) -* Czech: [pionl](https://github.com/pionl) && [pschiffe](https://github.com/pschiffe) -* Simplified Chinese: [jasonqsong](https://github.com/jasonqsong) && [charliedcc](https://github.com/charliedcc) +If you have any questions or need assistance, feel free to reach out to the language maintainers listed in [the language maintainers file](../.github/language-maintainers.yml). To add a new language, see the section "Steps to Add a new language to Weblate" below. **Tasks of the translator:** 1. Sign Up: Visit Weblate to create an account. -2. Configuration: After signing up, configure your Weblate account settings according to your preferences. +2. Configuration: After signing up, configure your Weblate account settings according to your preferences. Please note that the committer email is set to the login email address by default. You can adjust this in your Weblate profile under Account settings. 3. Navigate to [Keycloak on Weblate](https://hosted.weblate.org/projects/keycloak/), confirm the contribution agreement, and start contributing translations. 4. For all untranslated and not-yet-approved keys you can directly add or update the translation. @@ -143,28 +128,11 @@ the [localization platform discussion](https://github.com/keycloak/keycloak/disc - Each language requires **two volunteers** - Volunteers should comment on the discussion thread to confirm their participation. - The Keycloak-Team will then - - enable the specific language - - invite the two volunteers - - comment on discussion thread, that the language has been enabled + - in Weblate: + - Enable the specific language. + - Invite the two volunteers, + - on GitHub: + - Comment on discussion thread, that the language has been enabled + - Update [the language maintainers file](../.github/language-maintainers.yml) with the GitHub handles of the language mainatainers to enable notifications on updated files on GitHub. + - If it is a language that wasn't translated before, update the `theme.properties` files to include the new language. - Weblate synchronizes daily, so it may take up to 24 hours after enabling the language before you can start your first translations - -## Weblate Translation status - -| Language | Account UI | Admin UI | Theme base/account | Theme base/admin | Theme base/email | Theme base/login | Overall | -|--------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------| -| [German](https://hosted.weblate.org/projects/keycloak/-/de/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/de/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/de/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/de/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/de/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/de/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/de/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/de/svg-badge.svg) | -| [Dutch](https://hosted.weblate.org/projects/keycloak/-/nl/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/nl/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/nl/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/nl/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/nl/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/nl/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/nl/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/nl/svg-badge.svg) | -| [Japanese](https://hosted.weblate.org/projects/keycloak/-/ja/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/ja/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/ja/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/ja/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/ja/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/ja/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/ja/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/ja/svg-badge.svg) | -| [Catalan](https://hosted.weblate.org/projects/keycloak/-/ca/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/ca/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/ca/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/ca/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/ca/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/ca/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/ca/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/ca/svg-badge.svg) | -| [Spanish](https://hosted.weblate.org/projects/keycloak/-/es/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/es/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/es/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/es/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/es/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/es/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/es/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/es/svg-badge.svg) | -| [Slovenian](https://hosted.weblate.org/projects/keycloak/-/sl/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/sl/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/sl/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/sl/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/sl/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/sl/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/sl/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/sl/svg-badge.svg) | -| [Italian](https://hosted.weblate.org/projects/keycloak/-/it/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/it/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/it/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/it/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/it/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/it/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/it/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/it/svg-badge.svg) | -| [Romanian](https://hosted.weblate.org/projects/keycloak/-/ro/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/ro/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/ro/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/ro/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/ro/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/ro/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/ro/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/ro/svg-badge.svg) | -| [Portuguese (Brazil)](https://hosted.weblate.org/projects/keycloak/-/pt_BR/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/pt_BR/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/pt_BR/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/pt_BR/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/pt_BR/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/pt_BR/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/pt_BR/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/pt_BR/svg-badge.svg) | -| [French](https://hosted.weblate.org/projects/keycloak/-/fr/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/fr/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/fr/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/fr/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/fr/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/fr/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/fr/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/fr/svg-badge.svg) | -| [Russian](https://hosted.weblate.org/projects/keycloak/-/ru/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/ru/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/ru/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/ru/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/ru/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/ru/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/ru/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/ru/svg-badge.svg) | -| [Traditional Chinese](https://hosted.weblate.org/projects/keycloak/-/zh_hant/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/zh_hant/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/zh_hant/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/zh_hant/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/zh_hant/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/zh_hant/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/zh_hant/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/zh_hant/svg-badge.svg) | -| [Greek](https://hosted.weblate.org/projects/keycloak/-/el/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/el/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/el/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/el/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/el/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/el/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/el/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/el/svg-badge.svg) | -| [Turkish](https://hosted.weblate.org/projects/keycloak/-/tr/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/tr/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/tr/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/tr/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/tr/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/tr/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/tr/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/tr/svg-badge.svg) | -| [Czech](https://hosted.weblate.org/projects/keycloak/-/cz/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/cz/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/cz/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/cz/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/cz/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/cz/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/cz/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/cz/svg-badge.svg) | -| [Simplified Chinese](https://hosted.weblate.org/projects/keycloak/-/zh_Hans/) | ![Translation status](https://hosted.weblate.org/widget/keycloak/account-ui/zh_Hans/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/admin-ui/zh_Hans/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseaccount/zh_Hans/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseadmin/zh_Hans/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baseemail/zh_Hans/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/theme-baselogin/zh_Hans/svg-badge.svg) | ![Translation status](https://hosted.weblate.org/widget/keycloak/-/zh_Hans/svg-badge.svg) | \ No newline at end of file