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

[WIP] [Locale] Intl C4R #741

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/react-ui/__tests__/widgets/BarWidgetUI.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('BarWidgetUI', () => {

test('all selected', () => {
render(<Widget />);
expect(screen.getByText(/All selected/)).toBeInTheDocument();
expect(screen.getByText(/All/)).toBeInTheDocument();
});

test('renders with stacked false', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ const POLYGON_ICON_ID = 'polygon-icon';
const RECTANGLE_ICON_ID = 'rectangle-icon';

const FEATURE_SELECTION_MODES = [
{ id: 'polygon', label: 'polygon', icon: <PolygonIcon id={POLYGON_ICON_ID} /> },
{ id: 'rectangle', label: 'rectangle', icon: <RectangleIcon id={RECTANGLE_ICON_ID} /> }
{ id: 'drawPolygonMode', label: 'polygon', icon: <PolygonIcon id={POLYGON_ICON_ID} /> },
{
id: 'drawRectangleMode',
label: 'rectangle',
icon: <RectangleIcon id={RECTANGLE_ICON_ID} />
}
];

const EDIT_MODES = [{ id: 'edit', label: 'Edit mask', icon: <CursorIcon /> }];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('HistogramWidgetUI', () => {

test('all selected', () => {
render(<Widget />);
expect(screen.getByText(/All selected/)).toBeInTheDocument();
expect(screen.getByText(/All/)).toBeInTheDocument();
});

test('re-render with different data', () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/react-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@babel/preset-env": "^7.13.9",
"@babel/preset-react": "^7.12.13",
"@etchteam/storybook-addon-status": "^4.2.2",
"@formatjs/intl-localematcher": "^0.4.0",
"@reduxjs/toolkit": "^1.5.0",
"@storybook/addon-actions": "^6.5.12",
"@storybook/addon-essentials": "^6.5.12",
Expand Down Expand Up @@ -74,6 +75,7 @@
"firebase-tools": "^8.17.0",
"jest": "^26.6.3",
"react-redux": "^7.2.2",
"react-intl": "^5.18.5",
"storybook-addon-designs": "^6.3.1",
"webpack": "^5.24.2",
"webpack-cli": "^4.5.0"
Expand All @@ -92,6 +94,7 @@
"echarts-for-react": "^3.0.2",
"react": "17.x || 18.x",
"react-dom": "17.x || 18.x",
"react-intl": "5.x || 6.x",
"styled-components": "^5.2.3"
}
}
35 changes: 35 additions & 0 deletions packages/react-ui/src/hooks/useImperativeIntl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { createIntl, createIntlCache } from 'react-intl';
import { messages } from '../localization';
import { useMemo } from 'react';
import { match } from '@formatjs/intl-localematcher';
import { flattenMessages } from '../localization/localeUtils';

export const DEFAULT_LOCALE = 'en';
const cache = createIntlCache();

export default function useImperativeIntl(intlConfig) {
return useMemo(() => {
const locale = intlConfig?.locale || DEFAULT_LOCALE;
const messagesLocale = findMatchingMessagesLocale(locale);
const intMessages = intlConfig?.messages
? flattenMessages(intlConfig?.messages)
: messages[messagesLocale];

return (
createIntl(
{
locale,
messages: intMessages
},
cache
),
[locale, intMessages]
);
}, [intlConfig]);
}

// AUX
function findMatchingMessagesLocale(locale) {
const localeMatcher = match([locale], Object.keys(messages), DEFAULT_LOCALE);
return localeMatcher ? localeMatcher : DEFAULT_LOCALE;
}
71 changes: 71 additions & 0 deletions packages/react-ui/src/localization/en.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const locales = {
c4r: {
widgets: {
category: {
apply: 'Apply',
unlock: 'Unlock',
lock: 'Lock',
clear: 'Clear',
noResults: 'No results',
noResultsMessage: 'Your search "{searchValue}" didn\'t match with any value.',
cancel: 'Cancel',
searchInfo: 'Search in {elements} elements',
selectedItems: '{items} selected',
all: 'All',
search: 'Search'
},
bar: {
all: 'All',
selectedItems: '{items} selected',
clear: 'Clear'
},
histogram: {
all: 'All',
selectedItems: '{items} selected',
clear: 'Clear'
},
legend: {
by: 'By {attr}',
layerOptions: 'Layer options',
hide: 'Hide',
show: 'Show',
layer: 'layer',
opacity: 'Opacity',
hideLayer: 'Hide layer',
showLayer: 'Show layer'
},
range: {
clear: 'Clear',
minLimit: 'min limit',
maxLimit: 'max limit',
minValue: 'min value',
maxValue: 'max value'
},
table: {
rowsPerPage: 'Rows per page',
of: '{from}–{to} of {count}',
moreThan: 'more than'
},
timeSeries: {
speed: 'Speed',
clear: 'Clear'
},
featureSelection: {
drawPolygonMode: 'Polygon',
drawRectangleMode: 'Rectangle',
drawPolygonByDraggingMode: 'Lasso tool',
drawCircleFromCenterMode: 'Circle',
edit: 'Edit mask',
clickToEdit: 'Click on the mask to edit it',
clickToCreate: 'Click on the map to create a mask',
selectTool: 'Select a {label}',
selectMode: 'Select a mode',
chooseSelectionMode: 'Choose a selection mode',
applyMask: 'Apply mask',
clearMask: 'Clear mask'
}
}
}
};

export default locales;
9 changes: 9 additions & 0 deletions packages/react-ui/src/localization/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import en from './en';

import { flattenMessages } from './localeUtils';

const enFlat = flattenMessages(en);

export const messages = {
en: enFlat
};
12 changes: 12 additions & 0 deletions packages/react-ui/src/localization/localeUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export function flattenMessages(nestedMessages, prefix = '') {
return Object.keys(nestedMessages).reduce((messages, key) => {
const value = nestedMessages[key];
const prefixedKey = prefix ? `${prefix}.${key}` : key;
if (typeof value === 'string') {
messages[prefixedKey] = value;
} else {
Object.assign(messages, flattenMessages(value, prefixedKey));
}
return messages;
}, {});
}
6 changes: 6 additions & 0 deletions packages/react-ui/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type CategoryWidgetUI = {
onSelectedCategoriesChange?: Function;
order?: 'ranking' | 'fixed';
isLoading?: boolean;
intlConfig?: object;
};

export type FormulaWidgetUIData =
Expand All @@ -52,6 +53,7 @@ export type HistogramWidgetUI = {
onSelectedBarsChange?: Function;
height?: number;
isLoading?: boolean;
intlConfig?: object;
};

export type BarWidgetUI = {
Expand All @@ -71,6 +73,7 @@ export type BarWidgetUI = {
filterable?: boolean;
animation?: boolean;
isLoading?: boolean;
intlConfig?: object;
};

export type PieWidgetUIData = { name: string; value: number }[];
Expand Down Expand Up @@ -109,6 +112,7 @@ export type LegendWidgetUI = {
customLegendTypes?: Record<string, Function>;
layers?: LegendWidgetUIData[];
collapsed?: boolean;
intlConfig?: object;
onChangeCollapsed?: (collapsed: boolean) => void;
onChangeVisibility?: (args: { id: string; visible: boolean }) => void;
onChangeOpacity?: (args: { id: string; visible: boolean }) => void;
Expand Down Expand Up @@ -145,6 +149,7 @@ export type TimeSeriesWidgetUI = {
onTimeWindowUpdate?: Function;
showControls?: boolean;
isLoading?: boolean;
intlConfig?: object;
};

export type NoDataAlert = {
Expand All @@ -170,6 +175,7 @@ export type FeatureSelectionWidgetUI = {
tooltipPlacement?: 'bottom' | 'left' | 'right' | 'top';
size?: 'small' | 'medium';
chipLabel?: string;
intlConfig?: object;
};

export type FeatureSelectionUIDropdown = {
Expand Down
18 changes: 14 additions & 4 deletions packages/react-ui/src/widgets/BarWidgetUI/BarWidgetUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import detectTouchScreen from '../utils/detectTouchScreen';
import { processFormatterRes } from '../utils/formatterUtils';
import Typography from '../../components/atoms/Typography';
import BarSkeleton from './BarSkeleton';
import useImperativeIntl from '../../hooks/useImperativeIntl';

const IS_TOUCH_SCREEN = detectTouchScreen();

Expand Down Expand Up @@ -44,13 +45,16 @@ function BarWidgetUI(props) {
height,
filterable,
animation,
isLoading
isLoading,
intlConfig
} = useProcessedProps(props);

const isMultiSeries = series.length > 1;

const theme = useTheme();

const intl = useImperativeIntl(intlConfig);

// Tooltip
const tooltipOptions = useMemo(
() => ({
Expand Down Expand Up @@ -296,11 +300,16 @@ function BarWidgetUI(props) {
{onSelectedBarsChange && (
<OptionsSelectedBar container>
<Typography variant='caption'>
{selectedBars?.length || 'All'} selected
{selectedBars.length > 0
? intl.formatMessage(
{ id: 'c4r.widgets.bar.selectedItems' },
{ items: selectedBars.length }
)
: intl.formatMessage({ id: 'c4r.widgets.bar.all' })}
</Typography>
{selectedBars && selectedBars.length > 0 && (
<SelectAllButton onClick={() => clearBars()} underline='hover'>
Clear
{intl.formatMessage({ id: 'c4r.widgets.bar.clear' })}
</SelectAllButton>
)}
</OptionsSelectedBar>
Expand Down Expand Up @@ -357,7 +366,8 @@ BarWidgetUI.propTypes = {
height: numberOrString,
filterable: PropTypes.bool,
animation: PropTypes.bool,
isLoading: PropTypes.bool
isLoading: PropTypes.bool,
intlConfig: PropTypes.object
};

export default BarWidgetUI;
Expand Down
Loading