Skip to content

Commit 654e9c1

Browse files
committed
add more info about bidirectional text
1 parent 5dccb9a commit 654e9c1

File tree

1 file changed

+7
-7
lines changed

1 file changed

+7
-7
lines changed

packages/dev/docs/pages/blog/rtl-date-time.mdx

+7-7
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,25 @@ author: '[Yihui Liao](https://github.com/yihuiliao)'
2626

2727
# Improving Internationalization Support in Our Date and Time Components
2828

29-
Internationalization is a core feature of our Date and Time components. We support 13 different calendar systems as well as locale-specific formatting, number systems, and 12 and 24 hour time. However, we identified an issue in our support for several right-to-left (RTL) languages where in some right-to-left (RTL) languages, the format of the date and time fields was incorrect. While investigating this bug, we faced several challenges in ensuring accurate date and time representation in RTL languages and implemented various strategies that we’d like to share.
30-
29+
Internationalization is a core feature of our Date and Time components. We support 13 different calendar systems as well as locale-specific formatting, number systems, and 12 and 24 hour time. However, we identified an issue in our support for several right-to-left (RTL) languages where in some, the format of the date and time fields was incorrect. While investigating this bug, we faced several challenges in ensuring accurate date and time representation in RTL languages and implemented various strategies that we’d like to share.
3130

3231
## The Structure of Our Date and Time Components
3332

