primefaces / primereact

The Most Complete React UI Component Library
https://primereact.org
MIT License
6.89k stars 1.05k forks source link

Support RTL languages #3749

Closed kalinkrustev closed 1 year ago

kalinkrustev commented 1 year ago

Describe the feature you would like to see added

Improve CSS, so that components are rendered automatically and appropriately for right to left languages. This will require changes in:

Is your feature request related to a problem?

No response

Describe the solution you'd like

I think the best way is to use [dir='rtl'] in the CSS selectors and address the specific cases where left/right attributes are affected.

Describe alternatives you have considered

What I tried (and it worked well, but not perfect) is to include some additional CSS rules, as per the below example. You can observe the results in this Storybook https://master--626d34151d5489004a1c5228.chromatic.com/?path=/story/form--table-ar

import { createUseStyles } from 'react-jss';

const useStyles = createUseStyles({
    '@global': {
        html: {
            "&[dir='rtl']": {
                '& .left-0.right-0': {
                    left: '0px !important',
                    right: '0px !important'
                },
                '& .right-0': {
                    left: '0px !important',
                    right: 'unset !important'
                },
                '& .left-0': {
                    left: 'unset !important',
                    right: '0px !important'
                },
                '& .p-float-label > label': {
                    left: 'unset !important',
                    right: 'var(--inline-spacing)'
                },
                '& .mr-2': {
                    marginLeft: 'var(--inline-spacing) !important',
                    marginRight: 'unset !important'
                },
                '& .p-button-icon-left': {
                    marginLeft: 'var(--inline-spacing)',
                    marginRight: 'initial'
                },
                '& .pi-angle-double-left:before': {
                    content: '"\\e92e"'
                },
                '& .pi-angle-left:before': {
                    content: '"\\e932"'
                },
                '& .pi-angle-double-right:before': {
                    content: '"\\e92d"'
                },
                '& .pi-angle-right:before': {
                    content: '"\\e931"'
                },
                '& .p-datatable .p-datatable-tbody > tr > td': {
                    textAlign: 'right'
                },
                '& .p-calendar-w-btn-right .p-datepicker-trigger, & .p-buttonset .p-button:last-of-type': {
                    borderTopLeftRadius: 'var(--border-radius)',
                    borderBottomLeftRadius: 'var(--border-radius)',
                    borderTopRightRadius: '0px',
                    borderBottomRightRadius: '0px'
                },
                '& .p-calendar-w-btn-right .p-inputtext, & .p-inputnumber-buttons-stacked .p-inputnumber-input, & .p-buttonset .p-button:first-of-type': {
                    borderTopLeftRadius: '0px',
                    borderBottomLeftRadius: '0px',
                    borderTopRightRadius: 'var(--border-radius)',
                    borderBottomRightRadius: 'var(--border-radius)'
                },
                '& .p-inputnumber-buttons-stacked .p-button.p-inputnumber-button-up': {
                    borderTopLeftRadius: 'var(--border-radius)',
                    borderBottomLeftRadius: '0px',
                    borderTopRightRadius: '0px',
                    borderBottomRightRadius: '0px'
                },
                '& .p-inputnumber-buttons-stacked .p-button.p-inputnumber-button-down': {
                    borderTopLeftRadius: '0px',
                    borderBottomLeftRadius: 'var(--border-radius)',
                    borderTopRightRadius: '0',
                    borderBottomRightRadius: '0px'
                },
                '& .p-treeselect-close, & .p-multiselect-panel .p-multiselect-header .p-multiselect-close': {
                    marginLeft: 'initial',
                    marginRight: 'auto'
                },
                '& .p-tree .p-treenode-children': {
                    padding: '0 var(--content-padding) 0 0'
                },
                '& .p-multiselect-panel .p-multiselect-items .p-multiselect-item .p-checkbox, & .p-multiselect.p-multiselect-chip .p-multiselect-token, & .p-treeselect.p-treeselect-chip .p-treeselect-token, & .p-tabmenu .p-tabmenu-nav .p-tabmenuitem .p-menuitem-link .p-menuitem-icon': {
                    marginLeft: 'var(--inline-spacing)',
                    marginRight: '0px'
                },
                '& .p-multiselect.p-multiselect-chip .p-multiselect-token .p-multiselect-token-icon, & .p-chips .p-chips-multiple-container .p-chips-token .p-chips-token-icon, & .p-menubar-root-list > .p-menuitem > .p-menuitem-link .p-submenu-icon': {
                    marginLeft: '0px',
                    marginRight: 'var(--inline-spacing)'
                },
                '& .p-multiselect .p-multiselect-clear-icon': {
                    right: 'inherit',
                    left: '2.357rem'
                },
                '& ul.p-submenu-list': {
                    left: 0,
                    right: 'initial!important'
                },
                '& .p-buttonset .p-button:not(:last-child)': {
                    borderLeft: '0 none',
                    borderRight: '1px solid var(--surface-border)',
                    '&.p-highlight': {
                        borderColor: 'var(--primary-color)'
                    }
                },
                '& .pi-chevron-right, & .pi-chevron-left': {
                    rotate: '180deg'
                },
                '& .pi-caret-right:before': {
                    content: '"\\e904"'
                },
                '& .pi-caret-left:before': {
                    content: '"\\e905"'
                },
                '& .pi-arrow-right:before': {
                    content: '"\\e91a"'
                },
                '& .pi-arrow-left:before': {
                    content: '"\\e91b"'
                }
            }
        }
    }
});

