WIP: Arabic localization.|

This commit is contained in:
a.bouhuolia
2021-06-10 12:51:00 +02:00
parent 4fc7c37260
commit 1ea32884c2
465 changed files with 3299 additions and 2109 deletions

View File

@@ -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({});

View File

@@ -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';

View File

@@ -5,7 +5,7 @@ import { Suggest } from '@blueprintjs/select';
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';
/**

View File

@@ -1,5 +1,4 @@
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';
@@ -7,47 +6,60 @@ import { ReactQueryDevtools } from 'react-query/devtools';
import 'style/App.scss';
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 (
<RawIntlProvider value={intl}>
<QueryClientProvider client={queryClient}>
<div className="App">
<Router history={history}>
<Switch>
<Route path={'/auth'}>
<Authentication />
</Route>
<QueryClientProvider client={queryClient}>
<GlobalFetchQuery>
<AppIntlLoader>
<div className="App">
<Router history={history}>
<Switch>
<Route path={'/auth'} component={Authentication} />
<Route path={'/'}>
<PrivateRoute component={DashboardPrivatePages} />
</Route>
</Switch>
</Router>
<Route path={'/'}>
<PrivateRoute component={DashboardPrivatePages} />
</Route>
</Switch>
</Router>
<GlobalErrors />
</div>
</AppIntlLoader>
</GlobalFetchQuery>
<GlobalErrors />
</div>
<ReactQueryDevtools initialIsOpen />
</QueryClientProvider>
</RawIntlProvider>
<ReactQueryDevtools initialIsOpen />
</QueryClientProvider>
);
}

View File

@@ -0,0 +1,80 @@
import React from 'react';
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' }
];
/**
* 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`);
}
/**
* 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(() => {
setIsLoading(false);
});
}, [currentLocale, setIsLoading]);
return (
<DashboardLoadingIndicator isLoading={isLoading}>
{children}
</DashboardLoadingIndicator>
);
}

View File

@@ -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 }) {
>
<T id={'go_to_bigcapital_com'} />
</a>
<div class="authentication-page__form-wrapper">
<div class="authentication-insider">
<div className={'authentication-insider__logo-section'}>

View File

@@ -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';

View File

@@ -1,5 +1,5 @@
import React, { useCallback, useState, useEffect, useMemo } from 'react';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
import { MenuItem, Button } from '@blueprintjs/core';
import { Select } from '@blueprintjs/select';
import classNames from 'classnames';

View File

@@ -2,7 +2,7 @@ import React, { useMemo, useCallback, useState } from 'react';
import { MenuItem, Button } from '@blueprintjs/core';
import { omit } from 'lodash';
import MultiSelect from 'components/MultiSelect';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
export default function ContactsMultiSelect({
contacts,

View File

@@ -2,16 +2,16 @@ import React, { useCallback, useState, useEffect, useMemo } from 'react';
import { MenuItem } from '@blueprintjs/core';
import { Suggest } from '@blueprintjs/select';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import { formatMessage } from 'services/intl';
import intl from 'react-intl-universal';
export default function ContactsSuggestField({
contactsList,
initialContactId,
selectedContactId,
defaultTextSelect = formatMessage({id:'select_contact'}),
defaultTextSelect = intl.get('select_contact'),
onContactSelected,
selectedContactType = [],

View File

@@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useState } from 'react';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
import { CLASSES } from 'common/classes';
import classNames from 'classnames';
import { MenuItem, Button } from '@blueprintjs/core';

View File

@@ -30,7 +30,7 @@ export default function Dashboard() {
<Route path="/">
<DashboardSplitPane>
<Sidebar />
<DashboardContent />
<DashboardContent />
</DashboardSplitPane>
</Route>
</Switch>

View File

@@ -9,7 +9,7 @@ import {
PopoverInteractionKind,
Position,
} from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
import { Icon } from 'components';
/**

View File

@@ -1,9 +1,9 @@
import React from 'react';
import withBreadcrumbs from 'react-router-breadcrumbs-hoc';
import { useHistory } from 'react-router-dom';
import routes from 'routes/dashboard';
import { getDashboardRoutes } from 'routes/dashboard';
import { If, Icon } from 'components';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
import withDashboard from 'containers/Dashboard/withDashboard';
import { compose } from 'utils';
@@ -32,7 +32,7 @@ function DashboardBackLink({ dashboardBackLink, breadcrumbs }) {
}
export default compose(
withBreadcrumbs(routes),
withBreadcrumbs([]),
withDashboard(({ dashboardBackLink }) => ({
dashboardBackLink,
})),

View File

@@ -6,7 +6,7 @@ import {
Boundary,
} from '@blueprintjs/core';
import withBreadcrumbs from 'react-router-breadcrumbs-hoc';
import routes from 'routes/dashboard';
import { getDashboardRoutes } from 'routes/dashboard';
import { useHistory } from 'react-router-dom';
function DashboardBreadcrumbs({ breadcrumbs }){
@@ -31,4 +31,4 @@ function DashboardBreadcrumbs({ breadcrumbs }){
)
}
export default withBreadcrumbs(routes)(DashboardBreadcrumbs)
export default withBreadcrumbs([])(DashboardBreadcrumbs)

View File

@@ -1,12 +1,15 @@
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import routes from 'routes/dashboard';
import { getDashboardRoutes } from 'routes/dashboard';
import DashboardPage from './DashboardPage';
/**
* Dashboard content route.
*/
export default function DashboardContentRoute() {
const routes = getDashboardRoutes();
return (
<Route pathname="/">
<Switch>

View File

@@ -9,7 +9,7 @@ import {
Tooltip,
Position,
} from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
import DashboardTopbarUser from 'components/Dashboard/TopbarUser';
import DashboardBreadcrumbs from 'components/Dashboard/DashboardBreadcrumbs';

View File

@@ -1,5 +1,5 @@
import React, { useRef, useState, useEffect } from 'react';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
import PropTypes from 'prop-types';
import { Button, Tabs, Tab, Tooltip, Position } from '@blueprintjs/core';
import { useHistory } from 'react-router';

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useHistory } from 'react-router-dom';
import routes from 'routes/dashboard';
import { getDashboardRoutes } from 'routes/dashboard';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import { compose } from 'utils';
@@ -10,6 +10,7 @@ function GlobalHotkeys({
toggleSidebarExpend,
}) {
const history = useHistory();
const routes = getDashboardRoutes();
const globalHotkeys = routes
.filter(({ hotkey }) => hotkey)

View File

@@ -8,7 +8,7 @@ import {
Popover,
Position,
} from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
import { firstLettersArgs } from 'utils';
import { useAuthActions, useAuthUser } from 'hooks/state';

View File

@@ -10,7 +10,7 @@ import {
useAsyncDebounce,
} from 'react-table';
import { useSticky } from 'react-table-sticky';
import { formatMessage } from 'services/intl';
import intl from 'react-intl-universal';
import { useUpdateEffect } from 'hooks';
import { saveInvoke } from 'utils';
@@ -209,6 +209,6 @@ DataTable.defaultProps = {
TablePaginationRenderer: TablePagination,
TableNoResultsRowRenderer: TableNoResultsRow,
noResults: formatMessage({ id: 'there_is_no_results_in_the_table' }),
noResults: '',
payload: {},
};

View File

@@ -4,7 +4,7 @@ import ItemsSuggestField from 'components/ItemsSuggestField';
import classNames from 'classnames';
import { FormGroup, Classes, Intent } from '@blueprintjs/core';
import { formatMessage } from 'services/intl';
import intl from 'react-intl-universal';
import { useCellAutoFocus } from 'hooks';
@@ -41,7 +41,7 @@ export default function ItemsListCell({
purchasable={filterPurchasable}
inputProps={{
inputRef: (ref) => (fieldRef.current = ref),
placeholder: formatMessage({ id: 'enter_an_item' }),
placeholder: intl.get('enter_an_item'),
}}
openOnKeyDown={true}
blurOnSelectClose={false}

View File

@@ -46,6 +46,8 @@ export default function TableCell({
);
}
const isRTL = true;
return (
<div
{...cell.getCellProps({
@@ -62,7 +64,7 @@ export default function TableCell({
'cell-inner',
)}
style={{
paddingLeft:
[isRTL ? 'paddingRight' : 'paddingLeft']:
isExpandColumn && expandable
? `${depth * expandColumnSpace}rem`
: '',

View File

@@ -1,4 +1,5 @@
import React, { useContext } from 'react';
import intl from 'react-intl-universal';
import TableContext from './TableContext';
/**
@@ -6,12 +7,15 @@ import TableContext from './TableContext';
*/
export default function TableNoResultsRow() {
const {
props: { noResults }
props: { noResults },
} = useContext(TableContext);
const noResultText =
noResults || intl.get('there_is_no_results_in_the_table');
return (
<div className={'tr no-results'}>
<div class="td">{ noResults }</div>
<div class="td">{noResultText}</div>
</div>
);
}
}

View File

@@ -2,7 +2,7 @@ import React, { useState, useCallback, useEffect } from 'react';
import { useDropzone } from 'react-dropzone';
import classNames from 'classnames';
import Icon from 'components/Icon';
import { formatMessage } from 'services/intl';
import intl from 'react-intl-universal';
// const initialFile: {
// file: ?File,
@@ -12,7 +12,7 @@ import { formatMessage } from 'services/intl';
// };
export default function Dropzone({
text = formatMessage({ id: 'drag_drop_files_here_or_click_here' }),
text = intl.get('drag_drop_files_here_or_click_here'),
onDrop,
initialFiles = [],
onDeleteFile,

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
import { Classes, Icon, H4, Button } from '@blueprintjs/core';
import withDrawerActions from 'containers/Drawer/withDrawerActions';

View File

@@ -1,17 +1,15 @@
import React, { useMemo } from 'react';
import { HTMLSelect, Classes } from '@blueprintjs/core';
import { useIntl } from 'react-intl';
import intl from 'react-intl-universal';
import { getConditionTypeCompatators } from './DynamicFilterCompatators';
export default function DynamicFilterCompatatorField({
dataType,
...restProps
}) {
const { formatMessage } = useIntl();
const options = useMemo(
() => getConditionTypeCompatators(dataType).map(comp => ({
value: comp.value, label: formatMessage({ id: comp.label_id }),
value: comp.value, label: intl.get(comp.label_id),
})),
[dataType]
);

View File

@@ -10,7 +10,8 @@ import { connect } from 'react-redux';
import { useQuery } from 'react-query';
import { DateInput } from '@blueprintjs/datetime';
import classNames from 'classnames';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { FormattedMessage as T } from 'components';
import intl from 'react-intl-universal';
import { debounce } from 'lodash';
import moment from 'moment';
@@ -45,7 +46,7 @@ function DynamicFilterValueField({
onChange,
inputDebounceWait = 250,
}) {
const { formatMessage } = useIntl();
const [localValue, setLocalValue] = useState();
// Makes `localValue` controlled mode from `value`.
@@ -185,7 +186,7 @@ function DynamicFilterValueField({
<Choose.Otherwise>
<InputGroup
placeholder={formatMessage({ id: 'value' })}
placeholder={intl.get('value')}
onChange={handleInputChange}
value={localValue}
/>

View File

@@ -12,7 +12,8 @@ import { isEqual, last } from 'lodash';
import { usePrevious } from 'react-use';
import Icon from 'components/Icon';
import { checkRequiredProperties, uniqueMultiProps } from 'utils';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { FormattedMessage as T } from 'components';
import intl from 'react-intl-universal';
import {
DynamicFilterValueField,
DynamicFilterCompatatorField,
@@ -41,7 +42,7 @@ export default function FilterDropdown({
initialCondition,
initialConditions,
}) {
const { formatMessage } = useIntl();
// Fields key -> metadata table.
const fieldsKeyMapped = useMemo(() =>
@@ -51,10 +52,10 @@ export default function FilterDropdown({
// Conditions options.
const conditionalsOptions = useMemo(
() => [
{ value: '&&', label: formatMessage({ id: 'and' }) },
{ value: '||', label: formatMessage({ id: 'or' }) },
{ value: '&&', label: intl.get('and') },
{ value: '||', label: intl.get('or') },
],
[formatMessage],
[],
);
// Resources fileds options for fields options.
const resourceFieldsOptions = useMemo(
@@ -91,7 +92,7 @@ export default function FilterDropdown({
if (values.conditions.length >= 12) {
limitToast = Toaster.show(
{
message: formatMessage({ id: 'you_reached_conditions_limit' }),
message: intl.get('you_reached_conditions_limit'),
intent: Intent.WARNING,
},
limitToast,
@@ -102,7 +103,7 @@ export default function FilterDropdown({
defaultFilterCondition
]);
}
}, [values, setFieldValue, formatMessage, defaultFilterCondition]);
}, [values, setFieldValue, defaultFilterCondition]);
// Filtered conditions that filters conditions that don't contain atleast
// on required fields or fileds keys that not exists.

View File

@@ -1,7 +1,8 @@
import React, { useMemo, useCallback } from 'react';
import moment from 'moment';
import classnames from 'classnames';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { FormattedMessage as T } from 'components';
import intl from 'react-intl-universal';
import 'style/pages/FinancialStatements/FinancialSheet.scss';
@@ -23,7 +24,7 @@ export default function FinancialSheet({
fullWidth = false,
currentDate = true,
}) {
const { formatMessage } = useIntl();
const format = 'DD MMMM YYYY';
const formattedFromDate = useMemo(() => moment(fromDate).format(format), [
fromDate,
@@ -38,10 +39,10 @@ export default function FinancialSheet({
const nameModifer = name ? `financial-sheet--${name}` : '';
const methodsLabels = useMemo(
() => ({
cash: formatMessage({ id: 'cash' }),
accrual: formatMessage({ id: 'accrual' }),
cash: intl.get('cash'),
accrual: intl.get('accrual'),
}),
[formatMessage],
[],
);
const getBasisLabel = useCallback((b) => methodsLabels[b], [methodsLabels]);
const basisLabel = useMemo(() => getBasisLabel(basis), [

View File

@@ -0,0 +1,9 @@
import intl from 'react-intl-universal';
export function FormattedMessage({ id }) {
return intl.get(id);
}
export function FormattedHTMLMessage({ ...args }) {
return intl.formatHTMLMessage({ ...args })
}

View File

@@ -5,7 +5,7 @@ import { CLASSES } from 'common/classes';
import { Suggest } from '@blueprintjs/select';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
export default function ItemsSuggestField({
items,

View File

@@ -1,7 +1,7 @@
import React, { useState, useMemo, useEffect } from 'react';
import { Button, MenuItem } from '@blueprintjs/core';
import { Select } from '@blueprintjs/select';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from './FormattedMessage';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';

View File

@@ -3,7 +3,7 @@ import { FastField, ErrorMessage } from 'formik';
import { FormGroup, Checkbox, Switch } from '@blueprintjs/core';
import { CLASSES } from 'common/classes';
import { ListSelect } from 'components';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
import { inputIntent } from 'utils';
import {
moneyFormat,

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { useFormikContext } from 'formik';
import { Button, Classes, Intent } from '@blueprintjs/core';
import classNames from 'classnames';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
/**
* Number format footer.

View File

@@ -1,7 +1,7 @@
import React, { useReducer, useEffect } from 'react';
import classNames from 'classnames';
import { Button, ButtonGroup, Intent, HTMLSelect } from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
import PropTypes from 'prop-types';
import { range } from 'lodash';
import { Icon } from 'components';

View File

@@ -1,7 +1,7 @@
import React, { useCallback } from 'react';
import { MenuItem } from '@blueprintjs/core';
import ListSelect from 'components/ListSelect';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
function PaymentReceiveListField({
invoices,

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
import { useHistory, useLocation } from 'react-router-dom';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T } from 'components';
import preferencesMenu from 'config/preferencesMenu';
import PreferencesSidebarContainer from './PreferencesSidebarContainer';

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { formatMessage } from 'services/intl';
import intl from 'react-intl-universal';
import { ListSelect } from 'components';
@@ -15,7 +15,7 @@ export default function SalutationList({ ...restProps }) {
items={items}
selectedItemProp={'key'}
textProp={'label'}
defaultText={formatMessage({ id: 'salutation' })}
defaultText={intl.get('salutation')}
filterable={false}
{...restProps}
/>

View File

@@ -3,6 +3,7 @@ import Money from './Money';
import Icon from './Icon';
import Choose from './Utils/Choose';
import For from './Utils/For';
import { FormattedMessage, FormattedHTMLMessage } from './FormattedMessage';
import ListSelect from './ListSelect';
import FinancialStatement from './FinancialStatement';
import DynamicFilterValueField from './DynamicFilter/DynamicFilterValueField';
@@ -58,14 +59,19 @@ import MaterialProgressBar from './MaterialProgressBar';
const Hint = FieldHint;
const T = FormattedMessage;
export {
If,
For,
Money,
Choose,
Icon,
FormattedMessage,
FormattedHTMLMessage,
T,
Money,
ListSelect,
FinancialStatement,
Choose,
DynamicFilterValueField,
DynamicFilterCompatatorField,
MODIFIER,