3433
In a [previous blog post](https://react-spectrum.adobe.com/blog/date-and-time-pickers-for-all.html#date-fields), we discussed the reasoning behind the component structure of our date and time components. In short, we designed these components to render individually focusable segments for each date and time unit, eliminating the challenges of parsing various date formats — an issue commonly encountered with free-form text fields. Since the date and time format is automatically determined based on the user’s locale, the user only needs to fill in the values without worrying about the appropriate separators or the order. This made for a smoother, more intuitive experience for the user, removing the guesswork associated with formatting and parsing dates in various locales.
3534

36-
3735
<Video src={localeVideoURL} muted />
3836

3937
## Unicode Bidirectional Algorithm
4038

41-
To format the segments according to the user locale, we rely on the browser’s [Unicode Bidirectional Algorithm](https://unicode.org/reports/tr9/). However, we found that some of our CSS styles were interfering with the algorithm's application, leading to incorrect formatting. For instance, in Hebrew (`he-IL`), the proper numeric date format should be `DD.MM.YYYY`, but our date component was displaying `YYYY.MM.DD`. This issue varied across different RTL languages for date fields, but for time fields, we observed a consistent problem across all RTL languages where time segments were flipped, rendering `MM:HH` instead of the correct `HH:MM` format.
39+
To format the segments according to the user locale, we relied on the Intl.DateTimeFormat API to help localize our dates and flex-box to handle the layout. Flexbox conveniently handled the right-to-left layouts by mirroring the segment order provided by the API which typically works well for RTL languages. However, in some cases, it is more complex. RTL languages often have bidirectional text, meaning it contains a mix of right-to-left and left-to-right elements. For example, in Arabic and Hebrew, numbers are always written from left-to-right. In cases where numbers are combined with text, the general flow proceeds right to left, but the numbers are written from left to right.
40+
41+
We found that dates behave bidirectionally in right-to-left languages and simply mirroring the segment order was too simplistic for this complex behavior. As a result, some of our date fields were formatted incorrectly. For instance, in Hebrew (`he-IL`), the proper numeric date format should be `DD.MM.YYYY`, but our date component was displaying `YYYY.MM.DD`. This issue varied across different RTL languages for date fields, but for time fields, we observed a consistent problem across all RTL languages where time segments were flipped, rendering `MM:HH` instead of the correct `HH:MM` format.
4242

4343
<RTLTimefield />
4444

45-
We found the culprit to be two things. First, we were applying `display: flex` on the container wrapping the segments. Second, each segment was being rendered as a `div` with `display: block`. Instead, we needed to use [normal CSS flow layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_display/Flow_layout) on the wrapper around the segments and update each segment to be a span instead.
45+
Instead of applying `display: flex` on the container wrapping the segments, we needed to use [normal CSS flow layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_display/Flow_layout) on the wrapper around the segments and update each segment to be a span instead. This allows the browser to apply the [Unicode Bidirectional Algorithm](https://unicode.org/reports/tr9/), which reorders the characters in right-to-left text according to a set of standardized rules.
4646

47-
While it seemed like a relatively simple fix, we later discovered through testing that this only corrected the format when segments contained actual values. If they had placeholder values, the order was still incorrect, causing some undesirable behaviors. It seemed that the Unicode Bidirectional Algorithm was interpreting placeholder values differently from actual values. As a result, when a segment was cleared back to its placeholder, it would shift back to the incorrect order. Furthermore, when a user entered a value, the segment would shift back to its correct order. This posed an interesting challenge: how do we ensure consistent formatting regardless of whether a segment contained a placeholder or a user-entered value — all without hard coding segment order for each locale?
47+
While it seemed like a relatively simple fix, we later discovered through testing that this only corrected the format when segments contained numeric values. If they had placeholder values, the order was still incorrect, causing some undesirable behaviors. This was because the placeholders are typically non-numeric characters, e.g. `'شهر'` representing "month" in Arabic. As a result, when the user pressed the Backspace key to clear a segment to its placeholder, the order of the segments would change. Then, when a user entered a numeric value, the segment would shift back to its correct order. This posed an interesting challenge: how do we ensure consistent formatting regardless of whether a segment contained a placeholder or a user-entered value — all without hard coding segment order for each locale?
4848

4949
Below is an example of how the dates were being formatted based on user-entered or placeholder values along with a video to demonstrate the shifting behavior:
5050

@@ -57,7 +57,7 @@ Below is an example of how the dates were being formatted based on user-entered
5757

5858
We first addressed time fields since they were easier to fix. As mentioned earlier, the segments in time fields for RTL languages were flipped. We learned, however, that regardless of locale, all time fields should follow the `HH:MM` format. Knowing this, we could apply a direction of left-to-right (LTR) on the numeric values across all segments in a time field.
5959

60-
Instead of wrapping the the segments in a [`<bdo>` tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdo) with a `dir=“ltr”` which would impact the DOM structure and have potentially introduce side effects, we chose to use the [LRI (left-to-right isolate) Unicode character](https://www.w3.org/International/questions/qa-bidi-unicode-controls) to encapsulate the time segments and force an LTR direction. Adding this Unicode character is the equivalent of wrapping the time segments in a `<bdo>` tag but offers several advantages. Since the character is invisible, there are no visual changes, and by adding it as a siblings to the segments, we avoided major structural changes to the DOM. Additionally, by enforcing a LTR direction, we no longer had to worry about whether the time field consisted of placeholders or actual values. Lastly, it ensured that when a date field included a time, that the time field appeared in the correct order with respect to the date field (e.g. `8:45 1/31/2025` instead of `1/31/2025 8:45`).
60+
Instead of wrapping the the segments in a [`<bdo>` tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdo) with a `dir=“ltr”` which would impact the DOM structure and have potentially introduce side effects, we chose to use the [LRI (left-to-right isolate) Unicode character](https://www.w3.org/International/questions/qa-bidi-unicode-controls) to encapsulate the time segments and force an LTR direction. Adding this Unicode character is the equivalent of wrapping the time segments in a `<bdo>` tag but offers several advantages. Since the character is invisible, there are no visual changes, and by adding it as a siblings to the segments, we avoided major structural changes to the DOM. Additionally, by enforcing a LTR direction, we no longer had to worry about whether the time field consisted of placeholders or numeric values. Lastly, it ensured that when a date field included a time, that the time field appeared in the correct order with respect to the date field (e.g. `8:45 1/31/2025` instead of `1/31/2025 8:45`).
6161

6262
Below is a simplified code example of how we utilize Unicode characters to enforce an left-to-right direction on the segments:
6363

0 commit comments

Comments
 (0)