react-native-calendars
react-native-calendars copied to clipboard
i18n: how to use calendar in multiple locales?
Let's say I want to display, on the same view, the calendar in english and in french (or at least be able to toggle the language in app state).
Currently it seems the only way is to use a singleton like `LocaleConfig.defaultLocale = 'fr' but that does not seem appropriate to me. Couldn't we pass the locale as props in any component that need localization?
Agree this should be improved. Problem is that XDate locale setup is implemented as singleton. We should check if this can be improved somehow
Actually it's possible to change the singleton of xdate before re rendering and solved my usecase for translated app but it's not ideal. I'll try to add some doc about that soon it should be enough for most usecases
I remembered that locales in moment are well supported, so it should be possible to fill the arrays of LocaleConfig.locales[] by just giving the locales and some options to a 'useMomentLocales(options)' function? I'm willing to code the changes if it will be added to the repo...
Also, why are the translations and locale not passed into the Calendar component as properties?
I think we should rather migrate away from XDate and use another date lib which does not need singleton.
+1
Is there any way to make LocaleConfig work? In the example app this part is commented on app.js file Thanks! 🙏
I've successfully switched from english to french with the following:
LocaleConfig.locales.en = LocaleConfig.locales[''];
LocaleConfig.locales.fr = {
monthNames: [
'Janvier',
'FĂ©vrier',
'Mars',
'Avril',
'Mai',
'Juin',
'Juillet',
'Août',
'Septembre',
'Octobre',
'Novembre',
'DĂ©cembre',
],
monthNamesShort: [
'Janv.',
'FĂ©vr.',
'Mars',
'Avril',
'Mai',
'Juin',
'Juil.',
'Août',
'Sept.',
'Oct.',
'Nov.',
'DĂ©c.',
],
dayNames: [
'Dimanche',
'Lundi',
'Mardi',
'Mercredi',
'Jeudi',
'Vendredi',
'Samedi',
],
dayNamesShort: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.', 'Sam.'],
};
LocaleConfig.defaultLocale = 'fr';
Then you just have to run LocaleConfig.defaultLocale = 'fr'
or LocaleConfig.defaultLocale = 'en'
before triggering a re-render and you'll have the calendar localized.
Thanks @slorber it works! đź‘Ś
This module should support i18n like moment
Thanks :) save the hour đź‘Ť @slorber
It doesn't work for me. After I change the language, I also need to change its local setting by manual click the calendar one time to trigger it.
It doesn't work for me. After I change the language, I also need to change its local setting by manual click the calendar one time to trigger it.
I had the same issue if I set the LocaleConfig.defaultLocale
in componentDidMount
. If I place it in render
instead it seems to work.
It doesn't work for me. After I change the language, I also need to change its local setting by manual click the calendar one time to trigger it.
I had the same issue if I set the
LocaleConfig.defaultLocale
incomponentDidMount
. If I place it inrender
instead it seems to work.
Hey, I add "key={locale}" to Calendars component, it works.
If anyone need this in PT-BR:
import { LocaleConfig } from 'react-native-calendars';
// pt-br localization for react-native-calendars
LocaleConfig.locales['pt-br'] = {
monthNames: [
'Janeiro',
'Fevereiro',
'Março',
'Abril',
'Maio',
'Junho',
'Julho',
'Agosto',
'Setembro',
'Outubro',
'Novembro',
'Dezembro',
],
monthNamesShort: [
'Jan',
'Fev',
'Mar',
'Abr',
'Mai',
'Jun',
'Jul.',
'Ago',
'Set',
'Out',
'Nov',
'Dec',
],
dayNames: [
'Domingo',
'Segunda-feira',
'Terça-feira',
'Quarta-feira',
'Quinta-feira',
'Sexta-feira',
'Sábado',
],
dayNamesShort: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
today: 'Hoje',
};
LocaleConfig.defaultLocale = 'pt-br';
export { LocaleConfig };
@mayconmesquita thanks!
Need support for i18n
! Currently we can only use i18n
keys to replace hard-coded month and day names which really doesn't seem to be an idiomatic React way of doing things.
I've added PT and ES doing something similar as @mayconmesquita:
export const hourPickerLocales = {
pt: {
monthNames: [
'Janeiro',
'Fevereiro',
'Março',
'Abril',
'Maio',
'Junho',
'Julho',
'Agosto',
'Setembro',
'Outubro',
'Novembro',
'Dezembro',
],
monthNamesShort: [
'Jan',
'Fev',
'Mar',
'Abr',
'Mai',
'Jun',
'Jul.',
'Ago',
'Set',
'Out',
'Nov',
'Dec',
],
dayNames: [
'Domingo',
'Segunda-feira',
'Terça-feira',
'Quarta-feira',
'Quinta-feira',
'Sexta-feira',
'Sábado',
],
dayNamesShort: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
today: 'Hoje',
},
en: {
amDesignator: 'AM',
dayNames: [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
],
dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
monthNames: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
monthNamesShort: [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
],
pmDesignator: 'PM',
},
es: {
monthNames: [
'Enero',
'Febrero',
'Marzo',
'Abril',
'Mayo',
'Junio',
'Julio',
'Agosto',
'Septiembre',
'Octubre',
'Noviembre',
'Diciembre',
],
monthNamesShort: [
'En',
'Feb',
'Mar',
'Abr',
'May',
'Jun',
'Jul',
'Ago',
'Sep',
'Oct',
'Nov',
'Dic',
],
dayNames: [
'Domingo',
'Lunes',
'Martes',
'Miércoles',
'Jueves',
'Viernes',
'Sábado',
],
dayNamesShort: ['Dom', 'Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'Sáb'],
today: 'Hoy',
},
};
// External Components
import React, { useContext } from 'react';
import { CalendarList, LocaleConfig } from 'react-native-calendars';
// Utils & Config
import { hourPickerLocales } from '../../../../i18n/date_picker';
import AuthContext from '../../../context/AuthContext';
const HourPicker = (props) => {
const { user } = useContext(AuthContext);
const lang = user.default_language?.substring(0, 2) || 'en';
LocaleConfig.locales['pt'] = hourPickerLocales['pt'];
LocaleConfig.locales['en'] = hourPickerLocales['en'];
LocaleConfig.locales['es'] = hourPickerLocales['es'];
LocaleConfig.defaultLocale = lang;
return (
<CalendarList ... />
);
};
export { HourPicker };
I was looking for a solution to have the months in current locale of the app and the only way I've make it to work was to use a custom header like this
const renderMonth = (month: { current: string }) => {
const monthName = moment(month.current).format('MMMM YYYY');
const upperCaseMathName = monthName.charAt(0).toUpperCase() + monthName.slice(1);
return (
<View style={{ justifyContent: 'center', alignItems: 'center', marginTop: 20 }}>
<Text typo="buttonLarge">{upperCaseMathName}</Text>
</View>
);
};
return (
<CalendarList
{...otherProps}
customHeader={renderMonth}
/>
)
Easiest way to get this working:
import { Calendar, LocaleConfig } from 'react-native-calendars';
import dayjs from 'dayjs';
import localeData from 'dayjs/plugin/localeData';
dayjs.extend(localeData);
...
useEffect(() => {
LocaleConfig.locales["default"] = {
monthNames: dayjs.months(),
monthNamesShort: dayjs.monthsShort(),
dayNames: dayjs.weekdays(),
dayNamesShort: dayjs.weekdaysShort(),
today: "Today",
};
LocaleConfig.defaultLocale = "default";
}, []);
Just remember to set a proper dayjs locale when you're changing the language
Thanks @regalstreak! This is how I did it with luxon, a custom context we have and react-intl. And unfortunately there's an issue with luxon and getting the name of weekdays on React Native (https://github.com/moment/luxon/issues/1500), so I had to come up with a workaround for that.
import { Info } from "luxon";
function daysForLocale(
localeName: string,
weekday: "long" | "short" | "narrow" = "long"
) {
const { format } = new Intl.DateTimeFormat(localeName, { weekday });
return [...Array(7).keys()].map((day) =>
format(new Date(Date.UTC(2021, 5, day)))
);
}
useEffect(() => {
const locale = localizationContext.getLocale();
LocaleConfig.locales.default = {
monthNames: Info.months("long", { locale }),
monthNamesShort: Info.months("short", { locale }),
dayNames: daysForLocale(locale),
dayNamesShort: daysForLocale(locale, "short"),
today: intl.formatMessage({ id: "custom-interval.calendar.today" }),
};
LocaleConfig.defaultLocale = "default";
}, [localizationContext, intl]);
For those using moment.js, @regalstreak's implementation also works for moment.js since dayjs has the same apis.
Be sure to import your moment localization files somewhere in the root of your project otherwise moment will fallback to english.
LocaleConfig.locales['default'] = {
monthNames: moment.months(),
monthNamesShort: moment.monthsShort(),
dayNames: moment.weekdays(),
dayNamesShort: moment.weekdaysShort(),
}
LocaleConfig.defaultLocale = 'default'
I updated my code using @Akeuuh comment, to be used with i18n
. It smoothly translates as expected.
const renderMonth = (month: { current: string }) => {
const monthDateTime = DateTime.fromISO(month.current); // luxon DateTime
const monthName = monthDateTime
.setLocale(languageCode) // Language Code - en,sv...
.toLocaleString({ month: 'long', year: 'numeric' });
const namesOfWeek = [...Array(7).keys()].map(index =>
monthDateTime
.setLocale(languageCode)
.set({ weekday: index + 1 })
.toFormat('EEE'),
);
return (
<View style={{ justifyContent: 'center', alignItems: 'center', marginTop: 20 }}>
<Text>{monthName}</Text>
<View style={{ width: '100%',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginVertical: 20,}}>
{namesOfWeek.map((week, index) => (
<Text
style={{
marginRight: index >= 2 ? 4 : 2,
fontSize: 12,
}}>
{week}
</Text>
))}
</View>
</View>
);
};
return (
<CalendarList
{...otherProps}
customHeader={renderMonth}
/>
)