Skip to content

Conversation

codokie
Copy link

@codokie codokie commented Apr 9, 2025

🎟️ Tracking

#4753

📔 Objective

Fixing text direction of LTR text in RTL layout, and vice versa.

📸 Screenshots

Screenshot

(see "Steps To Reproduce" in the tracked issue)

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 Reviewer guidelines

  • 👍 (:+1:) or similar for great changes
  • 📝 (:memo:) or ℹ️ (:information_source:) for notes or general info
  • ❓ (:question:) for questions
  • 🤔 (:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
  • 🎨 (:art:) for suggestions / improvements
  • ❌ (:x:) or ⚠️ (:warning:) for more significant problems or concerns needing attention
  • 🌱 (:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt
  • ⛏ (:pick:) for minor or nitpick changes

@S-Kakar
Copy link

S-Kakar commented Apr 9, 2025

Thank you for your contribution! We've added this to our internal Community PR board for review.
ID: PM-20026

@S-Kakar S-Kakar changed the title RTL text direction [PM-20026] RTL text direction Apr 9, 2025
@djsmith85 djsmith85 linked an issue Apr 9, 2025 that may be closed by this pull request
1 task
@codokie
Copy link
Author

codokie commented Apr 9, 2025

Note that there is still a problem with bi-directional text in the password history view.

Historic custom hidden passwords are not formatted properly. See the following screenshot:

bug

(Password is 1234 5678 in both cases, only field label has been changed. Current behavior is the one to the left.)

Upon some digging I've found this to be the root cause.

Possible solution:

PasswordHistoryView.password should not include the label of the hidden field. Instead, in PasswordHistoryListItem, add a separate Text composable for the label.

I could not figure out how to add a label property to PasswordHistoryView data class, so I may not be able to implement the fix for this. It also seems that old fields are stored in the cipher, so there would be a need to migrate them.

Alternatively, BidiFormatter may prove to be useful. See this Android doc for usage and more info about bi-directional text formatting.

@codokie codokie marked this pull request as ready for review April 9, 2025 21:15
@codokie
Copy link
Author

codokie commented Aug 26, 2025

@david-livefront @brian-livefront

Could you please review the proposed changes?

Thank you

Copy link
Contributor

@SaintPatrck SaintPatrck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I'm wondering if having a BidiTextManager and ProvidableCopositionLocal<BidiTextManager> would be a better way to handle formatting. Something along the lines of

interface BidiTextManager {
    fun unicodeWrap(text: String): String
}

val LocalBidiTextManager: ProvidableCompositionLocal<BidiTextManager> = compositionLocalOf {
    error("CompositionLocal BidiTextManager not present")
}

Then we can apply the formatting in our common Composables.

An example from BitwardenTextField would look like...

fun BitwardenTextField(...) {
    var textFieldValueState by remember { mutableStateOf(TextFieldValue(text = formattedText)) }
    val textFieldValue = textFieldVauleState.copy(
        text = LocalBidiTextManager.current.unicodeWrap(value),
    )
    // Remaining logic omitted...
}

With that approach I was able to achieve the following results

Image

Comment on lines +322 to +326
textAlign = if (LocalLayoutDirection.current == LayoutDirection.Rtl) {
TextAlign.Left
} else {
TextAlign.Right
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid manually setting the text alignment in screens like this.

We also do not want to use Left and Right alignments. We should be using Start and End so they respect the selected language's layout direction.

sensitiveInfoMedium = sensitiveInfoMedium.copy(textAlign = newTextAlign),
eyebrowMedium = eyebrowMedium.copy(textAlign = newTextAlign),
)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All files must end with a newline.

The Setup section of README has instructions on how to create a macro to enforce this and other Code style guidelines.

@@ -133,7 +132,6 @@ private fun EnterpriseSignOnScreenContent(
Spacer(modifier = Modifier.height(height = 12.dp))
Text(
text = stringResource(id = R.string.log_in_sso_summary),
textAlign = TextAlign.Start,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not be removing alignment assignments in the screens. Start and End will respect the layout direction of the selected language.

@@ -228,6 +245,7 @@ val bitwardenTypography: BitwardenTypography = BitwardenTypography(
trim = LineHeightStyle.Trim.None,
),
platformStyle = PlatformTextStyle(includeFontPadding = false),
textDirection = TextDirection.Ltr,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be using TextDirection.Content?

@@ -240,6 +258,7 @@ val bitwardenTypography: BitwardenTypography = BitwardenTypography(
trim = LineHeightStyle.Trim.None,
),
platformStyle = PlatformTextStyle(includeFontPadding = false),
textDirection = TextDirection.Ltr,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be using TextDirection.Content?

@SaintPatrck
Copy link
Contributor

Can you update your fork & branches? There are several conflicts since we've moved common logic and UI components to their own module.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

RTL text direction
3 participants