-
Notifications
You must be signed in to change notification settings - Fork 57
Internationalization
We use the Luxon library to format dates. However, we have different timezones in the app (organization, customer, lago UTC) implying to have timezone format. By default, the timezone of the app is set to the selected organization timezone so you don't have to deal with it and can just use the Luxon formatter.
import { DateTime } from 'luxon'
DateTime.fromISO("2022-04-13T14:40:26Z").toFormat('LLL. dd, yyyy')}
// --> 2022/04/13
Note : The default format in the app is LLL. dd, yyyy
If you need to use another timezone, you can use the following :
import { formatDateToTZ, getTimezoneConfig } from "~/core/timezone";
const { name, offset /* Can be usefull for displaying informations*/ } =
getTimezoneConfig(TZ_AMERICA_ARGENTINA_BUENOS_AIRES);
formatDateToTZ("2022-04-13T14:40:26Z", name);
There is also a component that allow to display the date with a tooltip with the same date displayed in different timezone :
import { TimezoneDate } from "~/components/TimezoneDate";
import { TimezoneEnum } from "~/generated/graphql";
const UseTimezone = (date: string, customerTimezone: TimezoneEnum) => {
<TimezoneDate
date={date}
customerTimezone={customerTimezone}
mainTimezone="organization"
/>;
};
You can use the intlFormatNumber
function from the ~/core/intlFormatNumber file.
import { intlFormatNumber } from "~/core/intlFormatNumber";
intlFormatNumber(432222.34, {
currencyDisplay: "code",
currency: "USD",
maximumFractionDigits: 2,
});
// --> USD 432,222.34
intlFormatNumber(432222, {
currencyDisplay: "code",
currency: "USD",
minimumFractionDigits: 3,
});
// --> USD 4,322.220 as default format is in cent
intlFormatNumber(0.1, {
minimumFractionDigits: 2,
style: "percent",
});
// --> 0.10%
Some currency have specificity (ex: allowing 3 decimals, or allowing 0 decimals).
Also, to avoid issues with float storages, amounts are stored on the API in the smalles unit possible (ex: cents for EUR, USD..., regular unit for the currency not accepting decimals).
To deal with this issue in app, several functions have been defined for you to use:
SerializeAmount - Use it to convert the amount value according to currency to be sent to the API
import { serializeAmount } from "~/core/serializers/serializeAmount";
serializeAmount(100, CurrencyEnum.Jpy); // --> 100 as JPY does not accepts decimals
serializeAmount(9.95, CurrencyEnum.Jpy); // --> 10 as JPY does not accepts decimals
serializeAmount(9.95, CurrencyEnum.Usd); // --> 995 as USD accepts 2 decimals
serializeAmount(100, CurrencyEnum.Bhd); // --> 100000 as BHD accepts 3 decimals
DeserializeAmount - Use it to convert an amount value received from the API to a readable unit according to the currency
import { deserializeAmount } from "~/core/serializers/serializeAmount";
deserializeAmount(100, CurrencyEnum.Jpy); // --> 100 as JPY does not accepts decimals and is stored in the JPY unit
deserializeAmount(9.95, CurrencyEnum.Jpy); // --> 10 as JPY does not accepts decimals
deserializeAmount(100, CurrencyEnum.Usd); // --> 1 as USD accepts 2 decimals and is stored in cents
deserializeAmount(0.1, CurrencyEnum.Bhd); // --> 100000 as BHD accepts 3 decimals
Amount Input
To be sure that accepted values match the currency constraints, we have a component AmountInput
that will add the mandatory constraints to the input
import { AmountInput, ComboBox } from "~/components/form";
const Form = () => {
return (
<AmountInput
name="name"
currency="USD"
label={translate("text_624453d52e945301380e49b6")}
value={valuePointer?.amount}
onChange={handleUpdate}
InputProps={{
endAdornment: (
<InputAdornment position="end">
{getCurrencySymbol(currency)}
</InputAdornment>
),
}}
/>
);
};
All the translations can be found in the base.json file. To use them you can use the translate function (code here) Note: The translations keys are generated through the ditto app. If you'd like to own and generate your own translations keys, you'll have to overide the base.json file locally.
type translate = (
key: string,
data?: Record<string, string | number | undefined | null>,
plural?: number
) => string;
Second argument of the translate function can be an array of data to use.
For each key, include {{key}}
in your translation string - which will be parsed to replace with the given value.
// in the translation file
{
"translation_key": "{{name}} has {{count}} cookies in her pocket."
}
// in your component
<Typography>
{translate("translation_key", {
name: Morgane,
count: 7,
})}
</Typography>
// output --> "Morgane has 7 cookies in her pocket."
{
// In app link
"translation_in_app_key_id": "Already have an account? <a data-text=\"Log in\" href=\"{{link}}\">-</a>",
// Link to another website
"translation_outter_key_id": "By signing up, you agree to our <a rel=\"external\" target=\"_blank\" href=\"https://www.getlago.com/terms-of-service\">Terms of Service</a> and <a rel=\"external\" target=\"_blank\" href=\"https://www.getlago.com/privacy-policy\">Privacy Policy</a>.\nAlready have an account? <a data-text=\"Log in\" href=\"{{link}}\">-</a>"
}
You will then be able to use this key as follow in your React component :
import { useInternationalization } from '~/hooks/useInternationalization'
const MyComponent = () => {
const { translate } = useInternationalization()
return (
<Typography html={translate('translation_key_id', { link: '/login' })} />
<Typography html={translate('translation_outter_key_id')} />
)
}
The output will then look like this :
Third argument of the translate function can be a number :
- 0 means 'none'
- 1 means 'singular'
- 2 or more means plural
Then use |
to split your translations string for the same key.
// in the translation file
{
"translation_key": "I have no cake | I have {{ count }} cake | I have {{count}} cakes"
}
// Let's say with have this component:
const MyComponent = ({ count }) => {
return (
<Typography>{translate("translation_key", { count }, count)}</Typography>
);
};
// output for count === 0 --> "I have no cake"
// output for count === 1 --> "I have 1 cake"
// output for count === 4 --> "I have 4 cakes"
We are using Ditto based on our Figma files to generate the translations.
-
Go to Ditto projects and add a new one by importing an existing figma file
-
Once done, choose which frame you need (try to import the fewer frame to get a maximum of translations)
-
On each frame, select the translations you need and hide the other ones (you can select several and hide it on the right panel)
-
Don't forget to turn on the developer mode
-
Run the command
yarn ditto:addNew
and select the new project -
Run the command
yarn ditto
to update all the translations -
Use the id of translation you want to use in your code and you're good to go !
Note: If you want to update a translation, update it in ditto and then re-run the yarn ditto
command
- On the left pannel of a project click on manage
- Click on each frame "unlink group"
- Save, and place you project in the Production Folder