Additional context

While my solution worked, it does not feel right. Let me know if you are interested in including RTL support in the libraries. I can help with ideas and also with providing proper Arabic translations to test with.

melloware commented 1 year ago

Definitely interested in RTL support

Relates to #2263

Relates to #3096

kalinkrustev commented 1 year ago

OK, good to know that. Let me know if I can help with something, but at least for PrimeFlex, I think somebody with more experience can take good decision about how to address it, maybe based on the example I shared above.

melloware commented 1 year ago

@kalinkrustev 2 things.

  1. Can you open a ticket on PrimeFlex Github issues with the changes you think you need: https://github.com/primefaces/primeflex And you can link back to this ticket.

  2. If you are interested in Arabic translation can you donate one here to PrimeLocale: https://github.com/primefaces/primelocale Someone dontated an "fa" Farsi one but its not complete if you look at the "en_US" translation that has the complete list of values.

kalinkrustev commented 1 year ago

OK, will do. Point 2 is complete https://github.com/primefaces/primelocale/pull/43 Do you think the RTL support deserves a separate documentation page, where the components, that make sense are showcased? This is where the translations can be activated for demonstration.

melloware commented 1 year ago

Thanks merged your translation!

As for RTL examples here is what we do in PrimeFaces JSF. We have an RTL example on the page like this one: https://www.primefaces.org/showcase/ui/input/oneMenu.xhtml

But in PrimeFaces JSF we actually have an rtl="true" property on each component as well as a global override if you want RTL enabled for the whole application.

kalinkrustev commented 1 year ago

I think the PrimeReact documentation pages can use the same way of choosing the language with a dropdown, so RTL can be observed in each page, though it is best observed when the texts are also written in the respective language.

The approach I proposed probably will not introduce a new component property, as it is based on the standard CSS attribute direction: rtl, which will also work on any level in the rendered markup. PrimeFlex can introduce two new classes: direction-rtl and direction-ltr for easier usage.

melloware commented 1 year ago

Yep I think the only issue we will run into is sometimes for RTL support in PRimeFaces JSF we had to manipulate the DOM or JS code as there were some things that just couldn't be done with CSS. But I like your idea though!

melloware commented 1 year ago

@kalinkrustev I added all your info to this ticket: https://github.com/primefaces/primereact/issues/3096 so you might want to subscribe to that ticket. I will close this one for now as a duplicate.