Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Request] RTL support #9530

Open
Zontex opened this issue May 17, 2024 · 21 comments
Open

[Request] RTL support #9530

Zontex opened this issue May 17, 2024 · 21 comments

Comments

@Zontex
Copy link

Zontex commented May 17, 2024

What version of Invoice Ninja are you running? ie v4.5 / v5

v5

What environment are you running?
ZIP / self hosted Debian Nginx

Have you searched existing issues/requests?
Yes

Additional context
I've seen it's possible to add labels to each function to translate the system but how to enable RTL support? is there a built-in RTL support and if not, any chances to add?

@Zontex Zontex changed the title [Request] RTL suport [Request] RTL support May 17, 2024
@turbo124
Copy link
Member

@Zontex if you are referring to the PDF's and RTL support, you would need to use css helpers which would enable this.

Within the UI of the application rtl is not supported.

@Zontex
Copy link
Author

Zontex commented May 17, 2024

@Zontex if you are referring to the PDF's and RTL support, you would need to use css helpers which would enable this.

Within the UI of the application rtl is not supported.

Hi thanks for your reply, yes this is exactly what I mean. is there any instructions available on how to do so?

@turbo124
Copy link
Member

@Zontex

these types of CSS properties are available:

p.rtl {
direction: rtl;
}

@Zontex
Copy link
Author

Zontex commented May 18, 2024

@Zontex

these types of CSS properties are available:

p.rtl { direction: rtl; }

Got you, thank you so much for the quick response! any plan to make it integrated option? once changed to RTL language such as Arabic/Hebrew it will automatically change the PDF/UI view?

@Zontex
Copy link
Author

Zontex commented May 18, 2024

I changed the invoice successfully to this CSS:

<style id="style">
    @import url($font_url);

    :root {
        --primary-color: $primary_color;
        --secondary-color: $secondary_color;
        --line-height: 1.6;
    }

    html {
        width: 210mm;
        height: 200mm;     
    }

    body {
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        font-family: $font_name, Helvetica, sans-serif;
        font-size: $font_size !important;
        zoom: 80%;
        direction: rtl; /* Added for RTL */
        text-align: right; /* Added for RTL */
    }

    table tr td, table tr, th {
        font-size: $font_size !important;
    }

    @page {
        margin-left: $global_margin;
        margin-right: $global_margin;
        margin-top: 5;
        margin-bottom: 5;
        size: $page_size $page_layout;
    }

    p {
        margin: 0;
        padding: 0;
    }
    
    p.rtl{
        direction: rtl;
        text-align: right;
    }
    
    #qr-bill {
        width: 100% !important;
        box-sizing: border-box;
        border-collapse: collapse;
        color: #000;
    }

    #qr-bill td {
        max-width: none;
    }

    .header-container {
        display: grid;
        grid-template-columns: 1fr 1fr 1fr;
        gap: 20px;
    }

    .company-logo-container {
        display: inline-block;
    }

    .company-logo {
        max-width: $company_logo_size;
    }

    #company-details {
        display: flex;
        flex-direction: column;
        line-height: var(--line-height);
        text-align: right; /* Added for RTL */
    }

    #company-details > p:first-child {
        color: var(--primary-color);
    }

    #company-address {
        display: flex;
        flex-direction: column;
        line-height: var(--line-height);
        text-align: right; /* Added for RTL */
    }

    .entity-label {
        margin-top: 2.5rem;
        text-transform: uppercase;
        padding-right: 1rem; /* Changed padding for RTL */
        margin-bottom: 1rem;
        font-weight: bold;
        color: var(--primary-color);
    }

    .client-and-entity-wrapper {
        padding: 1rem;
        display: grid;
        grid-template-columns: 1fr 1fr 1fr;
        border-top: 1px solid #d8d8d8;
        border-bottom: 1px solid #d8d8d8;
    }

    #entity-details {
        display: flex;
        text-align: right; /* Changed text alignment for RTL */
        margin-left: 20px; /* Changed margin for RTL */
        line-height: var(--line-height) !important;
    }

    #entity-details > tr,
    #entity-details th {
        font-weight: normal;
        padding-left: 15px; /* Changed padding for RTL */
        line-height: var(--line-height) !important;
    }

    #client-details {
        display: flex;
        flex-direction: column;
        line-height: var(--line-height);
        padding-left: 30px; /* Changed padding for RTL */
    }

    #client-details > :first-child {
        font-weight: bold;
    }

    #shipping-details {
        display: $show_shipping_address;
        flex-direction: column;
        line-height: var(--line-height);
    }

    [data-ref="table"] {
        margin-top: 1rem;
        margin-bottom: 5px;
        min-width: 100%;
        table-layout: fixed;
        overflow-wrap: break-word;
    }

    .task-time-details {
        display: block;
        margin-top: 5px;
        color: grey;
    }

    [data-ref="table"] > thead {
        text-align: right; /* Changed text alignment for RTL */
    }

    [data-ref="table"] > thead > tr > th {
        font-size: 1.1rem;
        padding-bottom: 1.5rem;
        padding-right: 1rem; /* Changed padding for RTL */
    }

    [data-ref="table"] > tbody > tr > td {
        border-top: 1px solid #d8d8d8;
        border-bottom: 1px solid #d8d8d8;
        padding: 1rem 1rem;
    }

    [data-ref="table"] > tbody > tr > td:first-child {
        color: var(--primary-color);
    }

    [data-ref="table"] > thead > tr > th:last-child,
    [data-ref="table"] > tbody > tr > td:last-child {
        text-align: left; /* Changed text alignment for RTL */
    }

    [data-ref="table"] > thead > tr > th:last-child {
        padding-left: 1rem; /* Changed padding for RTL */
    }

    [data-ref="table"] > tbody > tr:nth-child(odd) {
        background-color: #f5f5f5;
    }

    #table-totals {
        margin-top: 0rem;
        display: grid;
        grid-template-columns: 2fr 1fr;
        padding-top: 0rem;
        padding-right: 1rem;
        padding-left: 1rem;
        gap: 80px;
        page-break-inside: avoid;
        overflow: visible !important;
        text-align: right; /* Added for RTL */
    }

    #table-totals .totals-table-right-side > * {
        display: grid;
        grid-template-columns: 1fr 1fr;
    }

    #table-totals>.totals-table-right-side>*> :nth-child(1) {
        text-align: right; /* Changed text alignment for RTL */
        margin-top: .75rem;
    }

    #table-totals>.totals-table-right-side>*> :nth-child(2) {
        text-align: left; /* Changed text alignment for RTL */
    }

    #table-totals>.totals-table-right-side> * > :not([hidden]) ~ :not([hidden]) {
        --tw-space-y-reverse: 0;
        margin-top: calc(.75rem * calc(1 - var(--tw-space-y-reverse)));
        margin-bottom: calc(.75rem * var(--tw-space-y-reverse));
    }

    #table-totals
    > *
    [data-element='product-table-balance-due-label'],
    #table-totals
    > *
    [data-element='product-table-balance-due'] {
        font-weight: bold;
    }

    #table-totals
    > *
    [data-element='product-table-balance-due'] {
        color: var(--primary-color);
    }

    #table-totals > * > :last-child {
        text-align: left; /* Changed text alignment for RTL */
        padding-left: 0.5rem; /* Changed padding for RTL */
    }

    #footer {
        margin-top: 10px;
        margin-right: 1rem; /* Changed margin for RTL */
        text-align: right; /* Added for RTL */
    }

    /** Markdown-specific styles. **/
    #product-table h3,
    #task-table h3,
    #delivery-note-table h3 {
        font-size: 1rem;
        margin-bottom: 0;
    }

    [data-ref="total_table-public_notes"] {
        margin-top: 1rem;
    }

    [data-ref="statement-totals"] {
        margin-top: 1rem;
        text-align: right;
        margin-right: .75rem;
    }
    
    [data-ref*=".line_total-td"] {
        white-space: nowrap;
    }

    /** .repeating-header,
    .repeating-header-space, **/
    .repeating-footer,
    .repeating-footer-space {
        height: 10px;
    }
    .repeating-header {
        position: fixed;
        top: 0;
    }
    .repeating-footer {
        position: fixed;
        bottom: 0;
    }

    [data-element='product_table-product.description-td'], td {
        min-width: 100%;
        max-width: 300px;
        overflow-wrap: break-word; 
    }
    
    .stamp {
        transform: rotate(12deg);
        color: #555;
        font-size: 3rem;
        font-weight: 700;
        border: 0.25rem solid #555;
        display: inline-block;
        padding: 0.25rem 1rem;
        text-transform: uppercase;
        border-radius: 1rem;
        font-family: 'Courier';
        mix-blend-mode: multiply;
        z-index: 200 !important;
        position: fixed;
        text-align: center;
        float: right;
    }

    .is-paid {
        color:  #D23;
        border: 1rem double  #D23;
        transform: rotate(-5deg);
        font-size: 6rem;
        font-family: "Open sans", Helvetica, Arial, sans-serif;
        border-radius: 0;
        padding: 0.5rem;
        opacity: 0.2;
        z-index: 200 !important;
        position: fixed;
        display: $show_paid_stamp;
    } 

    .project-header {
        font-size: 1.2em;
        margin-top: 0.1em;
        margin-bottom: 0;
        padding-bottom: 0;
        margin-left: 0;
        margin-right: 0;
        font-weight: bold;
        color: #505050;
    } 

    .pqrcode {
        
    }
        
    /** Useful snippets, uncomment to enable. **/

    /** Hide company logo **/
    /* .company-logo { display: none } */

    /* Hide company details */
    /* #company-details > * { display: none } */

    /* Hide company address */
    /* #company-address > * { display: none } */

    /* Hide public notes */
    /* [data-ref="total_table-public_notes"] { display: none } */

    /* Hide terms label */
    /* [data-ref="total_table-terms-label"] { display: none } */

    /* Hide totals table */
    /* #table-totals { display: none } */

    /* Hide totals table left side */
    /* #table-totals div:first-child > * { display: none !important } */

    /* Hide totals table right side */
    /* .totals-table-right-side { display: none } */

    /** For more info, please check our docs: https://invoiceninja.github.io **/
    /** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
</style>

But I still haven't figured out how to RTL the entire UI. when I do RTL on the body, it breaks the UI. any suggestion?

@turbo124
Copy link
Member

@Zontex

RTL is super tricky, and it is not something I am familiar with at all.

For this to be supported, the invoice designs would need to be customized to suit with the correct CSS implementation.

This would be a task best suited for a user whose language is RTL, I am happy to assist however in getting the designs updated.

@Zontex
Copy link
Author

Zontex commented May 18, 2024

@Zontex

RTL is super tricky, and it is not something I am familiar with at all.

For this to be supported, the invoice designs would need to be customized to suit with the correct CSS implementation.

This would be a task best suited for a user whose language is RTL, I am happy to assist however in getting the designs updated.

I sent here the correct design for the invoice, confirmed it's working. multiple places need to update the RTL. I'm wondering if there is anyway within the framework to say something like IF hebrew then use RTL template, if not use normal template..?

@turbo124
Copy link
Member

@Zontex

I'm not sure this could be executed, The best solution for this would be to create a client/group setting for your RTL clients and set your RTL design as the default design for those clients.

@turbo124
Copy link
Member

@Zontex I see your design creates a complete mirror image, including reversing the columns of the invoice table. Is this the way RTL is support to work?

@Zontex
Copy link
Author

Zontex commented May 18, 2024

@Zontex

I'm not sure this could be executed, The best solution for this would be to create a client/group setting for your RTL clients and set your RTL design as the default design for those clients.

That's a brilliant idea. Then I can leave the UI in English and serve Israeli clients with RTL portal/invoicing. will dig deeper into it. I suggest to leave this ticket open, i'll get this all working and push updates perhaps others Arabic/Hebrew needs could be served with this information.

@Zontex
Copy link
Author

Zontex commented May 18, 2024

@Zontex I see your design creates a complete mirror image, including reversing the columns of the invoice table. Is this the way RTL is support to work?

Yes it's exactly the way RTL work. RTL is Right-to-left, everything is the opposite. If you keep English or any other non-rtl language, it will look weird, but in RTL it looks perfect.

@Zontex
Copy link
Author

Zontex commented May 18, 2024

@turbo124 There are many localization fields missing, how can I localize Hebrew? I guess as updates went, some fields hasn't been updated. would love some guidance on where to get the latest english file so I can localize it completely to Hebrew

@turbo124
Copy link
Member

@Zontex we manage our translations here, you can join up and adjust the translations as necessary

https://transifex.com

@Zontex
Copy link
Author

Zontex commented May 18, 2024

@Zontex

I'm not sure this could be executed, The best solution for this would be to create a client/group setting for your RTL clients and set your RTL design as the default design for those clients.

I just tested your advice it works completely well. I opened a group called it "israeli clients" and then created a template for each thing: portal, invoice etc... with RTL called it "invoice rtl" or "portal rtl" then assigned the template to that group.

@Zontex
Copy link
Author

Zontex commented May 18, 2024

@turbo124 Quick update regarding RTL exploration:

Screenshot 2024-05-18 at 20 58 42

I managed to perfectly design the portal to RTL based on the user group using the following code:

/* Override Bootstrap for RTL */
body {
    direction: rtl;
    text-align: right;
}

