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

Feature/pxweb2 99 formatting numbers based on language #65

Merged
Merged
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
10 changes: 10 additions & 0 deletions apps/pxweb2/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,15 @@
"date": {
"simple_date": "{{value, datetime}}",
"simple_date_with_time": "{{value, datetime(year: 'numeric'; month: 'numeric'; day: 'numeric'; hour: 'numeric'; minute: 'numeric')}}"
},
"number": {
"simple_number": "{{value, pxNumber}}",
"simple_number_with_zero_decimal": "{{value, pxNumber(minimumFractionDigits: 0; maximumFractionDigits: 0;)}}",
"simple_number_with_one_decimal": "{{value, pxNumber(minimumFractionDigits: 1; maximumFractionDigits: 1;)}}",
"simple_number_with_two_decimals": "{{value, pxNumber(minimumFractionDigits: 2; maximumFractionDigits: 2;)}}",
"simple_number_with_three_decimals": "{{value, pxNumber(minimumFractionDigits: 3; maximumFractionDigits: 3;)}}",
"simple_number_with_four_decimals": "{{value, pxNumber(minimumFractionDigits: 4; maximumFractionDigits: 4;)}}",
"simple_number_with_five_decimals": "{{value, pxNumber(minimumFractionDigits: 5; maximumFractionDigits: 5;)}}",
"simple_number_with_default_formatter": "{{value, number(minimumFractionDigits: 5; maximumFractionDigits: 5; roundingMode: 'halfExpand')}}"
}
}
10 changes: 10 additions & 0 deletions apps/pxweb2/src/@types/resources.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ interface Resources {
"date": {
"simple_date": "{{value, datetime}}",
"simple_date_with_time": "{{value, datetime(year: 'numeric'; month: 'numeric'; day: 'numeric'; hour: 'numeric'; minute: 'numeric')}}"
},
"number": {
"simple_number": "{{value, pxNumber}}",
"simple_number_with_zero_decimal": "{{value, pxNumber(minimumFractionDigits: 0; maximumFractionDigits: 0;)}}",
"simple_number_with_one_decimal": "{{value, pxNumber(minimumFractionDigits: 1; maximumFractionDigits: 1;)}}",
"simple_number_with_two_decimals": "{{value, pxNumber(minimumFractionDigits: 2; maximumFractionDigits: 2;)}}",
"simple_number_with_three_decimals": "{{value, pxNumber(minimumFractionDigits: 3; maximumFractionDigits: 3;)}}",
"simple_number_with_four_decimals": "{{value, pxNumber(minimumFractionDigits: 4; maximumFractionDigits: 4;)}}",
"simple_number_with_five_decimals": "{{value, pxNumber(minimumFractionDigits: 5; maximumFractionDigits: 5;)}}",
"simple_number_with_default_formatter": "{{value, number(minimumFractionDigits: 5; maximumFractionDigits: 5; roundingMode: 'halfExpand')}}"
}
}
}
Expand Down
107 changes: 103 additions & 4 deletions apps/pxweb2/src/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import {
BodyLong,
Ingress,
Label,
Tag
Tag,
} from '@pxweb2/pxweb2-ui';
import useLocalizeDocumentAttributes from '../i18n/useLocalizeDocumentAttributes';
import { NumberFormatter } from '../i18n/formatters';

