diff --git a/.github/workflows/sync-from-external-sources.yml b/.github/workflows/sync-from-external-sources.yml new file mode 100644 index 0000000000..c9c8528904 --- /dev/null +++ b/.github/workflows/sync-from-external-sources.yml @@ -0,0 +1,25 @@ +name: Sync Data From External Sources +on: + schedule: + # Every nights at 6 + - cron: "0 6 * * *" + +jobs: + sync-strings: + runs-on: ubuntu-latest + # Skip in forks + if: github.repository == 'vector-im/element-x-android' + # No concurrency required, runs every time on a schedule. + steps: + - uses: actions/checkout@v3 + - name: Run local script + run: ./tools/strings/importStringsFromElement.sh + - name: Create Pull Request + uses: peter-evans/create-pull-request@v4 + with: + commit-message: Import strings from Element Android + title: Sync strings + body: | + - Update strings from Element Android + branch: sync-strings + base: develop diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 220835ffa4..33827f673b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -20,6 +20,7 @@ @Suppress("DSL_SCOPE_VIOLATION") plugins { id("io.element.android-compose-application") + alias(libs.plugins.stem) alias(libs.plugins.kotlin.android) alias(libs.plugins.ksp) alias(libs.plugins.anvil) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 120af4c3da..fee1385c85 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,6 @@ + - - - + + + ignored diff --git a/features/login/build.gradle.kts b/features/login/build.gradle.kts index 29987189e9..623477f727 100644 --- a/features/login/build.gradle.kts +++ b/features/login/build.gradle.kts @@ -41,6 +41,7 @@ dependencies { implementation(project(":libraries:designsystem")) implementation(project(":libraries:elementresources")) implementation(libs.appyx.core) + implementation(project(":libraries:ui-strings")) ksp(libs.showkase.processor) testImplementation(libs.test.junit) androidTestImplementation(libs.test.junitext) diff --git a/features/login/src/main/java/io/element/android/x/features/login/error/ErrorFormatter.kt b/features/login/src/main/java/io/element/android/x/features/login/error/ErrorFormatter.kt index 3afc697572..3700575b96 100644 --- a/features/login/src/main/java/io/element/android/x/features/login/error/ErrorFormatter.kt +++ b/features/login/src/main/java/io/element/android/x/features/login/error/ErrorFormatter.kt @@ -20,7 +20,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import io.element.android.x.core.uri.isValidUrl import io.element.android.x.features.login.root.LoginFormState -import io.element.android.x.element.resources.R as ElementR +import io.element.android.x.ui.strings.R as StringR @Composable fun loginError( @@ -30,7 +30,7 @@ fun loginError( return when { data.login.isEmpty() -> "Please enter a login" data.password.isEmpty() -> "Please enter a password" - throwable != null -> stringResource(id = ElementR.string.auth_invalid_login_param) + throwable != null -> stringResource(id = StringR.string.auth_invalid_login_param) else -> "No error provided" } } @@ -42,7 +42,7 @@ fun changeServerError( ): String { return when { data.isEmpty() -> "Please enter a server URL" - !data.isValidUrl() -> stringResource(id = ElementR.string.login_error_invalid_home_server) + !data.isValidUrl() -> stringResource(id = StringR.string.login_error_invalid_home_server) throwable != null -> "That server doesn’t seem right. Please check the address." else -> "No error provided" } diff --git a/features/login/src/main/java/io/element/android/x/features/login/root/LoginRootScreen.kt b/features/login/src/main/java/io/element/android/x/features/login/root/LoginRootScreen.kt index aa7814ccf2..6e0f38886a 100644 --- a/features/login/src/main/java/io/element/android/x/features/login/root/LoginRootScreen.kt +++ b/features/login/src/main/java/io/element/android/x/features/login/root/LoginRootScreen.kt @@ -62,7 +62,7 @@ import io.element.android.x.core.compose.textFieldState import io.element.android.x.designsystem.ElementXTheme import io.element.android.x.features.login.error.loginError import io.element.android.x.matrix.core.SessionId -import io.element.android.x.element.resources.R as ElementR +import io.element.android.x.ui.strings.R as StringR @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -97,7 +97,7 @@ fun LoginRootScreen( val isError = state.loggedInState is LoggedInState.ErrorLoggingIn // Title Text( - text = stringResource(id = ElementR.string.ftue_auth_welcome_back_title), + text = stringResource(id = StringR.string.ftue_auth_welcome_back_title), modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 48.dp), @@ -140,7 +140,7 @@ fun LoginRootScreen( .fillMaxWidth() .padding(top = 60.dp), label = { - Text(text = stringResource(id = ElementR.string.login_signin_username_hint)) + Text(text = stringResource(id = StringR.string.login_signin_username_hint)) }, onValueChange = { loginFieldState = it diff --git a/features/logout/build.gradle.kts b/features/logout/build.gradle.kts index c7a28ef2d7..93f9c4f6f2 100644 --- a/features/logout/build.gradle.kts +++ b/features/logout/build.gradle.kts @@ -39,6 +39,7 @@ dependencies { implementation(project(":libraries:matrix")) implementation(project(":libraries:designsystem")) implementation(project(":libraries:elementresources")) + implementation(project(":libraries:ui-strings")) ksp(libs.showkase.processor) testImplementation(libs.test.junit) androidTestImplementation(libs.test.junitext) diff --git a/features/logout/src/main/java/io/element/android/x/features/logout/LogoutPreferenceScreen.kt b/features/logout/src/main/java/io/element/android/x/features/logout/LogoutPreferenceScreen.kt index 75f29319dd..18c4e73b1e 100644 --- a/features/logout/src/main/java/io/element/android/x/features/logout/LogoutPreferenceScreen.kt +++ b/features/logout/src/main/java/io/element/android/x/features/logout/LogoutPreferenceScreen.kt @@ -30,7 +30,7 @@ import io.element.android.x.designsystem.components.ProgressDialog import io.element.android.x.designsystem.components.dialogs.ConfirmationDialog import io.element.android.x.designsystem.components.preferences.PreferenceCategory import io.element.android.x.designsystem.components.preferences.PreferenceText -import io.element.android.x.element.resources.R as ElementR +import io.element.android.x.ui.strings.R as StringR @Composable fun LogoutPreferenceView( @@ -55,9 +55,9 @@ fun LogoutPreferenceView( // Log out confirmation dialog if (openDialog.value) { ConfirmationDialog( - title = stringResource(id = ElementR.string.action_sign_out), - content = stringResource(id = ElementR.string.action_sign_out_confirmation_simple), - submitText = stringResource(id = ElementR.string.action_sign_out), + title = stringResource(id = StringR.string.action_sign_out), + content = stringResource(id = StringR.string.action_sign_out_confirmation_simple), + submitText = stringResource(id = StringR.string.action_sign_out), onCancelClicked = { openDialog.value = false }, @@ -80,9 +80,9 @@ fun LogoutPreferenceView( fun LogoutPreferenceContent( onClick: () -> Unit = {}, ) { - PreferenceCategory(title = stringResource(id = ElementR.string.settings_general_title)) { + PreferenceCategory(title = stringResource(id = StringR.string.settings_general_title)) { PreferenceText( - title = stringResource(id = ElementR.string.action_sign_out), + title = stringResource(id = StringR.string.action_sign_out), icon = Icons.Default.Logout, onClick = onClick ) diff --git a/features/onboarding/build.gradle.kts b/features/onboarding/build.gradle.kts index 4c25a25ea3..cdb646417a 100644 --- a/features/onboarding/build.gradle.kts +++ b/features/onboarding/build.gradle.kts @@ -28,6 +28,7 @@ android { dependencies { implementation(project(":libraries:core")) implementation(project(":libraries:elementresources")) + implementation(project(":libraries:ui-strings")) implementation(project(":libraries:designsystem")) implementation(project(":libraries:architecture")) implementation(libs.accompanist.pager) diff --git a/features/onboarding/src/main/java/io/element/android/x/features/onboarding/OnBoardingScreen.kt b/features/onboarding/src/main/java/io/element/android/x/features/onboarding/OnBoardingScreen.kt index f6e0ed0db4..fb88a76db0 100644 --- a/features/onboarding/src/main/java/io/element/android/x/features/onboarding/OnBoardingScreen.kt +++ b/features/onboarding/src/main/java/io/element/android/x/features/onboarding/OnBoardingScreen.kt @@ -52,7 +52,7 @@ import com.google.accompanist.pager.rememberPagerState import io.element.android.x.designsystem.components.VectorButton import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import io.element.android.x.element.resources.R as ElementR +import io.element.android.x.ui.strings.R as StringR @OptIn(ExperimentalPagerApi::class) @Composable @@ -120,7 +120,7 @@ fun OnBoardingScreen( ) */ VectorButton( - text = stringResource(id = ElementR.string.login_splash_submit), + text = stringResource(id = StringR.string.login_splash_submit), onClick = { onSignIn() }, diff --git a/features/onboarding/src/main/java/io/element/android/x/features/onboarding/SplashCarouselStateFactory.kt b/features/onboarding/src/main/java/io/element/android/x/features/onboarding/SplashCarouselStateFactory.kt index d12f83cfbe..c25fb89815 100644 --- a/features/onboarding/src/main/java/io/element/android/x/features/onboarding/SplashCarouselStateFactory.kt +++ b/features/onboarding/src/main/java/io/element/android/x/features/onboarding/SplashCarouselStateFactory.kt @@ -17,7 +17,7 @@ package io.element.android.x.features.onboarding import androidx.annotation.DrawableRes -import io.element.android.x.element.resources.R as ElementR +import io.element.android.x.ui.strings.R as StringR class SplashCarouselStateFactory { fun create(): SplashCarouselState { @@ -32,8 +32,8 @@ class SplashCarouselStateFactory { return SplashCarouselState( listOf( SplashCarouselState.Item( - ElementR.string.ftue_auth_carousel_secure_title, - ElementR.string.ftue_auth_carousel_secure_body, + StringR.string.ftue_auth_carousel_secure_title, + StringR.string.ftue_auth_carousel_secure_body, hero( R.drawable.ic_splash_conversations, R.drawable.ic_splash_conversations_dark @@ -41,20 +41,20 @@ class SplashCarouselStateFactory { background(R.drawable.bg_carousel_page_1) ), SplashCarouselState.Item( - ElementR.string.ftue_auth_carousel_control_title, - ElementR.string.ftue_auth_carousel_control_body, + StringR.string.ftue_auth_carousel_control_title, + StringR.string.ftue_auth_carousel_control_body, hero(R.drawable.ic_splash_control, R.drawable.ic_splash_control_dark), background(R.drawable.bg_carousel_page_2) ), SplashCarouselState.Item( - ElementR.string.ftue_auth_carousel_encrypted_title, - ElementR.string.ftue_auth_carousel_encrypted_body, + StringR.string.ftue_auth_carousel_encrypted_title, + StringR.string.ftue_auth_carousel_encrypted_body, hero(R.drawable.ic_splash_secure, R.drawable.ic_splash_secure_dark), background(R.drawable.bg_carousel_page_3) ), SplashCarouselState.Item( collaborationTitle(), - ElementR.string.ftue_auth_carousel_workplace_body, + StringR.string.ftue_auth_carousel_workplace_body, hero( R.drawable.ic_splash_collaboration, R.drawable.ic_splash_collaboration_dark @@ -68,7 +68,7 @@ class SplashCarouselStateFactory { private fun collaborationTitle(): Int { return when { true -> R.string.cut_the_slack_from_teams - else -> ElementR.string.ftue_auth_carousel_workplace_title + else -> StringR.string.ftue_auth_carousel_workplace_title } } } diff --git a/features/onboarding/src/main/res/values/strings.xml b/features/onboarding/src/main/res/values/strings.xml index 43ee27cb35..41a17b9693 100644 --- a/features/onboarding/src/main/res/values/strings.xml +++ b/features/onboarding/src/main/res/values/strings.xml @@ -16,5 +16,5 @@ --> - Cut the slack from teams. + Cut the slack from teams. diff --git a/features/preferences/build.gradle.kts b/features/preferences/build.gradle.kts index 32ad183d13..e8f5147642 100644 --- a/features/preferences/build.gradle.kts +++ b/features/preferences/build.gradle.kts @@ -42,6 +42,7 @@ dependencies { implementation(project(":features:logout")) implementation(project(":libraries:designsystem")) implementation(project(":libraries:elementresources")) + implementation(project(":libraries:ui-strings")) implementation(libs.datetime) implementation(libs.accompanist.placeholder) testImplementation(libs.test.junit) diff --git a/features/preferences/src/main/java/io/element/android/x/features/preferences/root/PreferencesRootView.kt b/features/preferences/src/main/java/io/element/android/x/features/preferences/root/PreferencesRootView.kt index 95049803a3..5d3e1dab7b 100644 --- a/features/preferences/src/main/java/io/element/android/x/features/preferences/root/PreferencesRootView.kt +++ b/features/preferences/src/main/java/io/element/android/x/features/preferences/root/PreferencesRootView.kt @@ -22,12 +22,12 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import io.element.android.x.architecture.Async import io.element.android.x.designsystem.components.preferences.PreferenceView -import io.element.android.x.element.resources.R import io.element.android.x.features.logout.LogoutPreferenceState import io.element.android.x.features.logout.LogoutPreferenceView import io.element.android.x.features.preferences.user.UserPreferences import io.element.android.x.features.rageshake.preferences.RageshakePreferencesState import io.element.android.x.features.rageshake.preferences.RageshakePreferencesView +import io.element.android.x.ui.strings.R as StringR @Composable fun PreferencesRootView( @@ -41,7 +41,7 @@ fun PreferencesRootView( PreferenceView( modifier = modifier, onBackPressed = onBackPressed, - title = stringResource(id = R.string.settings) + title = stringResource(id = StringR.string.settings) ) { UserPreferences(state.myUser) RageshakePreferencesView( diff --git a/features/rageshake/build.gradle.kts b/features/rageshake/build.gradle.kts index 92f9b75aaa..b3f8b14ded 100644 --- a/features/rageshake/build.gradle.kts +++ b/features/rageshake/build.gradle.kts @@ -39,6 +39,7 @@ dependencies { implementation(project(":anvilannotations")) implementation(project(":libraries:designsystem")) implementation(project(":libraries:elementresources")) + implementation(project(":libraries:ui-strings")) implementation(libs.squareup.seismic) implementation(libs.androidx.datastore.preferences) implementation(libs.coil) diff --git a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportView.kt b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportView.kt index 394cf69a5b..b22cc7c48c 100644 --- a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportView.kt +++ b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportView.kt @@ -56,7 +56,7 @@ import io.element.android.x.core.compose.textFieldState import io.element.android.x.designsystem.ElementXTheme import io.element.android.x.designsystem.components.LabelledCheckbox import io.element.android.x.designsystem.components.dialogs.ErrorDialog -import io.element.android.x.element.resources.R as ElementR +import io.element.android.x.ui.strings.R as StringR @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -96,7 +96,7 @@ fun BugReportView( val isFormEnabled = state.sending !is Async.Loading // Title Text( - text = stringResource(id = ElementR.string.send_bug_report), + text = stringResource(id = StringR.string.send_bug_report), modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 16.dp), @@ -106,7 +106,7 @@ fun BugReportView( ) // Form Text( - text = stringResource(id = ElementR.string.send_bug_report_description), + text = stringResource(id = StringR.string.send_bug_report_description), modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 16.dp), @@ -123,10 +123,10 @@ fun BugReportView( .padding(top = 16.dp), enabled = isFormEnabled, label = { - Text(text = stringResource(id = ElementR.string.send_bug_report_placeholder)) + Text(text = stringResource(id = StringR.string.send_bug_report_placeholder)) }, supportingText = { - Text(text = stringResource(id = ElementR.string.send_bug_report_description_in_english)) + Text(text = stringResource(id = StringR.string.send_bug_report_description_in_english)) }, onValueChange = { descriptionFieldState = it @@ -143,28 +143,28 @@ fun BugReportView( checked = state.formState.sendLogs, onCheckedChange = { eventSink(BugReportEvents.SetSendLog(it)) }, enabled = isFormEnabled, - text = stringResource(id = ElementR.string.send_bug_report_include_logs) + text = stringResource(id = StringR.string.send_bug_report_include_logs) ) if (state.hasCrashLogs) { LabelledCheckbox( checked = state.formState.sendCrashLogs, onCheckedChange = { eventSink(BugReportEvents.SetSendCrashLog(it)) }, enabled = isFormEnabled, - text = stringResource(id = ElementR.string.send_bug_report_include_crash_logs) + text = stringResource(id = StringR.string.send_bug_report_include_crash_logs) ) } LabelledCheckbox( checked = state.formState.canContact, onCheckedChange = { eventSink(BugReportEvents.SetCanContact(it)) }, enabled = isFormEnabled, - text = stringResource(id = ElementR.string.you_may_contact_me) + text = stringResource(id = StringR.string.you_may_contact_me) ) if (state.screenshotUri != null) { LabelledCheckbox( checked = state.formState.sendScreenshot, onCheckedChange = { eventSink(BugReportEvents.SetSendScreenshot(it)) }, enabled = isFormEnabled, - text = stringResource(id = ElementR.string.send_bug_report_include_screenshot) + text = stringResource(id = StringR.string.send_bug_report_include_screenshot) ) if (state.formState.sendScreenshot) { Box( @@ -191,7 +191,7 @@ fun BugReportView( .fillMaxWidth() .padding(vertical = 32.dp) ) { - Text(text = stringResource(id = ElementR.string.action_send)) + Text(text = stringResource(id = StringR.string.action_send)) } } when (state.sending) { diff --git a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionScreen.kt b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionScreen.kt index 7c1e855455..13d174468b 100644 --- a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionScreen.kt +++ b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionScreen.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.tooling.preview.Preview import io.element.android.x.core.compose.LogCompositions import io.element.android.x.designsystem.ElementXTheme import io.element.android.x.designsystem.components.dialogs.ConfirmationDialog -import io.element.android.x.element.resources.R as ElementR +import io.element.android.x.ui.strings.R as StringR @Composable fun CrashDetectionView( @@ -53,10 +53,10 @@ fun CrashDetectionContent( onDismiss: () -> Unit = { }, ) { ConfirmationDialog( - title = stringResource(id = ElementR.string.send_bug_report), - content = stringResource(id = ElementR.string.send_bug_report_app_crashed), - submitText = stringResource(id = ElementR.string.yes), - cancelText = stringResource(id = ElementR.string.no), + title = stringResource(id = StringR.string.send_bug_report), + content = stringResource(id = StringR.string.send_bug_report_app_crashed), + submitText = stringResource(id = StringR.string.yes), + cancelText = stringResource(id = StringR.string.no), onCancelClicked = onNoClicked, onSubmitClicked = onYesClicked, onDismiss = onDismiss, diff --git a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/detection/RageshakeDetectionView.kt b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/detection/RageshakeDetectionView.kt index 61e9f32150..6f647758ce 100644 --- a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/detection/RageshakeDetectionView.kt +++ b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/detection/RageshakeDetectionView.kt @@ -30,7 +30,7 @@ import io.element.android.x.core.screenshot.ImageResult import io.element.android.x.core.screenshot.screenshot import io.element.android.x.designsystem.ElementXTheme import io.element.android.x.designsystem.components.dialogs.ConfirmationDialog -import io.element.android.x.element.resources.R as ElementR +import io.element.android.x.ui.strings.R as StringR @Composable fun RageshakeDetectionView( @@ -83,11 +83,11 @@ fun RageshakeDialogContent( onYesClicked: () -> Unit = { }, ) { ConfirmationDialog( - title = stringResource(id = ElementR.string.send_bug_report), - content = stringResource(id = ElementR.string.send_bug_report_alert_message), - thirdButtonText = stringResource(id = ElementR.string.action_disable), - submitText = stringResource(id = ElementR.string.yes), - cancelText = stringResource(id = ElementR.string.no), + title = stringResource(id = StringR.string.send_bug_report), + content = stringResource(id = StringR.string.send_bug_report_alert_message), + thirdButtonText = stringResource(id = StringR.string.action_disable), + submitText = stringResource(id = StringR.string.yes), + cancelText = stringResource(id = StringR.string.no), onCancelClicked = onNoClicked, onThirdButtonClicked = onDisableClicked, onSubmitClicked = onYesClicked, diff --git a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/preferences/RageshakePreferencesView.kt b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/preferences/RageshakePreferencesView.kt index e9142a2c2e..f5ce13440e 100644 --- a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/preferences/RageshakePreferencesView.kt +++ b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/preferences/RageshakePreferencesView.kt @@ -27,7 +27,7 @@ import io.element.android.x.designsystem.components.preferences.PreferenceCatego import io.element.android.x.designsystem.components.preferences.PreferenceSlide import io.element.android.x.designsystem.components.preferences.PreferenceSwitch import io.element.android.x.designsystem.components.preferences.PreferenceText -import io.element.android.x.element.resources.R as ElementR +import io.element.android.x.ui.strings.R as StringR @Composable fun RageshakePreferencesView( @@ -44,24 +44,23 @@ fun RageshakePreferencesView( } Column(modifier = modifier) { - PreferenceCategory(title = stringResource(id = ElementR.string.send_bug_report)) { + PreferenceCategory(title = stringResource(id = StringR.string.send_bug_report)) { PreferenceText( - title = stringResource(id = ElementR.string.send_bug_report), + title = stringResource(id = StringR.string.send_bug_report), icon = Icons.Default.BugReport, onClick = onOpenRageshake ) } - - PreferenceCategory(title = stringResource(id = ElementR.string.settings_rageshake)) { + PreferenceCategory(title = stringResource(id = StringR.string.settings_rageshake)) { if (state.isSupported) { PreferenceSwitch( - title = stringResource(id = ElementR.string.send_bug_report_rage_shake), + title = stringResource(id = StringR.string.send_bug_report_rage_shake), isChecked = state.isEnabled, onCheckedChange = ::onEnabledChanged ) PreferenceSlide( - title = stringResource(id = ElementR.string.settings_rageshake_detection_threshold), - // summary = stringResource(id = ElementR.string.settings_rageshake_detection_threshold_summary), + title = stringResource(id = StringR.string.settings_rageshake_detection_threshold), + // summary = stringResource(id = StringR.string.settings_rageshake_detection_threshold_summary), value = state.sensitivity, enabled = state.isEnabled, steps = 3 /* 5 possible values - steps are in ]0, 1[ */, diff --git a/features/roomlist/build.gradle.kts b/features/roomlist/build.gradle.kts index 674ec25d88..646d2175c4 100644 --- a/features/roomlist/build.gradle.kts +++ b/features/roomlist/build.gradle.kts @@ -41,6 +41,7 @@ dependencies { implementation(project(":libraries:designsystem")) implementation(libs.appyx.core) implementation(project(":libraries:elementresources")) + implementation(project(":libraries:ui-strings")) implementation(libs.datetime) implementation(libs.accompanist.placeholder) testImplementation(libs.test.junit) diff --git a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomListTopBar.kt b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomListTopBar.kt index a6db83f09d..d8fe56142b 100644 --- a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomListTopBar.kt +++ b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomListTopBar.kt @@ -48,6 +48,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp @@ -55,6 +56,7 @@ import io.element.android.x.core.compose.LogCompositions import io.element.android.x.core.compose.textFieldState import io.element.android.x.designsystem.components.avatar.Avatar import io.element.android.x.matrix.ui.model.MatrixUser +import io.element.android.x.ui.strings.R as StringR @Composable fun RoomListTopBar( @@ -185,7 +187,7 @@ private fun DefaultRoomListTopBar( title = { Text( fontWeight = FontWeight.Bold, - text = "All Chats" + text = stringResource(id = StringR.string.all_chats) ) }, navigationIcon = { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0a936ed73c..44c3f89979 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -51,6 +51,7 @@ jsoup = "1.15.3" appyx = "1.0.1" seismic = "1.0.3" dependencycheck = "7.4.4" +stem = "2.2.3" # DI dagger = "2.43" @@ -150,3 +151,5 @@ ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } molecule = {id = "app.cash.molecule", version.ref = "molecule"} dependencygraph = { id = "com.savvasdalkitsis.module-dependency-graph", version.ref = "dependencygraph" } dependencycheck = { id = "org.owasp.dependencycheck", version.ref = "dependencycheck" } +stem = { id = "com.likethesalad.stem", version.ref = "stem" } +stemlibrary = { id = "com.likethesalad.stem-library", version.ref = "stem" } diff --git a/libraries/designsystem/build.gradle.kts b/libraries/designsystem/build.gradle.kts index 9c70a68b4b..597d8a4514 100644 --- a/libraries/designsystem/build.gradle.kts +++ b/libraries/designsystem/build.gradle.kts @@ -29,6 +29,7 @@ android { implementation(libs.coil.compose) implementation(libs.accompanist.systemui) implementation(project(":libraries:elementresources")) + implementation(project(":libraries:ui-strings")) ksp(libs.showkase.processor) } } diff --git a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/dialogs/ConfirmationDialog.kt b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/dialogs/ConfirmationDialog.kt index 349ec32f41..9d74a2da87 100644 --- a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/dialogs/ConfirmationDialog.kt +++ b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/dialogs/ConfirmationDialog.kt @@ -29,15 +29,15 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import io.element.android.x.element.resources.R as ElementR +import io.element.android.x.ui.strings.R as StringR @Composable fun ConfirmationDialog( title: String, content: String, modifier: Modifier = Modifier, - submitText: String = stringResource(id = ElementR.string.ok), - cancelText: String = stringResource(id = ElementR.string.action_cancel), + submitText: String = stringResource(id = StringR.string.ok), + cancelText: String = stringResource(id = StringR.string.action_cancel), thirdButtonText: String? = null, onSubmitClicked: () -> Unit = {}, onCancelClicked: () -> Unit = {}, diff --git a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/dialogs/ErrorDialog.kt b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/dialogs/ErrorDialog.kt index b46f3104a7..241d47fe9f 100644 --- a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/dialogs/ErrorDialog.kt +++ b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/dialogs/ErrorDialog.kt @@ -28,14 +28,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import io.element.android.x.element.resources.R as ElementR +import io.element.android.x.ui.strings.R as StringR @Composable fun ErrorDialog( content: String, modifier: Modifier = Modifier, - title: String = stringResource(id = ElementR.string.dialog_title_error), - submitText: String = stringResource(id = ElementR.string.ok), + title: String = stringResource(id = StringR.string.dialog_title_error), + submitText: String = stringResource(id = StringR.string.ok), onDismiss: () -> Unit = {}, ) { AlertDialog( diff --git a/libraries/textcomposer/build.gradle.kts b/libraries/textcomposer/build.gradle.kts index cda96fb319..41cc543ba2 100644 --- a/libraries/textcomposer/build.gradle.kts +++ b/libraries/textcomposer/build.gradle.kts @@ -31,6 +31,7 @@ android { dependencies { implementation(project(":libraries:elementresources")) + implementation(project(":libraries:ui-strings")) implementation(project(":libraries:core")) implementation(project(":libraries:matrix")) implementation(libs.wysiwyg) diff --git a/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/RichTextComposerLayout.kt b/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/RichTextComposerLayout.kt index eb51388ca3..7eb8d5dcda 100644 --- a/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/RichTextComposerLayout.kt +++ b/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/RichTextComposerLayout.kt @@ -45,12 +45,13 @@ import io.element.android.wysiwyg.inputhandlers.models.InlineFormat import io.element.android.x.core.ui.DimensionConverter import io.element.android.x.core.ui.hideKeyboard import io.element.android.x.core.ui.showKeyboard -import io.element.android.x.element.resources.R as ElementR import io.element.android.x.textcomposer.databinding.ComposerRichTextLayoutBinding import io.element.android.x.textcomposer.databinding.ViewRichTextMenuButtonBinding import io.element.android.x.textcomposer.tools.setTextIfDifferent import uniffi.wysiwyg_composer.ActionState import uniffi.wysiwyg_composer.ComposerAction +import io.element.android.x.element.resources.R as ElementR +import io.element.android.x.ui.strings.R as StringR // Imported from Element Android class RichTextComposerLayout @JvmOverloads constructor( @@ -248,28 +249,28 @@ class RichTextComposerLayout @JvmOverloads constructor( private fun setupRichTextMenu() { addRichTextMenuItem( R.drawable.ic_composer_bold, - ElementR.string.rich_text_editor_format_bold, + StringR.string.rich_text_editor_format_bold, ComposerAction.BOLD ) { views.richTextComposerEditText.toggleInlineFormat(InlineFormat.Bold) } addRichTextMenuItem( R.drawable.ic_composer_italic, - ElementR.string.rich_text_editor_format_italic, + StringR.string.rich_text_editor_format_italic, ComposerAction.ITALIC ) { views.richTextComposerEditText.toggleInlineFormat(InlineFormat.Italic) } addRichTextMenuItem( R.drawable.ic_composer_underlined, - ElementR.string.rich_text_editor_format_underline, + StringR.string.rich_text_editor_format_underline, ComposerAction.UNDERLINE ) { views.richTextComposerEditText.toggleInlineFormat(InlineFormat.Underline) } addRichTextMenuItem( R.drawable.ic_composer_strikethrough, - ElementR.string.rich_text_editor_format_strikethrough, + StringR.string.rich_text_editor_format_strikethrough, ComposerAction.STRIKE_THROUGH ) { views.richTextComposerEditText.toggleInlineFormat(InlineFormat.StrikeThrough) @@ -495,16 +496,16 @@ class RichTextComposerLayout @JvmOverloads constructor( */ views.plainTextComposerEditText.setText(text) } - views.sendButton.contentDescription = resources.getString(ElementR.string.action_send) + views.sendButton.contentDescription = resources.getString(StringR.string.action_send) hasRelatedMessage = false } views.sendButton.apply { if (mode is MessageComposerMode.Edit) { - contentDescription = resources.getString(ElementR.string.action_save) + contentDescription = resources.getString(StringR.string.action_save) setImageResource(R.drawable.ic_composer_rich_text_save) } else { - contentDescription = resources.getString(ElementR.string.action_send) + contentDescription = resources.getString(StringR.string.action_send) setImageResource(R.drawable.ic_rich_composer_send) } } diff --git a/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/TextComposer.kt b/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/TextComposer.kt index d7a5788c68..8bb7aea485 100644 --- a/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/TextComposer.kt @@ -34,7 +34,7 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.viewinterop.AndroidView import androidx.core.view.isInvisible import androidx.core.view.isVisible -import io.element.android.x.element.resources.R as ElementR +import io.element.android.x.ui.strings.R as StringR @Composable fun TextComposer( @@ -137,7 +137,7 @@ private fun MessageComposerView.setup(isDarkMode: Boolean, composerMode: Message } editText.setTextColor(editTextColor) editText.setHintTextColor(editTextColor) - editText.setHint(ElementR.string.room_message_placeholder) + editText.setHint(StringR.string.room_message_placeholder) emojiButton?.isVisible = true sendButton.isVisible = true editText.maxLines = MessageComposerView.MAX_LINES_WHEN_COLLAPSED diff --git a/libraries/ui-strings/README.md b/libraries/ui-strings/README.md new file mode 100644 index 0000000000..bb55709240 --- /dev/null +++ b/libraries/ui-strings/README.md @@ -0,0 +1,15 @@ +## Module ui-strings + +This module contains strings from Element Android. + +The content of this module will be keep synced by the script `rootDir/tools/strings/importStringsFromElement.sh`, which is run daily by a GitHub action. + +### Adding new strings + +Add new strings to the file `./src/main/res/values/new_strings.xml`, or your new strings will be erased on the next sync. + +Also, new strings must be added to Element Android to give a chance to get translation. + +### Long term + +In the future, Weblate will be synced directly with this project. diff --git a/libraries/ui-strings/build.gradle.kts b/libraries/ui-strings/build.gradle.kts new file mode 100644 index 0000000000..dc4693ad7f --- /dev/null +++ b/libraries/ui-strings/build.gradle.kts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed +@Suppress("DSL_SCOPE_VIOLATION") +plugins { + id("io.element.android-library") + alias(libs.plugins.stemlibrary) +} + +android { + namespace = "io.element.android.x.ui.strings" +} + +// forcing the stem string template generator to be cacheable, without this the templates +// are regenerated causing the app module to recompile its sources +tasks.withType(com.likethesalad.android.templates.common.tasks.BaseTask::class.java) { + outputs.cacheIf { true } +} diff --git a/libraries/ui-strings/src/main/AndroidManifest.xml b/libraries/ui-strings/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..8072ee00db --- /dev/null +++ b/libraries/ui-strings/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/libraries/ui-strings/src/main/res/values/array.xml b/libraries/ui-strings/src/main/res/values/array.xml new file mode 100644 index 0000000000..26f7a39bf1 --- /dev/null +++ b/libraries/ui-strings/src/main/res/values/array.xml @@ -0,0 +1,61 @@ + + + + + + @string/compression_opt_list_choose + @string/compression_opt_list_original + @string/compression_opt_list_large + @string/compression_opt_list_medium + @string/compression_opt_list_small + + + 0 + 1 + 2 + 3 + 4 + + + @string/media_source_choose + @string/option_send_files + @string/option_send_voice + @string/option_send_sticker + @string/option_take_photo + @string/option_take_video + + + 0 + 1 + 2 + 3 + 4 + 5 + + + + + + + @string/system_theme + @string/light_theme + @string/dark_theme + @string/black_theme + + + + system + light + dark + black + + + + + @string/media_saving_period_3_days + @string/media_saving_period_1_week + @string/media_saving_period_1_month + @string/media_saving_period_forever + + + \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values/donottranslate.xml b/libraries/ui-strings/src/main/res/values/donottranslate.xml new file mode 100755 index 0000000000..bfe751ef5a --- /dev/null +++ b/libraries/ui-strings/src/main/res/values/donottranslate.xml @@ -0,0 +1,15 @@ + + + + + + + + Not implemented yet in ${app_name} + + + Cut the slack from teams. + + + © MapTiler © OpenStreetMap contributors + diff --git a/libraries/elementresources/src/main/res/values/strings.xml b/libraries/ui-strings/src/main/res/values/strings.xml similarity index 94% rename from libraries/elementresources/src/main/res/values/strings.xml rename to libraries/ui-strings/src/main/res/values/strings.xml index f257482aef..73cb60bb68 100644 --- a/libraries/elementresources/src/main/res/values/strings.xml +++ b/libraries/ui-strings/src/main/res/values/strings.xml @@ -1,20 +1,4 @@ - - @@ -150,6 +134,9 @@ ** Unable to decrypt: %s ** The sender\'s device has not sent us the keys for this message. + %1$s ended a voice broadcast. + You ended a voice broadcast. + @@ -209,7 +196,7 @@ Initial sync:\nImporting account data Initial sync request - ElementX needs to perform a clear cache to be up to date, for the following reason:\n%s\n\nNote that this action will restart the app and it may take some time. + ${app_name} needs to perform a clear cache to be up to date, for the following reason:\n%s\n\nNote that this action will restart the app and it may take some time. - Some users have been unignored Message sent @@ -432,6 +419,7 @@ Got it Select all Deselect all + Yes, Stop Copied to clipboard @@ -537,8 +525,8 @@ Are you sure that you want to start a voice call? Are you sure that you want to start a video call? - - ElementX Call Failed + + ${app_name} Call Failed Failed to establish real time connection.\nPlease ask the administrator of your homeserver to configure a TURN server in order for calls to work reliably. Select Sound Device @@ -600,8 +588,8 @@ Re-request encryption keys from your other sessions. - - Please launch ElementX on another device that can decrypt the message so it can send the keys to this session. + + Please launch ${app_name} on another device that can decrypt the message so it can send the keys to this session. @@ -621,8 +609,8 @@ Calls Prevent accidental call Ask for confirmation before starting a call - - Use default ElementX ringtone for incoming calls + + Use default ${app_name} ringtone for incoming calls Incoming call ringtone Select ringtone for calls: @@ -659,12 +647,12 @@ Information - - ElementX needs permission to access your microphone to perform audio calls. - - ElementX needs permission to access your camera and your microphone to perform video calls.\n\nPlease allow access on the next pop-ups to be able to make the call. - - ElementX needs permission to display notifications. Notifications can display your messages, your invitations, etc.\n\nPlease allow access on the next pop-ups to be able to view notification. + + ${app_name} needs permission to access your microphone to perform audio calls. + + ${app_name} needs permission to access your camera and your microphone to perform video calls.\n\nPlease allow access on the next pop-ups to be able to make the call. + + ${app_name} needs permission to display notifications. Notifications can display your messages, your invitations, etc.\n\nPlease allow access on the next pop-ups to be able to view notification. To scan a QR code, you need to allow camera access. Allow permission to access your contacts. @@ -884,7 +872,7 @@ System Settings. Notifications are enabled in the system settings. Notifications are disabled in the system settings.\nPlease check system settings. - ElementX needs the permission to show notifications.\nPlease grant the permission. + ${app_name} needs the permission to show notifications.\nPlease grant the permission. Open Settings Grant Permission @@ -895,8 +883,8 @@ Session Settings. Notifications are enabled for this session. - - Notifications are not enabled for this session.\nPlease check the ElementX settings. + + Notifications are not enabled for this session.\nPlease check the ${app_name} settings. Enable Custom Settings. @@ -905,19 +893,19 @@ Play Services Check Google Play Services APK is available and up-to-date. - - ElementX uses Google Play Services to deliver push messages but it doesn’t seem to be configured correctly:\n%1$s + + ${app_name} uses Google Play Services to deliver push messages but it doesn’t seem to be configured correctly:\n%1$s Fix Play Services Firebase Token FCM token successfully retrieved:\n%1$s Failed to retrieved FCM token:\n%1$s - - [%1$s]\nThis error is out of control of ElementX and according to Google, this error indicates that the device has too many apps registered with FCM. The error only occurs in cases where there are extreme numbers of apps, so it should not affect the average user. - - [%1$s]\nThis error is out of control of ElementX. It can occur for several reasons. Maybe it will work if you retry later, you can also check that Google Play Service is not restricted in data usage in the system settings, or that your device clock is correct, or it can happen on custom ROM. - - [%1$s]\nThis error is out of control of ElementX. There is no Google account on the phone. Please open the account manager and add a Google account. + + [%1$s]\nThis error is out of control of ${app_name} and according to Google, this error indicates that the device has too many apps registered with FCM. The error only occurs in cases where there are extreme numbers of apps, so it should not affect the average user. + + [%1$s]\nThis error is out of control of ${app_name}. It can occur for several reasons. Maybe it will work if you retry later, you can also check that Google Play Service is not restricted in data usage in the system settings, or that your device clock is correct, or it can happen on custom ROM. + + [%1$s]\nThis error is out of control of ${app_name}. There is no Google account on the phone. Please open the account manager and add a Google account. Add Account Token Registration @@ -939,20 +927,20 @@ Start on boot Service will start when the device is restarted. - - The service will not start when the device is restarted, you will not receive notifications until ElementX has been opened once. + + The service will not start when the device is restarted, you will not receive notifications until ${app_name} has been opened once. Enable Start on boot Check background restrictions - - Background restrictions are disabled for ElementX. This test should be run using mobile data (no WIFI).\n%1$s - - Background restrictions are enabled for ElementX.\nWork that the app tries to do will be aggressively restricted while it is in the background, and this could affect notifications.\n%1$s + + Background restrictions are disabled for ${app_name}. This test should be run using mobile data (no WIFI).\n%1$s + + Background restrictions are enabled for ${app_name}.\nWork that the app tries to do will be aggressively restricted while it is in the background, and this could affect notifications.\n%1$s Disable restrictions Battery Optimization - - ElementX is not affected by Battery Optimization. + + ${app_name} is not affected by Battery Optimization. If a user leaves a device unplugged and stationary for a period of time, with the screen off, the device enters Doze mode. This prevents apps from accessing the network and defers their jobs, syncs, and standard alarms. Ignore Optimization @@ -992,11 +980,11 @@ Background synchronization Background Sync Mode Optimized for battery - - ElementX will sync in background in way that preserves the device’s limited resources (battery).\nDepending on your device resource state, the sync may be deferred by the operating system. + + ${app_name} will sync in background in way that preserves the device’s limited resources (battery).\nDepending on your device resource state, the sync may be deferred by the operating system. Optimized for real time - - ElementX will sync in background periodically at precise time (configurable).\nThis will impact radio and battery usage, there will be a permanent notification displayed stating that ElementX is listening for events. + + ${app_name} will sync in background periodically at precise time (configurable).\nThis will impact radio and battery usage, there will be a permanent notification displayed stating that ${app_name} is listening for events. No background sync You will not be notified of incoming messages when the app is in background. @@ -1048,6 +1036,8 @@ Use /confetti command or send a message containing ❄️ or 🎉 Autoplay animated images Play animated images in the timeline as soon as they are visible + Enable direct share + Show recent chats in the system share menu Show join and leave events Invites, removes, and bans are unaffected. Show account events @@ -1075,12 +1065,12 @@ Analytics Send analytics data - - ElementX collects anonymous analytics to allow us to improve the application. + + ${app_name} collects anonymous analytics to allow us to improve the application. - Help improve ElementX + Help improve ${app_name} - Help us identify issues and improve ElementX by sharing anonymous usage data. To understand how people use multiple devices, we’ll generate a random identifier, shared by your devices.\n\nYou can read all our terms %s. + Help us identify issues and improve ${app_name} by sharing anonymous usage data. To understand how people use multiple devices, we’ll generate a random identifier, shared by your devices.\n\nYou can read all our terms %s. here We don\'t record or profile any account data We don\'t share information with third parties @@ -1099,7 +1089,7 @@ Allow integrations Integration manager - ElementX policy + ${app_name} policy Your homeserver policy Your identity server policy Third party libraries @@ -1520,8 +1510,8 @@ - - Please delete the passphrase if you want ElementX to generate a recovery key. + + Please delete the passphrase if you want ${app_name} to generate a recovery key. Never lose encrypted messages Messages in encrypted rooms are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.\n\nSecurely back up your keys to avoid losing them. @@ -1658,7 +1648,10 @@ It looks like you’re trying to connect to another homeserver. Do you want to sign out? Edit + Editing Reply + Replying to %s + Quoting Reply in thread View In Room @@ -1695,7 +1688,8 @@ Create New Room Create New Space No network. Please check your Internet connection. - Something went wrong. Please check your network connection and try again. + + Something went wrong. Please check your network connection and try again. "Change network" "Please wait…" Updating your data… @@ -1751,7 +1745,7 @@ Help Help and support - Get help with using ElementX + Get help with using ${app_name} Versions System settings @@ -1980,8 +1974,8 @@ Secure and independent communication that gives you the same level of privacy as a face-to-face conversation in your own home. Choose where your conversations are kept, giving you control and independence. Connected via Matrix. End-to-end encrypted and no phone number required. No ads or datamining. - - ElementX is also great for the workplace. It’s trusted by the world’s most secure organisations. + + ${app_name} is also great for the workplace. It’s trusted by the world’s most secure organisations. Who will you chat to the most? We\'ll help you get connected @@ -2230,8 +2224,8 @@ Clear data Clear all data currently stored on this device?\nSign in again to access your account data and messages. You’ll lose access to secure messages unless you sign in to recover your encryption keys. - - The current session is for user %1$s and you provide credentials for user %2$s. This is not supported by ElementX.\nPlease first clear data, then sign in again on another account. + + The current session is for user %1$s and you provide credentials for user %2$s. This is not supported by ${app_name}.\nPlease first clear data, then sign in again on another account. Your matrix.to link was malformed Cannot open this link: communities have been replaced by spaces @@ -2253,8 +2247,8 @@ Showing only the first results, type more letters… Fail-fast - - ElementX may crash more often when an unexpected error occurs + + ${app_name} may crash more often when an unexpected error occurs Show debug info on screen Show some useful info to help debugging the application @@ -2362,10 +2356,10 @@ Direct message Jump to read receipt - - ElementX does not handle events of type \'%1$s\' - - ElementX encountered an issue when rendering content of event with id \'%1$s\' + + ${app_name} does not handle events of type \'%1$s\' + + ${app_name} encountered an issue when rendering content of event with id \'%1$s\' Unignore @@ -2491,12 +2485,15 @@ Keys are already up to date! - - ElementX Android + + ${app_name} Android Key Requests Export Audit + Nightly build + Get the latest build (note: you may have trouble to sign in) + Unlock encrypted messages history Refresh @@ -2623,15 +2620,15 @@ Could not save media file Set a new account password… - - Use the latest ElementX on your other devices, ElementX Web, ElementX Desktop, ElementX iOS, ElementX for Android, or another cross-signing capable Matrix client - - ElementX Web\nElementX Desktop - - ElementX iOS\nElementX Android + + Use the latest ${app_name} on your other devices, ${app_name} Web, ${app_name} Desktop, ${app_name} iOS, ${app_name} for Android, or another cross-signing capable Matrix client + + ${app_name} Web\n${app_name} Desktop + + ${app_name} iOS\n${app_name} Android or another cross-signing capable Matrix client - - Use the latest ElementX on your other devices: + + Use the latest ${app_name} on your other devices: Forces the current outbound group session in an encrypted room to be discarded Only supported in encrypted rooms @@ -2659,8 +2656,12 @@ Unencrypted Encrypted by an unverified device The authenticity of this encrypted message can\'t be guaranteed on this device. - Review where you’re logged in - Verify all your sessions to ensure your account & messages are safe + + Review where you’re logged in + + Verify all your sessions to ensure your account & messages are safe + You have unverified sessions + Review to ensure your account is safe Verify the new login accessing your account: %1$s @@ -2686,10 +2687,10 @@ Inviting users… Invite Users Invite friends - - Hey, talk to me on ElementX: %s - - 🔐️ Join me on ElementX + + Hey, talk to me on ${app_name}: %s + + 🔐️ Join me on ${app_name} Invitation sent to %1$s Invitations sent to %1$s and %2$s "It's not a valid matrix QR code" @@ -2710,13 +2711,13 @@ Open terms of %s Disconnect from the identity server %s? - - This identity server is outdated. ElementX support only API V2. + + This identity server is outdated. ${app_name} support only API V2. This operation is not possible. The homeserver is outdated. Please first configure an identity server. Please first accepts the terms of the identity server in the settings. - - For your privacy, ElementX only supports sending hashed user email addresses and phone numbers. + + For your privacy, ${app_name} only supports sending hashed user email addresses and phone numbers. The association has failed. There is no current association with this identifier. The user consent has not been provided. @@ -2810,17 +2811,17 @@ If you want to reset your PIN, tap Forgot PIN to logout and reset. Enable biometrics Enable device specific biometrics, like fingerprints and face recognition. - - PIN code is the only way to unlock ElementX. + + PIN code is the only way to unlock ${app_name}. Could not enable biometric authentication. Show content in notifications Show details like room names and message content. Only display number of unread messages in a simple notification. Require PIN after 2 minutes - - PIN code is required after 2 minutes of not using ElementX. - - PIN code is required every time you open ElementX. + + PIN code is required after 2 minutes of not using ${app_name}. + + PIN code is required every time you open ${app_name}. Change PIN Change your current PIN Can\'t open a room where you are banned from. @@ -2879,8 +2880,8 @@ Slide to end the call Re-Authentication Needed - - ElementX requires you to enter your credentials to perform this action. + + ${app_name} requires you to enter your credentials to perform this action. Failed to authenticate Screenshot @@ -3035,7 +3036,7 @@ Auto Report Decryption Errors. Your system will automatically send logs when an unable to decrypt error occurs - Enable Thread Messages + Enable threaded messages Note: app will be restarted Show latest user info Show the latest profile info (avatar and display name) for all the messages. @@ -3104,18 +3105,24 @@ (%1$s) Live + Live broadcast + + Buffering… Resume voice broadcast record Pause voice broadcast record Stop voice broadcast record Play or resume voice broadcast Pause voice broadcast - Buffering Fast backward 30 seconds Fast forward 30 seconds Can’t start a new voice broadcast You don’t have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions. Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one. You are already recording a voice broadcast. Please end your current voice broadcast to start a new one. + + %1$s left + Stop live broadcasting? + Are you sure you want to stop your live broadcast? This will end the broadcast and the full recording will be available in the room. Anyone in %s will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime. Anyone in a parent space will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime. @@ -3129,7 +3136,7 @@ Link this email address with your account - %s in Settings to receive invites directly in ElementX. + %s in Settings to receive invites directly in ${app_name}. Enable LaTeX mathematics Restart the application for the change to take effect. @@ -3200,8 +3207,8 @@ 15 minutes 1 hour 8 hours - ElementX could not access your location - ElementX could not access your location. Please try again later. + ${app_name} could not access your location + ${app_name} could not access your location. Please try again later. Open with Failed to load map Unable to load map\nThis home server may not be configured to display maps. @@ -3214,7 +3221,7 @@ Stop %1$s left - ElementX Live Location + ${app_name} Live Location Location sharing is in progress Enable Live Location Sharing Temporary implementation: locations persist in room history @@ -3257,7 +3264,7 @@ Room notification - ElementX Screen Sharing + ${app_name} Screen Sharing Screen sharing is in progress Choose how to receive notifications @@ -3308,6 +3315,7 @@ Verify your current session for enhanced secure messaging. Verify or sign out from this session for best security and reliability. Verify your current session to reveal this session\'s verification status. + This session doesn\'t support encryption and thus can\'t be verified. Verify Session View Details View All (%1$d) @@ -3330,7 +3338,7 @@ Consider signing out from old sessions (%1$d day or more) that you don’t use anymore. Consider signing out from old sessions (%1$d days or more) that you don’t use anymore. - Current Session + Current session Session Device @@ -3361,6 +3369,14 @@ No inactive sessions found. Clear Filter Select sessions + Sign out + + Sign out of %1$d session + Sign out of %1$d sessions + + Sign out of all other sessions + Show IP address + Hide IP address Sign out of this session Session details Application, device, and activity information. @@ -3389,7 +3405,10 @@ Unverified sessions Unverified sessions are sessions that have logged in with your credentials but not been cross-verified.\n\nYou should make especially certain that you recognise these sessions as they could represent an unauthorised use of your account. Verified sessions - Verified sessions have logged in with your credentials and then been verified, either using your secure passphrase or by cross-verifying.\n\nThis means they hold encryption keys for your previous messages, and confirm to other users you are communicating with that these sessions are really you. + + Verified sessions have logged in with your credentials and then been verified, either using your secure passphrase or by cross-verifying.\n\nThis means they hold encryption keys for your previous messages, and confirm to other users you are communicating with that these sessions are really you. + Verified sessions are anywhere you are using this account after entering your passphrase or confirming your identity with another verified session.\n\nThis means that you have all the keys needed to unlock your encrypted messages and confirm to other users that you trust this session. + This session doesn\'t support encryption, so it can\'t be verified.\n\nYou won\'t be able to participate in rooms where encryption is enabled when using this session.\n\nFor best security and privacy, it is recommended to use Matrix clients that support encryption. Renaming sessions Other users in direct messages and rooms that you join are able to view a full list of your sessions.\n\nThis provides them with confidence that they are really speaking to you, but it also means they can see the session name you enter here. Enable new session manager @@ -3404,14 +3423,14 @@ Spaces are a new way to group rooms and people. Add an existing room, or create a new one, using the bottom-right button. - Welcome to ElementX,\n%s. + Welcome to ${app_name},\n%s. The all-in-one secure chat app for teams, friends and organisations. Create a chat, or join an existing room, to get started. Nothing to report. This is where your unread messages will show up, when you have some. Welcome to a new view! - To simplify your ElementX, tabs are now optional. Manage them using the top-right menu. + To simplify your ${app_name}, tabs are now optional. Manage them using the top-right menu. Access Spaces Access your Spaces (bottom-right) faster and easier than ever before. @@ -3460,11 +3479,26 @@ Confirm Please ensure that you know the origin of this code. By linking devices, you will provide someone with full access to your account. - + Apply bold format Apply italic format Apply strikethrough format Apply underline format + Set link Toggle full screen mode + Text + Link + Create a link + Edit link + + + In reply to + sent a file. + sent an audio file. + sent a voice message. + sent an image. + sent a video. + sent a sticker. + created a poll. diff --git a/libraries/ui-strings/src/main/res/values/strings_no_weblate.xml b/libraries/ui-strings/src/main/res/values/strings_no_weblate.xml new file mode 100644 index 0000000000..d9650bc797 --- /dev/null +++ b/libraries/ui-strings/src/main/res/values/strings_no_weblate.xml @@ -0,0 +1,23 @@ + + + + + + + + en + US + + Latn + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + diff --git a/settings.gradle.kts b/settings.gradle.kts index 245c0b2402..2e1ed9af84 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -40,6 +40,7 @@ include(":libraries:matrix") include(":libraries:matrixui") include(":libraries:textcomposer") include(":libraries:elementresources") +include(":libraries:ui-strings") include(":features:onboarding") include(":features:login") include(":features:logout") diff --git a/tools/strings/importStringsFromElement.sh b/tools/strings/importStringsFromElement.sh new file mode 100755 index 0000000000..b17adf06a5 --- /dev/null +++ b/tools/strings/importStringsFromElement.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +# +# Copyright (c) 2023 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +printf "\n" +printf "================================================================================\n" +printf "| Importing strings from Element |\n" +printf "================================================================================\n" + +basedir=`pwd` +tmpPath="${basedir}/tmpStrings" + +## Delete tmp dir +#rm -rf ${tmpPath} + +# Create tmp dir +mkdir ${tmpPath} + +printf "\n================================================================================\n" +printf "Downloading Element Android source from develop...\n" + +curl https://github.com/vector-im/element-android/archive/refs/heads/develop.zip -i -L -o ${tmpPath}/element.zip + +printf "\n================================================================================\n" +printf "Unzipping Element Android source...\n" + +unzip -q ${tmpPath}/element.zip -d ${tmpPath} + +printf "\n================================================================================\n" +printf "Importing the strings...\n" +elementAndroidPath="${tmpPath}/element-android-develop" + +cp -R ${elementAndroidPath}/library/ui-strings/src/main/res ${basedir}/libraries/ui-strings/src/main + +## Delete tmp dir +rm -rf ${tmpPath} + +# Commit all changes to git +# git commit -a -m "Import strings from Element Android" + +printf "\n================================================================================\n" +printf "Done\n" +printf "================================================================================\n"