.container,
.container-fluid,
.row {
    direction: rtl;
}

.col,
.col-1,
.col-2,
.col-3,
.col-4,
.col-5,
.col-6,
.col-7,
.col-8,
.col-9,
.col-10,
.col-11,
.col-12,
.col-auto,
.col-sm,
.col-sm-1,
.col-sm-2,
.col-sm-3,
.col-sm-4,
.col-sm-5,
.col-sm-6,
.col-sm-7,
.col-sm-8,
.col-sm-9,
.col-sm-10,
.col-sm-11,
.col-sm-12,
.col-sm-auto,
.col-md,
.col-md-1,
.col-md-2,
.col-md-3,
.col-md-4,
.col-md-5,
.col-md-6,
.col-md-7,
.col-md-8,
.col-md-9,
.col-md-10,
.col-md-11,
.col-md-12,
.col-md-auto,
.col-lg,
.col-lg-1,
.col-lg-2,
.col-lg-3,
.col-lg-4,
.col-lg-5,
.col-lg-6,
.col-lg-7,
.col-lg-8,
.col-lg-9,
.col-lg-10,
.col-lg-11,
.col-lg-12,
.col-lg-auto,
.col-xl,
.col-xl-1,
.col-xl-2,
.col-xl-3,
.col-xl-4,
.col-xl-5,
.col-xl-6,
.col-xl-7,
.col-xl-8,
.col-xl-9,
.col-xl-10,
.col-xl-11,
.col-xl-12,
.col-xl-auto {
    float: right;
}

