From da1a75a7f491f47bbaec32838de9b98d7478782d Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Fri, 18 Mar 2022 13:25:30 -0700 Subject: [PATCH] [change] Refactor localization APIs * Control writing direction using 'dir' and 'lang' props. * Change the compiler output for RTL styles polyfill. * Export 'useLocaleContext' for working with writing direction context. * Turn I18nManager into a mock API --- .../src/moduleMap.js | 1 + .../docs/src/pages/docs/apis/i18n-manager.md | 54 ---- .../src/pages/docs/components/text-input.md | 4 +- .../docs/src/pages/docs/components/text.md | 2 +- .../docs/src/pages/docs/components/view.md | 6 +- .../src/pages/docs/concepts/accessibility.md | 2 +- .../src/pages/docs/concepts/localization.md | 60 ++-- .../docs/src/pages/docs/concepts/styling.md | 2 +- .../react-native-compatibility.md | 4 +- .../src/pages/docs/hooks/use-color-scheme.md | 2 +- .../pages/docs/hooks/use-locale-context.md | 37 +++ .../pages/docs/hooks/use-window-dimensions.md | 2 +- .../{i18n-manager => localization}/index.js | 45 ++- packages/react-native-web/package.json | 2 +- .../I18nManager/__tests__/index-test.js | 93 ------ .../src/exports/I18nManager/index.js | 40 +-- .../src/exports/Image/index.js | 2 +- .../src/exports/Picker/PickerItem.js | 2 +- .../__tests__/compiler-preprocess-test.js | 294 ------------------ .../StyleSheet/__tests__/compiler-test.js | 258 ++++++++++++++- .../StyleSheet/__tests__/index-test.js | 76 ++++- .../StyleSheet/__tests__/preprocess-test.js | 107 +++++++ .../src/exports/StyleSheet/compiler/index.js | 223 +++++++++++-- .../exports/StyleSheet/compiler/preprocess.js | 202 ------------ .../src/exports/StyleSheet/index.js | 60 ++-- .../src/exports/StyleSheet/preprocess.js | 105 +++++++ .../__snapshots__/index-test.js.snap | 39 ++- .../src/exports/Text/__tests__/index-test.js | 23 +- .../src/exports/Text/index.js | 22 +- .../src/exports/Text/types.js | 1 + .../src/exports/TextInput/index.js | 12 +- .../__snapshots__/index-test.js.snap | 51 ++- .../src/exports/View/__tests__/index-test.js | 35 ++- .../src/exports/View/index.js | 14 +- .../src/exports/View/types.js | 1 + .../src/exports/createElement/index.js | 16 +- .../src/exports/useLocaleContext/index.js | 10 + packages/react-native-web/src/index.js | 1 + .../src/modules/RTLContext/index.js | 15 - .../src/modules/createDOMProps/index.js | 5 +- .../src/modules/useLocale/index.js | 60 ++++ .../src/modules/useLocale/isLocaleRTL.js | 74 +++++ yarn.lock | 8 +- 43 files changed, 1183 insertions(+), 889 deletions(-) delete mode 100644 packages/docs/src/pages/docs/apis/i18n-manager.md create mode 100644 packages/docs/src/pages/docs/hooks/use-locale-context.md rename packages/examples/pages/{i18n-manager => localization}/index.js (94%) delete mode 100644 packages/react-native-web/src/exports/I18nManager/__tests__/index-test.js delete mode 100644 packages/react-native-web/src/exports/StyleSheet/__tests__/compiler-preprocess-test.js create mode 100644 packages/react-native-web/src/exports/StyleSheet/__tests__/preprocess-test.js delete mode 100644 packages/react-native-web/src/exports/StyleSheet/compiler/preprocess.js create mode 100644 packages/react-native-web/src/exports/StyleSheet/preprocess.js create mode 100644 packages/react-native-web/src/exports/useLocaleContext/index.js delete mode 100644 packages/react-native-web/src/modules/RTLContext/index.js create mode 100644 packages/react-native-web/src/modules/useLocale/index.js create mode 100644 packages/react-native-web/src/modules/useLocale/isLocaleRTL.js diff --git a/packages/babel-plugin-react-native-web/src/moduleMap.js b/packages/babel-plugin-react-native-web/src/moduleMap.js index 5f524da3..d33577a4 100644 --- a/packages/babel-plugin-react-native-web/src/moduleMap.js +++ b/packages/babel-plugin-react-native-web/src/moduleMap.js @@ -67,5 +67,6 @@ module.exports = { render: true, unmountComponentAtNode: true, useColorScheme: true, + useLocaleContext: true, useWindowDimensions: true }; diff --git a/packages/docs/src/pages/docs/apis/i18n-manager.md b/packages/docs/src/pages/docs/apis/i18n-manager.md deleted file mode 100644 index 7a331a66..00000000 --- a/packages/docs/src/pages/docs/apis/i18n-manager.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: I18nManager -date: Last Modified -permalink: /docs/i18n-manager/index.html -eleventyNavigation: - key: I18nManager - parent: APIs ---- - -{% import "fragments/macros.html" as macro with context %} - -:::lead -Control and query the layout and writing direction of the application. -::: - -```js -import { I18nManager } from 'react-native'; -``` - ---- - -## API - -### Static methods - -{% call macro.prop('allowRTL', '(boolean) => void') %} -Allow the application to display in RTL mode. -{% endcall %} - -{% call macro.prop('forceRTL', '(boolean) => void') %} -Force the application to display in RTL mode. -{% endcall %} - -{% call macro.prop('getConstants', '() => I18nConstants') %} -Determine how the application is handling bidi layout. -{% endcall %} - -{% call macro.prop('setPreferredLanguageRTL', '(boolean) => void') %} -Set the application's preferred writing direction to RTL. You may need to infer the user's preferred locale on the server (from HTTP headers) and decide whether it's an RTL language. (Web-only) -{% endcall %} - -### I18nConstants - -The object returned by `I18nManager.getConstants()`. - -{% call macro.prop('isRTL', 'boolean = false') %} -Whether the application is currently in RTL mode. -{% endcall %} - ---- - -## Examples - -{{ macro.codesandbox('i18n-manager') }} diff --git a/packages/docs/src/pages/docs/components/text-input.md b/packages/docs/src/pages/docs/components/text-input.md index 8eae1383..ebf9dd00 100644 --- a/packages/docs/src/pages/docs/components/text-input.md +++ b/packages/docs/src/pages/docs/components/text-input.md @@ -72,7 +72,7 @@ The initial value of the input. Useful for simple use-cases where you don't want {% endcall %} {% call macro.prop('dir', '?("auto" | "ltr" | "rtl") = "auto"') %} -Equivalent to [HTMLElement.dir](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dir) +Equivalent to [HTMLElement.dir](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dir). {% endcall %} {% call macro.prop('disabled', '?boolean = false') %} @@ -88,7 +88,7 @@ Hints at the type of data that might be entered by the user while editing the el {% endcall %} {% call macro.prop('lang', '?string') %} -Equivalent to [HTMLElement.lang](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang). +Equivalent to [HTMLElement.lang](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang). This prop is used to infer writing direction if no `dir` is set. {% endcall %} {% call macro.prop('maxLength', '?string') %} diff --git a/packages/docs/src/pages/docs/components/text.md b/packages/docs/src/pages/docs/components/text.md index 85efd84a..d7be63df 100644 --- a/packages/docs/src/pages/docs/components/text.md +++ b/packages/docs/src/pages/docs/components/text.md @@ -76,7 +76,7 @@ If `href` is defined, this prop defines related attributes to include on the anc {% endcall %} {% call macro.prop('lang', '?string') %} -Equivalent to [HTMLElement.lang](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang). +Equivalent to [HTMLElement.lang](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang). This prop is used to infer writing direction if no `dir` is set. {% endcall %} {% call macro.prop('nativeID', '?string') %} diff --git a/packages/docs/src/pages/docs/components/view.md b/packages/docs/src/pages/docs/components/view.md index 7b7ac71f..4e6ce8a7 100644 --- a/packages/docs/src/pages/docs/components/view.md +++ b/packages/docs/src/pages/docs/components/view.md @@ -62,7 +62,7 @@ Equivalent to [HTMLElement.dataset](https://developer.mozilla.org/en-US/docs/Web {% endcall %} {% call macro.prop('dir', '?("ltr" | "rtl")') %} -Equivalent to [HTMLElement.dir](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir) +Equivalent to [HTMLElement.dir](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir). {% endcall %} {% call macro.prop('focusable', '?boolean') %} @@ -77,6 +77,10 @@ If `href` is defined, the view is rendered as an anchor tag pointing to this URL If `href` is defined, this prop defines related attributes to include on the anchor (e.g., `download`, `rel`, `target`) which may modify its behavior. {% endcall %} +{% call macro.prop('lang', '?string') %} +Equivalent to [HTMLElement.lang](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang). This prop is used to infer writing direction if no `dir` is set. +{% endcall %} + {% call macro.prop('nativeID', '?string') %} Equivalent to [HTMLElement.id](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id). {% endcall %} diff --git a/packages/docs/src/pages/docs/concepts/accessibility.md b/packages/docs/src/pages/docs/concepts/accessibility.md index 4be2c8f8..ef08ca13 100644 --- a/packages/docs/src/pages/docs/concepts/accessibility.md +++ b/packages/docs/src/pages/docs/concepts/accessibility.md @@ -157,7 +157,7 @@ Equivalent to [aria-readonly](https://www.w3.org/TR/wai-aria-1.2/#aria-readonly) Equivalent to [aria-required](https://www.w3.org/TR/wai-aria-1.2/#aria-required). {% endcall %} -{% call macro.prop('accessibilityRole', '?boolean') %} +{% call macro.prop('accessibilityRole', '?string') %} Equivalent to [role](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). {% endcall %} diff --git a/packages/docs/src/pages/docs/concepts/localization.md b/packages/docs/src/pages/docs/concepts/localization.md index edbcb26d..fd2e2662 100644 --- a/packages/docs/src/pages/docs/concepts/localization.md +++ b/packages/docs/src/pages/docs/concepts/localization.md @@ -10,27 +10,15 @@ eleventyNavigation: {% import "fragments/macros.html" as macro with context %} :::lead -Localized layout is largely automatic if you follow this guide. +A guide to producing localized layout for different locales. ::: -{{ site.name }} provides several mechanisms to automatically flip application layout to match the writing direction of the primary language. +{{ site.name }} provides simple mechanisms to automatically flip layouts to match the writing direction of the app or a specific component tree. -## Localized component layout -To automatically flip the layout of a flexbox or grid container, set the `dir` prop on `View`, `Text`, or `TextInput` components to desired writing direction (e.g., `"rtl"`). By default, the native writing direction is set to `"auto"` for `Text` and `TextInput` elements. This uses the browser's built-in writing direction algorithm to detect whether the text should be displayed left-to-right or right-to-left. +## Localization styles -You can also set the `lang` prop on `Text` or `TextInput` to provide browsers with information about the language of the text. - -```jsx -const style = { alignItems: 'flex-start' }; -return ( - - ... - -); -``` - -The non-standard [direction-independent style properties]({{ '/docs/styling/#non-standard-properties' | url }}) should also be used as much as possible. {{ site.name }} will automatically flip the direction of these properties within subtrees based on the value of the `dir` prop on `View` or `Text` elements in the ancestral hierarchy. +The non-standard [direction-independent style properties and values]({{ '/docs/styling/#non-standard-properties' | url }}) (e.g., `marginStart`) should be used as much as possible. {{ site.name }} will automatically flip the direction of these properties within subtrees based on the writing direction of the ancestor tree. ```jsx // "start" is "left" for LTR and "right" for RTL @@ -40,26 +28,30 @@ return ( ); ``` -The `I18nManager` API can also be used to help with more fine-grained control of layout, e.g., flipping images or transforms. +## Localization props + +To automatically flip the layout of a component and its subtree, you can either: + +1. Set the `dir` prop to the desired writing direction (e.g., `"rtl"`). +2. Set the `lang` prop to the locale of the content. ```jsx -const { isRTL } = I18nManager.getConstants(); -const transform = { [{ scaleX: isRTL ? -1 : 1 }] }; +... +... +``` - +By default, the native writing direction of `Text` and `TextInput` components is set to `"auto"`. This uses the browser's built-in writing direction algorithm to detect whether the text should be displayed left-to-right or right-to-left. + +## Localization hook + +The `useLocaleContext` API can be used for fine-grained control of layout, e.g., flipping images or transforms. + +```jsx +const { direction, locale } = useLocaleContext(); + +const isRTL = direction === 'rtl'; +const style = { transform: [{ scaleX: isRTL ? -1 : 1 }] }; + + ``` - -## Localized application layout - -The application will automatically display as RTL if rendered *after* setting the direction to RTL. - -```js -// Either force RTL (e.g., for unit tests) -I18nManager.forceRTL(true); - -// Or set RTL if you know the language is RTL -I18nManager.setPreferredLanguageRTL(true); -``` - -Once the application is rendered in RTL mode, it will flip all the direction-independent styles and ensure that the `isRTL` constant is `true`. diff --git a/packages/docs/src/pages/docs/concepts/styling.md b/packages/docs/src/pages/docs/concepts/styling.md index 6ce47694..e00afbc7 100644 --- a/packages/docs/src/pages/docs/concepts/styling.md +++ b/packages/docs/src/pages/docs/concepts/styling.md @@ -66,7 +66,7 @@ You have greater control over how styles are composed when compared to using cla ### Short-form properties -The only supported short-form CSS properties accept only a *single* value. +The supported short-form CSS properties accept only a *single* value. {% call macro.prop('borderColor', '?string') %} Accepts only a single value that is applied to all sides. diff --git a/packages/docs/src/pages/docs/getting-started/react-native-compatibility.md b/packages/docs/src/pages/docs/getting-started/react-native-compatibility.md index 9d7bd0aa..7a23069b 100644 --- a/packages/docs/src/pages/docs/getting-started/react-native-compatibility.md +++ b/packages/docs/src/pages/docs/getting-started/react-native-compatibility.md @@ -36,7 +36,7 @@ Visit the [React Native Directory](https://reactnative.directory/?web=true) to f | SectionList | ✓ | | | StatusBar | (✓) | Mock. No equivalent web APIs. | | Switch | ✓ | | -| Text | ✓ | Missing `onLongPress` ([#1011](https://github.com/necolas/react-native-web/issues/1011)) support. | +| Text | ✓ | No `onLongPress` ([#1011](https://github.com/necolas/react-native-web/issues/1011)). | | TextInput | ✓ | Missing rich text features ([#1023](https://github.com/necolas/react-native-web/issues/1023)), and auto-expanding behaviour ([#795](https://github.com/necolas/react-native-web/issues/795)). | | Touchable | ✓ | Includes additional support for mouse and keyboard interactions. | | TouchableHighlight | ✓ | | @@ -63,7 +63,7 @@ Visit the [React Native Directory](https://reactnative.directory/?web=true) to f | Dimensions | ✓ | | | Easing | ✓ | | | Geolocation | ✓ | | -| I18nManager | ✓ | Includes additional support for runtime switch to RTL. | +| I18nManager | (✓) | Mock. See [localization](https://necolas.github.io/react-native-web/docs/localization/) for preferred approach. | | InteractionManager | (✓) | | | Keyboard | (✓) | Mock. | | LayoutAnimation | (✓) | Missing translation to web animations. | diff --git a/packages/docs/src/pages/docs/hooks/use-color-scheme.md b/packages/docs/src/pages/docs/hooks/use-color-scheme.md index 4c220ee9..a75ef350 100644 --- a/packages/docs/src/pages/docs/hooks/use-color-scheme.md +++ b/packages/docs/src/pages/docs/hooks/use-color-scheme.md @@ -10,7 +10,7 @@ eleventyNavigation: {% import "fragments/macros.html" as macro with context %} :::lead -The `useColorScheme` React hook provides and subscribes to color scheme updates from the `Appearance` module. +Respond to color scheme updates from the `Appearance` module. ::: The return value indicates the current user preferred color scheme. The value may be updated later, either through direct user action (e.g., theme selection in device settings) or on a schedule (e.g., light and dark themes that follow the day/night cycle). diff --git a/packages/docs/src/pages/docs/hooks/use-locale-context.md b/packages/docs/src/pages/docs/hooks/use-locale-context.md new file mode 100644 index 00000000..e94aa274 --- /dev/null +++ b/packages/docs/src/pages/docs/hooks/use-locale-context.md @@ -0,0 +1,37 @@ +--- +title: useLocaleContext +date: Last Modified +permalink: /docs/use-locale-context/index.html +eleventyNavigation: + key: useLocaleContext + parent: Hooks +--- + +{% import "fragments/macros.html" as macro with context %} + +:::lead +Respond to locale and writing direction changes from ancestors. +::: + +The return value matches the current locale and writing direction of the ancestral tree. The value will be updated for descendants when setting the `dir` or `lang` prop on an element. + +```js +import { useLocaleContext } from 'react-native'; +const { direction, locale } = useLocaleContext(); +``` + +--- + +## API + +### Return value + +`useLocaleContext` returns the locale context. + +{% call macro.prop('direction', '"ltr" | "rtl"') %} +A string representing the writing direction context. +{% endcall %} + +{% call macro.prop('locale', '?string') %} +The locale context. Locale is `null` if a new direction context is set without an associated locale. +{% endcall %} diff --git a/packages/docs/src/pages/docs/hooks/use-window-dimensions.md b/packages/docs/src/pages/docs/hooks/use-window-dimensions.md index fb861c92..7d9528e1 100644 --- a/packages/docs/src/pages/docs/hooks/use-window-dimensions.md +++ b/packages/docs/src/pages/docs/hooks/use-window-dimensions.md @@ -10,7 +10,7 @@ eleventyNavigation: {% import "fragments/macros.html" as macro with context %} :::lead -The `useWindowDimensions` React hook provides and subscribes to window size changes from the `Dimensions` module. +Respond to window size changes from the `Dimensions` module. ::: `useWindowDimensions` automatically updates `width` and `height` values when viewport size changes. diff --git a/packages/examples/pages/i18n-manager/index.js b/packages/examples/pages/localization/index.js similarity index 94% rename from packages/examples/pages/i18n-manager/index.js rename to packages/examples/pages/localization/index.js index 1126f288..547c7d33 100644 --- a/packages/examples/pages/i18n-manager/index.js +++ b/packages/examples/pages/localization/index.js @@ -16,7 +16,6 @@ import { Button, Image, PixelRatio, - Platform, ScrollView, StyleSheet, Text, @@ -88,10 +87,6 @@ function withRTLState(Component) { } const RTLToggler = ({ isRTL, setRTL }) => { - if (Platform.OS !== 'ios') { - return {isRTL ? 'RTL' : 'LTR'}; - } - const toggleRTL = () => setRTL(!isRTL); return (