function test(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
event.preventDefault();
Expand All @@ -29,6 +30,10 @@ export function App() {
ar: { title: 'العربية' },
};

const customRoundingMode = 'halfExpand';
const customMinDecimals = 2;
const customMaxDecimals = 4;

useLocalizeDocumentAttributes();

return (
Expand Down Expand Up @@ -64,9 +69,15 @@ export function App() {
her every day, every week, every month, every year. She never saw a
wolf, no even a little fox.
</BodyLong>
<Tag size="medium" variant="info">Mandatory</Tag>&nbsp;
<Tag size="medium" variant="info" type='border'>Mandatory</Tag>&nbsp;
<br />
<Tag size="medium" variant="info">
Mandatory
</Tag>
&nbsp;
<Tag size="medium" variant="info" type="border">
Mandatory
</Tag>
&nbsp;
<br />
<form id="form1" onSubmit={testSubmit}>
<Label htmlFor="fname" textcolor="subtle">
First name:
Expand Down Expand Up @@ -122,6 +133,94 @@ export function App() {
"Welcome to the <b>app</b> for PxWeb 2.0!"
</Trans>
</p>
<p>Test custom number formatter: {NumberFormatter(2000.6666666, 2)}</p>
<p>
Simple number:{' '}
{t('number.simple_number', {
value: 2000.066666666,
})}
</p>
<p>
Simple number with custom decimals:{' '}
{t('number.simple_number', {
value: 2000.00007,
minimumFractionDigits: customMinDecimals,
maximumFractionDigits: customMaxDecimals,
roundingMode: customRoundingMode,
})}
</p>
<p>
Simple number with 0 decimals:{' '}
{t('number.simple_number_with_zero_decimal', {
value: 2000.044444444,
})}
</p>
<p>
Simple number with 1 decimal:{' '}
{t('number.simple_number_with_one_decimal', {
value: 2000.044444444,
})}
</p>
<p>
Simple number with 2 decimals:{' '}
{t('number.simple_number_with_two_decimals', {
value: 2000.044444444,
})}
</p>
<p>
Simple number with 3 decimals:{' '}
{t('number.simple_number_with_three_decimals', {
value: 2000.044444444,
})}
</p>
<p>
Simple number with 4 decimals:{' '}
{t('number.simple_number_with_four_decimals', {
value: 2000.044444444,
})}
</p>
<p>
Simple number with 5 decimals:{' '}
{t('number.simple_number_with_five_decimals', {
value: 2000.044447444,
})}
</p>
<p>
Round test:{' '}
{t('number.simple_number_with_one_decimal', {
value: 2.23,
})}
</p>
<p>
{' '}
{t('number.simple_number_with_one_decimal', {
value: 2.25,
})}
</p>
<p>
{' '}
{t('number.simple_number_with_one_decimal', {
value: 2.28,
})}
</p>
<p>
{' '}
{t('number.simple_number_with_one_decimal', {
value: -2.23,
})}
</p>
<p>
{' '}
{t('number.simple_number_with_one_decimal', {
value: -2.25,
})}
</p>
<p>
{' '}
{t('number.simple_number_with_one_decimal', {
value: -2.28,
})}
</p>
</>
);
}
Expand Down
6 changes: 5 additions & 1 deletion apps/pxweb2/src/i18n/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import i18n from 'i18next';
import HttpApi from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next';

import { pxNumber } from './formatters';

export const defaultNS = 'translation';

i18n
Expand All @@ -10,7 +12,7 @@ i18n
.init({
backend: {
requestOptions: {
// Do not cache the response from the server. This is needed because site administrators
// Do not cache the response from the server. This is needed because site administrators
// may want to change the translations without having to wait for the cache to expire.
cache: 'no-store',
},
Expand All @@ -27,4 +29,6 @@ i18n
},
});

i18n.services.formatter?.add('pxNumber', pxNumber);

export default i18n;
71 changes: 71 additions & 0 deletions apps/pxweb2/src/i18n/formatters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useTranslation } from 'react-i18next';

const customRoundingMode = 'halfExpand';

/*
* Custom number formatter
* @param value - number to format
* @param decimals - number of decimals, or minimum number of decimals if maxDecimals is set
* @param maxDecimals - maximum number of decimals
* @returns formatted number
*
* Example usage:
* NumberFormatter(2000.6666666, 2)
* NumberFormatter(2000.6666666, 2, 4)
*
* This can be used to format a number, and could be easier to use than
* the built-in number formatter in i18next when dealing with a large
* amount of numbers.
*
* It also set the rounding mode to the value of the variable customRoundingMode,
* which should be read from a configuration file.
*/
export function NumberFormatter(
value: number,
decimals: number,
maxDecimals?: number
): string {
const { i18n } = useTranslation();
const max = maxDecimals ? maxDecimals : decimals;

const nf = new Intl.NumberFormat(i18n.resolvedLanguage, {
minimumFractionDigits: decimals,
maximumFractionDigits: max,

// Missing in the type definition in typescript, but a commit was merged to fix this
// in end of Feb, early March. So it should be available soon.
// @ts-expect-error Remove when typescript is updated with fix
roundingMode: customRoundingMode,
});

return nf.format(value);
}

/*
* Custom number formatter
* @param value - number to format
* @param lng - language
* @param options - number format options
* @returns formatted number
*
* This custom formatter is to be used in translation files, in the same way as the built-in
* number formatter in i18next. But it also sets the rounding mode to the value of the variable
* customRoundingMode, which should be read from a configuration file.
*/
export function pxNumber(
value: number,
lng: string | undefined,
options?: Intl.NumberFormatOptions
): string {
if (!options) {
return new Intl.NumberFormat(lng, {
// @ts-expect-error See earlier comment. Remove when typescript is updated with fix
roundingMode: customRoundingMode,
}).format(value);
}

// @ts-expect-error See earlier comment. Remove when typescript is updated with fix
options.roundingMode = customRoundingMode;

return new Intl.NumberFormat(lng, options).format(value);
}