.card,
.nav,
.navbar,
.modal,
.dropdown-menu,
.tooltip,
.popover {
    direction: rtl;
}

.navbar .navbar-nav {
    float: right;
}

.navbar .navbar-nav .nav-item {
    float: right;
}

.dropdown-menu {
    right: 0;
    left: auto;
}

.tooltip .arrow::before {
    border-right-color: #000;
    border-left-color: transparent;
}

.popover .arrow::before {
    border-right-color: #fff;
    border-left-color: transparent;
}

/* Additional Bootstrap component adjustments */
.form-control {
    text-align: right;
}

.input-group .input-group-text,
.input-group .form-control {
    direction: rtl;
    text-align: right;
}

.input-group-prepend,
.input-group-append {
    float: right;
}

.input-group-prepend .input-group-text,
.input-group-append .input-group-text {
    direction: rtl;
}

.modal-dialog {
    float: right;
}

.breadcrumb {
    direction: rtl;
}

.pagination {
    direction: rtl;
}

.pagination .page-link {
    direction: rtl;
}

/* Additional custom styles */
.custom-rtl {
    direction: rtl;
    text-align: right;
}

/* Ensure all custom classes with RTL are properly aligned */
.custom-rtl .row,
.custom-rtl .col,
.custom-rtl .container,
.custom-rtl .container-fluid,
.custom-rtl .card,
.custom-rtl .nav,
.custom-rtl .navbar,
.custom-rtl .modal,
.custom-rtl .dropdown-menu,
.custom-rtl .tooltip,
.custom-rtl .popover,
.custom-rtl .breadcrumb,
.custom-rtl .pagination,
.custom-rtl .form-control,
.custom-rtl .input-group,
.custom-rtl .input-group-prepend,
.custom-rtl .input-group-append,
.custom-rtl .input-group-text {
    direction: rtl;
    text-align: right;
    float: right;
}

.custom-rtl .navbar .navbar-nav {
    float: right;
}

.custom-rtl .navbar .navbar-nav .nav-item {
    float: right;
}

.custom-rtl .dropdown-menu {
    right: 0;
    left: auto;
}

.custom-rtl .tooltip .arrow::before {
    border-right-color: #000;
    border-left-color: transparent;
}

.custom-rtl .popover .arrow::before {
    border-right-color: #fff;
    border-left-color: transparent;
}

Now the last issue that seems I can't resolve is the main dashboard. if I create an account to login to Invoice ninja with RTL language by default, I dont see a place where I can inject custom CSS code into the dashboard object? in the portal by injecting the code I could fix all the portal dashboard, any chance to put a priority into adding css/javascript custom code injection on the dashboard based on a specific user / user group?

@turbo124
Copy link
Member

The front end implementation would need to have the react code modified.

@beganovich is there an easy win to support RTL with the react UI?

@hillelcoren
Copy link
Member

Note: the desktop/mobile app supports RTL for Arabic, I assume it can be enabled for other languages as well.

image

@Zontex
Copy link
Author

Zontex commented May 19, 2024

Note: the desktop/mobile app supports RTL for Arabic, I assume it can be enabled for other languages as well.

image

Screenshot 2024-05-19 at 14 23 57

For me the desktop app still shows LTR (same as web), didn't try the mobile yet. I changed the Arabic in the desktop app also shows LTR, how does it show RTL for you? I'm using OSX desktop app

@hillelcoren
Copy link
Member

That's the web app, here are links to the desktop app.

https://invoiceninja.com/apps

@Zontex
Copy link
Author

Zontex commented May 19, 2024 via email

@hillelcoren
Copy link
Member

This is the macOS app, the screenshot you shared is the React web app.

https://apps.apple.com/us/app/invoice-ninja/id1503970375

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

No branches or pull requests

3 participants