From bbb83c2ff02e9d990b75fe248aec0d272869cce9 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 27 Jul 2026 15:12:53 +0200 Subject: [PATCH] Localazy: improve search, parallelize checks (#7261) * Remove no longer temporary string These are now in localazy and the sync strings CI flow was failing because we have 2 conflicting values in `temporary.xml` and `localazy.xml` * Use the right placeholder value in `screen_local_network_opt_in_subtitle` * Localazy: improve search, parallelize checks The search for XML files has been narrowed down and it's several times faster now, and processing the files is now done in parallel * Improve script to also find and delete previous files Also add the option of having a `-v|--verbose` flag to print which files we're modifying * Use `LocalBuildMeta` to get the app name inside `LocalNetworkPermissionDialogView` * Allow finding all the forbidden terms in the strings and translations and then return an error exit code instead of a success one --------- Co-authored-by: ElementBot --- .../android/appnav/loggedin/LoggedInView.kt | 1 - .../impl/changeserver/ChangeServerView.kt | 1 - .../ChooseAccountProviderView.kt | 2 - .../loginwithclassic/LoginWithClassicView.kt | 2 - .../ConfirmAccountProviderView.kt | 2 - .../impl/screens/onboarding/OnBoardingView.kt | 1 - .../LocalNetworkPermissionDialogView.kt | 4 +- .../appnav.loggedin_LoggedInView_Day_4_en.png | 4 +- .../appnav.loggedin_LoggedInView_Day_5_en.png | 4 +- ...ppnav.loggedin_LoggedInView_Night_4_en.png | 4 +- ...ppnav.loggedin_LoggedInView_Night_5_en.png | 4 +- tools/localazy/checkForbiddenTerms.py | 5 + tools/localazy/downloadStrings.sh | 106 ++++++++++++++++-- 13 files changed, 110 insertions(+), 30 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt index 3bbd640c78..e57d378da4 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt @@ -85,7 +85,6 @@ fun LoggedInView( } LocalNetworkPermissionDialogView( - appName = state.appName, dialog = state.localNetworkPermissionDialog, onSubmit = { state.eventSink(LoggedInEvents.RequestLocationNetworkPermission) }, onDismiss = { state.eventSink(LoggedInEvents.DismissLocalNetworkPermissionPrompt) }, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt index 39c167cd13..30ae644a70 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt @@ -123,7 +123,6 @@ fun ChangeServerView( AsyncData.Uninitialized -> Unit } LocalNetworkPermissionDialogView( - appName = LocalBuildMeta.current.applicationName, dialog = state.localNetworkPermissionDialog, onSubmit = { eventSink.invoke(ChangeServerEvents.RequestLocalNetworkPermission) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt index 3eb31689ff..17e7d10efb 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt @@ -41,7 +41,6 @@ import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.LocalBuildMeta import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.TopAppBar @@ -139,7 +138,6 @@ fun ChooseAccountProviderView( } } LocalNetworkPermissionDialogView( - appName = LocalBuildMeta.current.applicationName, dialog = state.loginModeState.localNetworkPermissionDialog, onSubmit = { state.loginModeState.eventSink(LoginModeEvent.RequestLocalNetworkPermission) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt index 02e16fca95..75181a068e 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt @@ -47,7 +47,6 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.BitmapAvatar import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.LocalBuildMeta import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text @@ -208,7 +207,6 @@ fun LoginWithClassicView( onCreateAccountContinue = onCreateAccountContinue, ) LocalNetworkPermissionDialogView( - appName = LocalBuildMeta.current.applicationName, dialog = state.loginModeState.localNetworkPermissionDialog, onSubmit = { state.loginModeState.eventSink(LoginModeEvent.RequestLocalNetworkPermission) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index a84a046e2a..95fbff25ab 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -29,7 +29,6 @@ import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.LocalBuildMeta import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.matrix.api.auth.OAuthDetails @@ -113,7 +112,6 @@ fun ConfirmAccountProviderView( } LocalNetworkPermissionDialogView( - appName = LocalBuildMeta.current.applicationName, dialog = state.loginModeState.localNetworkPermissionDialog, onSubmit = { state.loginModeState.eventSink(LoginModeEvent.RequestLocalNetworkPermission) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt index 6b3ab4043a..bf96b2dbe0 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt @@ -87,7 +87,6 @@ fun OnBoardingView( onCreateAccountContinue = onCreateAccountContinue, ) LocalNetworkPermissionDialogView( - appName = state.productionApplicationName, dialog = state.loginModeState.localNetworkPermissionDialog, onSubmit = { state.loginModeState.eventSink(LoginModeEvent.RequestLocalNetworkPermission) diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/localnetwork/LocalNetworkPermissionDialogView.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/localnetwork/LocalNetworkPermissionDialogView.kt index 2fddff16b1..66616c197e 100644 --- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/localnetwork/LocalNetworkPermissionDialogView.kt +++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/localnetwork/LocalNetworkPermissionDialogView.kt @@ -11,11 +11,11 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog +import io.element.android.libraries.designsystem.theme.LocalBuildMeta import io.element.android.libraries.ui.strings.CommonStrings @Composable fun LocalNetworkPermissionDialogView( - appName: String, dialog: LocalNetworkPermissionDialog, onSubmit: () -> Unit, onDismiss: () -> Unit, @@ -28,7 +28,7 @@ fun LocalNetworkPermissionDialogView( } ConfirmationDialog( title = stringResource(CommonStrings.screen_local_network_opt_in_title), - content = stringResource(CommonStrings.screen_local_network_opt_in_subtitle, appName), + content = stringResource(CommonStrings.screen_local_network_opt_in_subtitle, LocalBuildMeta.current.applicationName), submitText = stringResource(submitTextRes), cancelText = stringResource(CommonStrings.action_not_now), onSubmitClick = onSubmit, diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_4_en.png index e0bfa036e8..5adfe35003 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cdab8410501666e33f7d810376640b732e4b36255797dbf4ef09c79d896c2b3f -size 33825 +oid sha256:965cd4692e88a413e5e0a83e16a190386e8f199716fdd7288f4e05b8c623a8b2 +size 33756 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_5_en.png index eacf92c3c1..bf9aed6830 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8100f2bdc31632af9b5894e45fa1d8b47ca992cdf7564d442ac649edb1ba8e5b -size 33022 +oid sha256:771c01d4563687aff208b0f279c9fb98192b0cd2b2ba56d43627f6c785e92b90 +size 32956 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_4_en.png index ab8ff08c43..75fdd8172d 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ebce406b2ee9354e25caee308786c7424c159cc8276f92d8d6474d07d7036cb -size 31882 +oid sha256:8f2d01f11bcca2ba3465b6308f7368797c6fd9712bf90f4e9493b30dbece8277 +size 31907 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_5_en.png index f3cdb4ff7d..28fd0cfc21 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1c49dbd3cb5b4cd78d1b65a7b159d368e4a0c2734d24c7429c1f5362cf7bae9 -size 31148 +oid sha256:484173d52fcb0acff42f62d28f88cd544e205bde49370ccc8230c30b193e1959 +size 31173 diff --git a/tools/localazy/checkForbiddenTerms.py b/tools/localazy/checkForbiddenTerms.py index 123246ffd0..48bf7030b8 100755 --- a/tools/localazy/checkForbiddenTerms.py +++ b/tools/localazy/checkForbiddenTerms.py @@ -34,6 +34,11 @@ forbiddenTerms = { # Contains "Element Classic" "screen_missing_key_backup_open_element_classic", "screen_missing_key_backup_step_1", + # These are notification sound names + "screen_notification_settings_sound_element_default", + "screen_notification_settings_sound_element_fade", + # This contains the word 'element' in some languages + "screen_media_upload_preview_item_count", ] } diff --git a/tools/localazy/downloadStrings.sh b/tools/localazy/downloadStrings.sh index 5a60656f35..5560aa002d 100755 --- a/tools/localazy/downloadStrings.sh +++ b/tools/localazy/downloadStrings.sh @@ -8,23 +8,51 @@ set -e -if [[ $1 == "--all" ]]; then +allFiles=0 + +while [[ $# -gt 0 ]]; do + case $1 in + -a|--all) + allFiles=1 + shift + ;; + -v|--verbose) + verbose=1 + shift + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +if [[ $allFiles == 1 ]]; then echo "Note: I will update all the files." - allFiles=1 else echo "Note: I will update only the English files." - allFiles=0 fi echo "Generating the configuration file for localazy..." python3 ./tools/localazy/generateLocalazyConfig.py $allFiles +# Search for existing string files first so we can remove them +localazyXmlFiles=$(find . \ + -type d \( -name build -o -name .git \) -prune -o \ + -type f -path '*/src/*/res/values/localazy.xml' -print | sort) + +if [[ $allFiles == 1 ]]; then + translationXmlFiles=$(find . \ + -type d \( -name build -o -name .git \) -prune -o \ + -type f -path '*/src/*/res/values-*/translations.xml' -print | sort) +fi + echo "Deleting all existing localazy.xml files..." -find . -name 'localazy.xml' -delete +echo "$localazyXmlFiles" | xargs -L1 -P0 rm if [[ $allFiles == 1 ]]; then echo "Deleting all existing translations.xml files..." - find . -name 'translations.xml' -delete + echo "$translationXmlFiles" | xargs -L1 -P0 rm fi echo "Importing the strings..." @@ -33,16 +61,72 @@ localazy download --config ./tools/localazy/localazy.json echo "Removing the generated config" rm ./tools/localazy/localazy.json -echo "Formatting the resources files..." -find . -name 'localazy.xml' -exec ./tools/localazy/formatXmlResourcesFile.py {} \; +# Now list the newly generated files so we can format them and check for forbidden terms +localazyXmlFiles=$(find . \ + -type d \( -name build -o -name .git \) -prune -o \ + -type f -path '*/src/*/res/values/localazy.xml' -print | sort) + if [[ $allFiles == 1 ]]; then - find . -name 'translations.xml' -exec ./tools/localazy/formatXmlResourcesFile.py {} \; + translationXmlFiles=$(find . \ + -type d \( -name build -o -name .git \) -prune -o \ + -type f -path '*/src/*/res/values-*/translations.xml' -print | sort) fi -echo "Checking forbidden terms..." -find . -name 'localazy.xml' -exec ./tools/localazy/checkForbiddenTerms.py {} \; +formatXmlFiles() { + local xml="$1" + if [[ $verbose == 1 ]]; then + echo "Formatting $xml" + fi + ./tools/localazy/formatXmlResourcesFile.py "$xml" +} + +checkForbiddenTerms() { + set +e + local xml="$1" + if [[ $verbose == 1 ]]; then + echo "Checking forbidden terms in $xml" + fi + ./tools/localazy/checkForbiddenTerms.py "$xml" + + # If the script finds a forbidden term, it will exit with a non-zero status code and interrupt the execution, but we don't want that. + # Instead we want to capture that exit code and write it to a global variable so we can check later if any forbidden terms were found. + status=$? + if [[ $status != 0 ]]; then + forbiddenTermsResult=$((forbiddenTermsResult || status)) + fi +} + +export -f formatXmlFiles +export -f checkForbiddenTerms + +echo "Formatting the resources files..." + +echo "Formatting original localazy.xml files..." +# Format the original localazy.xml files in parallel +echo "$localazyXmlFiles" | xargs -L1 -P0 bash -c 'formatXmlFiles "$@"' _ + if [[ $allFiles == 1 ]]; then - find . -name 'translations.xml' -exec ./tools/localazy/checkForbiddenTerms.py {} \; + if [[ $verbose == 1 ]]; then + echo "Formatting translation files..." + fi + + echo "$translationXmlFiles" | xargs -L1 -P0 bash -c 'formatXmlFiles "$@"' _ +fi + +# This stores whether any forbidden terms were found in any of the files. If any forbidden terms are found, this will be set to a non-zero value. +export forbiddenTermsResult=0 + +echo "Checking forbidden terms..." +echo "$localazyXmlFiles" | xargs -L1 -P0 bash -c 'checkForbiddenTerms "$@"' _ + +if [[ $allFiles == 1 ]]; then + echo "Checking forbidden terms in translation files..." + echo "$translationXmlFiles" | xargs -L1 -P0 bash -c 'checkForbiddenTerms "$@"' _ +fi + +if [[ $forbiddenTermsResult != 0 ]]; then + echo "Error: Forbidden terms found in the resources files. Please check the output above." + exit 1 fi echo "Success!"