diff --git a/client/config/webpack.config.js b/client/config/webpack.config.js
index f671e349b..3b4b4a595 100644
--- a/client/config/webpack.config.js
+++ b/client/config/webpack.config.js
@@ -27,6 +27,8 @@ const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
const CompressionPlugin = require("compression-webpack-plugin");
const postcssNormalize = require('postcss-normalize');
+const { postcssRTLCSS} = require('postcss-rtlcss');
+
const appPackageJson = require(paths.appPackageJson);
@@ -92,6 +94,14 @@ module.exports = function(webpackEnv) {
// https://github.com/facebook/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
+ // Postcss rtlcss plugin.
+ // require( 'postcss-rtl' )({
+ // // options here.
+ // removeComments: false,
+ // }),
+ postcssRTLCSS({
+
+ }),
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
@@ -120,6 +130,10 @@ module.exports = function(webpackEnv) {
loader: require.resolve(preProcessor),
options: {
sourceMap: true,
+ sassOptions: {
+ sourceComments: true,
+ outputStyle: 'expanded'
+ }
},
}
);
diff --git a/client/package.json b/client/package.json
index 61f6187e5..a0b40b717 100644
--- a/client/package.json
+++ b/client/package.json
@@ -61,7 +61,9 @@
"postcss-loader": "3.0.0",
"postcss-normalize": "8.0.1",
"postcss-preset-env": "6.7.0",
+ "postcss-rtl": "^1.7.3",
"postcss-safe-parser": "4.0.1",
+ "ramda": "^0.27.1",
"react": "^16.12.0",
"react-app-polyfill": "^1.0.6",
"react-body-classname": "^1.3.1",
@@ -71,7 +73,7 @@
"react-dropzone": "^11.0.1",
"react-error-boundary": "^3.0.2",
"react-hotkeys-hook": "^3.0.3",
- "react-intl": "^3.12.0",
+ "react-intl-universal": "^2.4.7",
"react-loadable": "^5.5.0",
"react-query": "^3.6.0",
"react-redux": "^7.1.3",
@@ -92,6 +94,7 @@
"redux-thunk": "^2.3.0",
"resolve": "1.15.0",
"resolve-url-loader": "3.1.1",
+ "rtl-detect": "^1.0.3",
"sass-loader": "8.0.2",
"semver": "6.3.0",
"style-loader": "0.23.1",
@@ -135,6 +138,7 @@
"@welldone-software/why-did-you-render": "^6.0.0-rc.1",
"compression-webpack-plugin": "^6.1.0",
"http-proxy-middleware": "^1.0.0",
+ "postcss-rtlcss": "^1.7.2",
"react-query-devtools": "^2.1.1",
"redux-devtools": "^3.5.0",
"typescript": "^4.1.2"
diff --git a/client/public/index.html b/client/public/index.html
index 35b8d3acb..9a52dc7d6 100644
--- a/client/public/index.html
+++ b/client/public/index.html
@@ -1,5 +1,5 @@
-
+
diff --git a/client/src/common/adjustmentType.js b/client/src/common/adjustmentType.js
index f2239f6f8..8db0f29e1 100644
--- a/client/src/common/adjustmentType.js
+++ b/client/src/common/adjustmentType.js
@@ -1,4 +1,6 @@
+import intl from 'react-intl-universal';
+
export default [
- { name: 'Decrement', value: 'decrement' },
- { name: 'Increment', value: 'increment' },
+ { name: intl.get('decrement'), value: 'decrement' },
+ { name: intl.get('increment'), value: 'increment' },
]
\ No newline at end of file
diff --git a/client/src/common/contactsOptions.js b/client/src/common/contactsOptions.js
index 0cbe1a5ed..f03a49e22 100644
--- a/client/src/common/contactsOptions.js
+++ b/client/src/common/contactsOptions.js
@@ -1,4 +1,7 @@
+import React from 'react';
+import intl from 'react-intl-universal';
+
export default [
- { name: 'Customer', path: 'customers' },
- { name: 'Vendor', path: 'vendors' },
+ { name: intl.get('customer'), path: 'customers' },
+ { name: intl.get('vendor'), path: 'vendors' },
];
diff --git a/client/src/common/countries.js b/client/src/common/countries.js
index cf08fcf48..c86568887 100644
--- a/client/src/common/countries.js
+++ b/client/src/common/countries.js
@@ -1,5 +1,3 @@
+import intl from 'react-intl-universal';
-
-export default [
- { name: 'Libya', value: 'libya' },
-]
\ No newline at end of file
+export default [{ name: intl.get('libya'), value: 'libya' }];
diff --git a/client/src/common/currencies.js b/client/src/common/currencies.js
index 8e9ffd911..ae1dbd6d9 100644
--- a/client/src/common/currencies.js
+++ b/client/src/common/currencies.js
@@ -1,5 +1,7 @@
+import intl from 'react-intl-universal';
+
export default [
- { name: 'US Dollar', code: 'USD' },
- { name: 'Euro', code: 'EUR' },
- { name: 'Libyan Dinar ', code: 'LYD' },
-]
\ No newline at end of file
+ { name: intl.get('us_dollar'), code: 'USD' },
+ { name: intl.get('euro'), code: 'EUR' },
+ { name: intl.get('libyan_diner'), code: 'LYD' },
+];
diff --git a/client/src/common/dateFormatsOptions.js b/client/src/common/dateFormatsOptions.js
index bed2cda21..8c6ed99a2 100644
--- a/client/src/common/dateFormatsOptions.js
+++ b/client/src/common/dateFormatsOptions.js
@@ -1,40 +1,41 @@
import moment from 'moment';
+import intl from 'react-intl-universal';
export default [
{
id: 1,
- name: 'MM/DD/YY',
+ name: intl.get('mm_dd_yy'),
label: `${moment().format('MM/DD/YYYY')}`,
value: 'mm/dd/yy',
},
{
id: 2,
- name: 'DD/MM/YY',
+ name: intl.get('dd_mm_yy'),
label: `${moment().format('DD/MM/YYYY')}`,
value: 'dd/mm/yy',
},
{
id: 3,
- name: 'YY/MM/DD',
+ name: intl.get('yy_mm_dd'),
label: `${moment().format('YYYY/MM/DD')}`,
value: 'yy/mm/dd',
},
{
id: 4,
- name: 'MM-DD-YY',
+ name: intl.get('mm_dd_yy'),
label: `${moment().format('MM-DD-YYYY')}`,
value: 'mm-dd-yy',
},
{
id: 5,
- name: 'DD-MM-YY',
+ name: intl.get('dd_mm_yy_'),
label: `${moment().format('DD-MM-YYYY')}`,
value: 'dd-mm-yy',
},
{
id: 6,
- name: 'YY-MM-DD',
+ name: intl.get('yy_mm_dd_'),
label: `${moment().format('YYYY-MM-DD')}`,
value: 'yy-mm-dd',
},
-]
\ No newline at end of file
+];
diff --git a/client/src/common/fiscalYearOptions.js b/client/src/common/fiscalYearOptions.js
index 1a89aabb7..24043ec80 100644
--- a/client/src/common/fiscalYearOptions.js
+++ b/client/src/common/fiscalYearOptions.js
@@ -1,88 +1,64 @@
-import { formatMessage } from 'services/intl';
+import intl from 'react-intl-universal';
-export default [
+export const getFiscalYearOptions = () => [
{
id: 0,
- name: `${formatMessage({ id: 'january' })} - ${formatMessage({
- id: 'december',
- })}`,
+ name: `${intl.get('january')} - ${intl.get('december')}`,
value: 'january',
},
{
id: 1,
- name: `${formatMessage({ id: 'february' })} - ${formatMessage({
- id: 'january',
- })}`,
+ name: `${intl.get('february')} - ${intl.get('january')}`,
value: 'february',
},
{
id: 2,
- name: `${formatMessage({ id: 'march' })} - ${formatMessage({
- id: 'february',
- })}`,
+ name: `${intl.get('march')} - ${intl.get('february')}`,
value: 'March',
},
{
id: 3,
- name: `${formatMessage({ id: 'april' })} - ${formatMessage({
- id: 'march',
- })}`,
+ name: `${intl.get('april')} - ${intl.get('march')}`,
value: 'april',
},
{
id: 4,
- name: `${formatMessage({ id: 'may' })} - ${formatMessage({
- id: 'april',
- })}`,
+ name: `${intl.get('may')} - ${intl.get('april')}`,
value: 'may',
},
{
id: 5,
- name: `${formatMessage({ id: 'june' })} - ${formatMessage({
- id: 'may',
- })}`,
+ name: `${intl.get('june')} - ${intl.get('may')}`,
value: 'june',
},
{
id: 6,
- name: `${formatMessage({ id: 'july' })} - ${formatMessage({
- id: 'june',
- })}`,
+ name: `${intl.get('july')} - ${intl.get('june')}`,
value: 'july',
},
{
id: 7,
- name: `${formatMessage({ id: 'august' })} - ${formatMessage({
- id: 'july',
- })}`,
+ name: `${intl.get('august')} - ${intl.get('july')}`,
value: 'August',
},
{
id: 8,
- name: `${formatMessage({ id: 'september' })} - ${formatMessage({
- id: 'august',
- })}`,
+ name: `${intl.get('september')} - ${intl.get('august')}`,
value: 'september',
},
{
id: 9,
- name: `${formatMessage({ id: 'october' })} - ${formatMessage({
- id: 'november',
- })}`,
+ name: `${intl.get('october')} - ${intl.get('november')}`,
value: 'october',
},
{
id: 10,
- name: `${formatMessage({ id: 'november' })} - ${formatMessage({
- id: 'october',
- })}`,
+ name: `${intl.get('november')} - ${intl.get('october')}`,
value: 'november',
},
{
id: 11,
- name: `${formatMessage({ id: 'december' })} - ${formatMessage({
- id: 'november',
- })}`,
+ name: `${intl.get('december')} - ${intl.get('november')}`,
value: 'december',
},
]
\ No newline at end of file
diff --git a/client/src/common/homepageOptions.js b/client/src/common/homepageOptions.js
index 681ceef20..82972db3a 100644
--- a/client/src/common/homepageOptions.js
+++ b/client/src/common/homepageOptions.js
@@ -1,38 +1,35 @@
import React from 'react';
-import { FormattedMessage as T } from 'react-intl';
+import { FormattedMessage as T } from 'components';
export const accountsReceivable = [
{
sectionTitle: ,
shortcuts: [
{
- title: 'Sales invoices',
- description: 'Tracking sales invoices with your customers with payment due date.',
+ title: ,
+ description: ,
link: '/invoices',
},
{
- title: 'Sales estimates',
- description:
- 'Manage your sales estimates to create quotes that can later be turned to a sale invoice.',
-
+ title: ,
+ description: ,
link: '/estimates',
},
{
- title: 'Sales receipts',
- description:
- 'Manage sales receipts for sales that get paid immediately from the customer.',
-
+ title: ,
+ description: ,
link: '/receipts',
},
{
- title: 'Customers',
- description: 'Manage the customers relations with customer receivable and credit balances.',
+ title: ,
+ description: ,
link: '/customers',
},
{
- title: 'Customers payment',
- description:
- 'Manage payment transactions from your customers with sale invoices.',
+ title: ,
+ description: (
+
+ ),
link: '/payment-receives',
},
],
@@ -44,20 +41,22 @@ export const accountsPayable = [
sectionTitle: ,
shortcuts: [
{
- title: 'Purchase invoices',
- description: 'Manage the purchase invoices with your vendors with payment due date.',
+ title: ,
+ description: (
+
+ ),
link: '/bills',
},
{
- title: 'Vendors',
- description: 'Manage the vendors relations with vendor payable and debit balances.',
+ title: ,
+ description: (
+
+ ),
link: '/vendors',
},
{
- title: 'Vendors payments',
- description:
- 'Manage payments transactions to your vendors with purchase invoices.',
-
+ title: ,
+ description: ,
link: '/payment-mades',
},
],
@@ -69,26 +68,29 @@ export const financialAccounting = [
sectionTitle: ,
shortcuts: [
{
- title: 'Chart of accounts',
- description:
- 'Manage your accounts chart to record your transactions and categorise your transactions in parent accounts.',
+ title: ,
+ description: (
+
+ ),
link: '/accounts',
},
{
- title: 'Manual journal',
- description: 'Manage manual journal transactions on accounts, cost centra and projects.',
+ title: ,
+ description:,
link: '/manual-journals',
},
{
- title: 'Expenses',
- description:
- 'Track your indirect expenses under specific categories such as payroll, rent.',
+ title: ,
+ description:,
link: '/expenses',
},
{
- title: 'Financial statements',
- description:
- 'Show financial reports about your organization to summarize your business’s financial performance.',
+ title: ,
+ description:,
link: '/financial-reports',
},
],
@@ -100,20 +102,18 @@ export const productsServices = [
sectionTitle: ,
shortcuts: [
{
- title: 'Products & Services',
- description:
- 'Manage your products (inventory or non-inventory) and services and place them into categories.',
+ title: ,
+ description:,
link: '/items',
},
{
- title: 'Products & Services Categories',
- description:
- 'Group your products and service into different categories.',
+ title: ,
+ description:,
link: 'items/categories',
},
{
- title: 'Inventory Adjustments',
- description: 'Manage your inventory adjustment of inventory items.',
+ title: ,
+ description: ,
link: '/inventory-adjustments',
},
],
diff --git a/client/src/common/keyboardShortcutsOptions.js b/client/src/common/keyboardShortcutsOptions.js
index 8bd1efd33..c33e5a705 100644
--- a/client/src/common/keyboardShortcutsOptions.js
+++ b/client/src/common/keyboardShortcutsOptions.js
@@ -1,102 +1,105 @@
+import React from 'react';
+import intl from 'react-intl-universal';
+
export default [
{
shortcut_key: 'Shift + I',
- description: 'Jump to the invoices.',
+ description: intl.get('jump_to_the_invoices'),
},
{
shortcut_key: 'Shift + E',
- description: 'Jump to the estimates.',
+ description: intl.get('jump_to_the_estimates'),
},
{
shortcut_key: 'Shift + R',
- description: 'Jump to the receipts.',
+ description: intl.get('jump_to_the_receipts'),
},
{
shortcut_key: 'Shift + X',
- description: 'Jump to the expenses.',
+ description: intl.get('jump_to_the_expenses'),
},
{
shortcut_key: 'Shift + C',
- description: 'Jump to the customers.',
+ description: intl.get('jump_to_the_customers'),
},
{
shortcut_key: 'Shift + V',
- description: 'Jump to the vendors.',
+ description: intl.get('jump_to_the_vendors'),
},
{
shortcut_key: 'Shift + A',
- description: 'Jump to the Chart of Accounts.',
+ description: intl.get('jump_to_the_chart_of_accounts'),
},
{
shortcut_key: 'Shift + B',
- description: 'Jump to the bills.',
+ description: intl.get('jump_to_the_bills'),
},
{
shortcut_key: 'Shift + M',
- description: 'Jump to the Manual Journals.',
+ description: intl.get('jump_to_the_manual_journals'),
},
{
shortcut_key: 'Shift + W',
- description: 'Jump to the items.',
+ description: intl.get('jump_to_the_items'),
},
{
shortcut_key: 'Shift + 1',
- description: 'Jump to the Balance Sheet.',
+ description: intl.get('jump_to_the_balance_sheet'),
},
{
shortcut_key: 'Shift + 2',
- description: 'Jump to the Profit Loss Sheet.',
+ description: intl.get('jump_to_the_profit_loss_sheet'),
},
{
shortcut_key: 'Shift + 3',
- description: 'Jump to the Journal Sheet.',
+ description: intl.get('jump_to_the_journal_sheet'),
},
{
shortcut_key: 'Shift + 4',
- description: 'Jump to the General Ledger Sheet.',
+ description: intl.get('jump_to_the_general_ledger_sheet'),
},
{
shortcut_key: 'Shift + 5',
- description: 'Jump to the Trial Balance Sheet.',
+ description: intl.get('jump_to_the_trial_balance_sheet'),
},
{
shortcut_key: 'Ctrl + Shift + I ',
- description: 'Create a new invoice .',
+ description: intl.get('create_a_new_invoice'),
},
{
shortcut_key: 'Ctrl + Shift + E ',
- description: 'Create a new estimate .',
+ description: intl.get('create_a_new_estimate'),
},
{
shortcut_key: 'Ctrl + Shift + R ',
- description: 'Create a new receipt .',
+ description: intl.get('create_a_new_receipt'),
},
{
shortcut_key: 'Ctrl + Shift + X ',
- description: 'Create a new expense.',
+ description: intl.get('create_a_new_expense'),
},
{
shortcut_key: 'Ctrl + Shift + C ',
- description: 'Create a new customer.',
+ description: intl.get('create_a_new_customer'),
},
{
shortcut_key: 'Ctrl + Shift + V ',
- description: 'Create a new vendor.',
+ description: intl.get('create_a_new_vendor'),
},
{
shortcut_key: 'Ctrl + Shift + B ',
- description: 'Create a new bill.',
+ description: intl.get('create_a_new_bill'),
},
{
shortcut_key: 'Ctrl + Shift + M ',
- description: 'Create a new Make Journal.',
+ description: intl.get('create_a_new_journal'),
},
{
shortcut_key: 'Ctrl + Shift + W ',
- description: 'Create a new item.',
+ description: intl.get('create_a_new_item'),
},
{
shortcut_key: 'Ctrl + / ',
- description: 'Close and open sidebar .',
+ description: intl.get('close_and_open_sidebar'),
},
];
diff --git a/client/src/common/languagesOptions.js b/client/src/common/languagesOptions.js
index b331d32ec..77ddb2f6b 100644
--- a/client/src/common/languagesOptions.js
+++ b/client/src/common/languagesOptions.js
@@ -1,6 +1,6 @@
-
+import intl from 'react-intl-universal';
export default [
- { name: 'English', value: 'en' },
- { name: 'Arabic', value: 'ar' },
-];
\ No newline at end of file
+ { name: intl.get('english'), value: 'en' },
+ { name: intl.get('arabic'), value: 'ar' },
+];
diff --git a/client/src/common/numberFormatsOptions.js b/client/src/common/numberFormatsOptions.js
index 0315eae93..4e2b6ccf8 100644
--- a/client/src/common/numberFormatsOptions.js
+++ b/client/src/common/numberFormatsOptions.js
@@ -1,7 +1,9 @@
+import intl from 'react-intl-universal';
+
export const moneyFormat = [
- { key: 'total', text: 'Total rows' },
- { key: 'always', text: 'Always' },
- { key: 'none', text: 'None' },
+ { key: 'total', text: intl.get('total_rows') },
+ { key: 'always', text: intl.get('always') },
+ { key: 'none', text: intl.get('none') },
];
export const negativeFormat = [
diff --git a/client/src/common/quickNewOptions.js b/client/src/common/quickNewOptions.js
index a5a9efd68..6ebb1ec57 100644
--- a/client/src/common/quickNewOptions.js
+++ b/client/src/common/quickNewOptions.js
@@ -1,8 +1,10 @@
-export default [
- { path: 'invoices/new', name: 'Sale invoice' },
- { path: 'bills//new', name: 'Purchase invoice' },
- { path: 'make-journal-entry', name: 'Manual journal' },
- { path: 'expenses/new', name: 'Expense' },
- { path: 'customers/new', name: 'Customer' },
- { path: 'vendors/new', name: 'Vendor' },
+import intl from 'react-intl-universal';
+
+export const getQuickNewActions = () => [
+ { path: 'invoices/new', name: intl.get('sale_invoice') },
+ { path: 'bills//new', name: intl.get('purchase_invoice') },
+ { path: 'make-journal-entry', name: intl.get('manual_journal') },
+ { path: 'expenses/new', name: intl.get('expense') },
+ { path: 'customers/new', name: intl.get('customer') },
+ { path: 'vendors/new', name: intl.get('vendor') },
];
diff --git a/client/src/common/registerWizard.js b/client/src/common/registerWizard.js
index 634f2271a..900822f01 100644
--- a/client/src/common/registerWizard.js
+++ b/client/src/common/registerWizard.js
@@ -1,16 +1,16 @@
+import intl from 'react-intl-universal';
-
-export const registerWizardSteps = [
+export const getSetupWizardSteps = () => [
{
- label: 'payment_or_trial',
+ label: intl.get('Plans & Payment'),
},
{
- label: 'initializing',
+ label: intl.get('Initializing'),
},
{
- label: 'getting_started',
+ label: intl.get('Getting started'),
},
{
- label: 'Congratulations',
+ label: intl.get('Congratulations'),
},
];
\ No newline at end of file
diff --git a/client/src/components/AccountsMultiSelect.js b/client/src/components/AccountsMultiSelect.js
index d9e24c7e5..726bd7f18 100644
--- a/client/src/components/AccountsMultiSelect.js
+++ b/client/src/components/AccountsMultiSelect.js
@@ -2,7 +2,7 @@ import React, { useMemo, useCallback, useState } from 'react';
import { omit } from 'lodash';
import { MenuItem, Button } from '@blueprintjs/core';
import MultiSelect from 'components/MultiSelect';
-import { FormattedMessage as T } from 'react-intl';
+import { FormattedMessage as T } from 'components';
export default function AccountsMultiSelect({ accounts, onAccountSelected }) {
const [selectedAccounts, setSelectedAccounts] = useState({});
@@ -56,7 +56,7 @@ export default function AccountsMultiSelect({ accounts, onAccountSelected }) {
return (
}
+ noResults={} />}
itemRenderer={accountItem}
popoverProps={{ minimal: true }}
filterable={true}
diff --git a/client/src/components/AccountsSelectList.js b/client/src/components/AccountsSelectList.js
index 7b173dff2..cddbcbe81 100644
--- a/client/src/components/AccountsSelectList.js
+++ b/client/src/components/AccountsSelectList.js
@@ -1,7 +1,7 @@
import React, { useCallback, useState, useEffect, useMemo } from 'react';
import { MenuItem, Button } from '@blueprintjs/core';
import { Select } from '@blueprintjs/select';
-import { FormattedMessage as T } from 'react-intl';
+import { FormattedMessage as T } from 'components';
import classNames from 'classnames';
import { filterAccountsByQuery } from './utils';
import { CLASSES } from 'common/classes';
diff --git a/client/src/components/AccountsSuggestField.js b/client/src/components/AccountsSuggestField.js
index bdd1742a5..177aecc60 100644
--- a/client/src/components/AccountsSuggestField.js
+++ b/client/src/components/AccountsSuggestField.js
@@ -1,11 +1,12 @@
import React, { useState, useCallback, useEffect, useMemo } from 'react';
import { MenuItem } from '@blueprintjs/core';
import { Suggest } from '@blueprintjs/select';
+import intl from 'react-intl-universal';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
-import { FormattedMessage as T } from 'react-intl';
+import { FormattedMessage as T } from 'components';
import { filterAccountsByQuery } from './utils';
/**
@@ -15,7 +16,7 @@ export default function AccountsSuggestField({
accounts,
initialAccountId,
selectedAccountId,
- defaultSelectText = 'Select account',
+ defaultSelectText = intl.formatMessage({ id: 'select_account' }),
popoverFill = false,
onAccountSelected,
@@ -32,7 +33,7 @@ export default function AccountsSuggestField({
filterByRootTypes,
filterByParentTypes,
filterByTypes,
- filterByNormal,
+ filterByNormal,
});
return filteredAccounts;
}, [
diff --git a/client/src/components/App.js b/client/src/components/App.js
index 9df37df3c..f851f4234 100644
--- a/client/src/components/App.js
+++ b/client/src/components/App.js
@@ -1,53 +1,67 @@
import React from 'react';
-import { RawIntlProvider } from 'react-intl';
import { Router, Switch, Route } from 'react-router';
import { createBrowserHistory } from 'history';
import { QueryClientProvider, QueryClient } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools';
import 'style/App.scss';
+import 'moment/locale/ar-ly';
+import 'moment/locale/es-us'
+import AppIntlLoader from './AppIntlLoader';
import PrivateRoute from 'components/Guards/PrivateRoute';
-import Authentication from 'components/Authentication';
-import DashboardPrivatePages from 'components/Dashboard/PrivatePages';
import GlobalErrors from 'containers/GlobalErrors/GlobalErrors';
-import intl from 'services/intl';
+import DashboardPrivatePages from 'components/Dashboard/PrivatePages';
+import Authentication from 'components/Authentication';
+// Query client config.
+const queryConfig = {
+ defaultOptions: {
+ queries: {
+ refetchOnWindowFocus: true,
+ staleTime: 30000,
+ },
+ },
+};
+
+// Global fetch query.
+function GlobalFetchQuery({
+ children
+}) {
+ window.localStorage.setItem('lang', 'en');
+ return children
+}
+
+/**
+ * Core application.
+ */
function App({ locale }) {
const history = createBrowserHistory();
- const queryConfig = {
- defaultOptions: {
- queries: {
- refetchOnWindowFocus: true,
- staleTime: 30000,
- },
- },
- };
+ // Query client.
const queryClient = new QueryClient(queryConfig);
return (
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
);
}
diff --git a/client/src/components/AppIntlLoader.js b/client/src/components/AppIntlLoader.js
new file mode 100644
index 000000000..fdd533325
--- /dev/null
+++ b/client/src/components/AppIntlLoader.js
@@ -0,0 +1,95 @@
+import React from 'react';
+import moment from 'moment';
+import { setLocale } from 'yup';
+import intl from 'react-intl-universal';
+import { find } from 'lodash';
+import rtlDetect from 'rtl-detect';
+import DashboardLoadingIndicator from 'components/Dashboard/DashboardLoadingIndicator';
+
+const SUPPORTED_LOCALES = [
+ { name: 'English', value: 'en' },
+ { name: 'العربية', value: 'ar-ly' },
+];
+
+/**
+ * Retrieve the current local.
+ */
+function getCurrentLocal() {
+ let currentLocale = intl.determineLocale({
+ urlLocaleKey: 'lang',
+ cookieLocaleKey: 'lang',
+ localStorageLocaleKey: 'lang',
+ });
+ if (!find(SUPPORTED_LOCALES, { value: currentLocale })) {
+ currentLocale = 'en';
+ }
+ return currentLocale;
+}
+
+/**
+ * Loads the localization data of the given locale.
+ */
+function loadLocales(currentLocale) {
+ return import(`../lang/${currentLocale}/index.json`);
+}
+
+function loadYupLocales(currentLocale) {
+ return import(`../lang/${currentLocale}/locale`);
+}
+
+/**
+ * Modifies the html document direction to RTl if it was rtl-language.
+ */
+function useDocumentDirectionModifier(locale) {
+ React.useEffect(() => {
+ const isRTL = rtlDetect.isRtlLang(locale);
+
+ if (isRTL) {
+ const htmlDocument = document.querySelector('html');
+ htmlDocument.setAttribute('dir', 'rtl');
+ htmlDocument.setAttribute('lang', locale);
+ }
+ }, []);
+}
+
+/**
+ * Application Intl loader.
+ */
+export default function AppIntlLoader({ children }) {
+ const [isLoading, setIsLoading] = React.useState(true);
+ const currentLocale = getCurrentLocal();
+
+ // Modifies the html document direction
+ useDocumentDirectionModifier(currentLocale);
+
+ React.useEffect(() => {
+ // Lodas the locales data file.
+ loadLocales(currentLocale)
+ .then((results) => {
+ return intl.init({
+ currentLocale,
+ locales: {
+ [currentLocale]: results,
+ },
+ });
+ })
+ .then(() => {
+ moment.locale(currentLocale);
+ setIsLoading(false);
+ });
+ }, [currentLocale, setIsLoading]);
+
+ React.useEffect(() => {
+ loadYupLocales(currentLocale)
+ .then(({ locale }) => {
+ setLocale(locale);
+ })
+ .then(() => {});
+ }, [currentLocale]);
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/client/src/components/Authentication.js b/client/src/components/Authentication.js
index 9eb01c6c1..8bbb29515 100644
--- a/client/src/components/Authentication.js
+++ b/client/src/components/Authentication.js
@@ -3,7 +3,7 @@ import { Redirect, Route, Switch, Link, useLocation } from 'react-router-dom';
import BodyClassName from 'react-body-classname';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import authenticationRoutes from 'routes/authentication';
-import { FormattedMessage as T } from 'react-intl';
+import { FormattedMessage as T } from 'components';
import Icon from 'components/Icon';
import { useIsAuthenticated } from 'hooks/state';
@@ -32,6 +32,7 @@ export default function AuthenticationWrapper({ ...rest }) {
>
+
diff --git a/client/src/components/CategoriesSelectList.js b/client/src/components/CategoriesSelectList.js
index 1345d1f6c..6cd4f83c5 100644
--- a/client/src/components/CategoriesSelectList.js
+++ b/client/src/components/CategoriesSelectList.js
@@ -1,5 +1,5 @@
import React, { useCallback } from 'react';
-import { FormattedMessage as T } from 'react-intl';
+import { FormattedMessage as T } from 'components';
import { ListSelect } from 'components';
import { MenuItem } from '@blueprintjs/core';
import { saveInvoke } from 'utils';
diff --git a/client/src/components/ContactSelecetList.js b/client/src/components/ContactSelecetList.js
index d19d4943c..02f301fbe 100644
--- a/client/src/components/ContactSelecetList.js
+++ b/client/src/components/ContactSelecetList.js
@@ -1,5 +1,7 @@
import React, { useCallback, useState, useEffect, useMemo } from 'react';
-import { FormattedMessage as T } from 'react-intl';
+import { FormattedMessage as T } from 'components';
+import intl from 'react-intl-universal';
+
import { MenuItem, Button } from '@blueprintjs/core';
import { Select } from '@blueprintjs/select';
import classNames from 'classnames';
@@ -14,7 +16,7 @@ export default function ContactSelecetList({
onContactSelected,
popoverFill = false,
disabled = false,
- buttonProps
+ buttonProps,
}) {
const contacts = useMemo(
() =>
@@ -79,7 +81,7 @@ export default function ContactSelecetList({
return (
}
+ noResults={
} />}
itemRenderer={handleContactRenderer}
itemPredicate={filterContacts}
filterable={true}
@@ -89,6 +91,9 @@ export default function ContactSelecetList({
className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, {
[CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill,
})}
+ inputProps={{
+ placeholder: intl.get('filter_')
+ }}
>