react-native-calendars icon indicating copy to clipboard operation
react-native-calendars copied to clipboard

i18n: how to use calendar in multiple locales?

Open slorber opened this issue 7 years ago • 23 comments

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?

slorber avatar Sep 06 '17 11:09 slorber

Agree this should be improved. Problem is that XDate locale setup is implemented as singleton. We should check if this can be improved somehow

tautvilas avatar Sep 14 '17 12:09 tautvilas

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

slorber avatar Sep 18 '17 04:09 slorber

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...

wumke avatar Feb 15 '18 08:02 wumke

Also, why are the translations and locale not passed into the Calendar component as properties?

wumke avatar Feb 15 '18 08:02 wumke

I think we should rather migrate away from XDate and use another date lib which does not need singleton.

slorber avatar Feb 15 '18 09:02 slorber

+1

pinpong avatar Mar 22 '18 10:03 pinpong

Is there any way to make LocaleConfig work? In the example app this part is commented on app.js file Thanks! 🙏

piny4man avatar May 11 '18 15:05 piny4man

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.

slorber avatar May 11 '18 16:05 slorber

Thanks @slorber it works! đź‘Ś

piny4man avatar May 14 '18 08:05 piny4man

This module should support i18n like moment

anhtuank7c avatar Sep 28 '18 04:09 anhtuank7c

Thanks :) save the hour đź‘Ť @slorber

k2an avatar Dec 21 '19 17:12 k2an

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.

yt-dev avatar Jul 01 '20 08:07 yt-dev

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.

systemride avatar Jul 06 '20 22:07 systemride

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.

Hey, I add "key={locale}" to Calendars component, it works.

yt-dev avatar Jul 07 '20 14:07 yt-dev

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 avatar Apr 13 '21 15:04 mayconmesquita

@mayconmesquita thanks!

danielkv avatar May 14 '21 01:05 danielkv

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.

urie-ozaki avatar Nov 09 '21 20:11 urie-ozaki

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 };

Irungaray avatar Feb 09 '23 18:02 Irungaray

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}
      />
)

Akeuuh avatar Apr 13 '23 12:04 Akeuuh

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

regalstreak avatar May 17 '23 14:05 regalstreak

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]);

Tobbe avatar Nov 02 '23 12:11 Tobbe

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'

leviFrosty avatar Feb 23 '24 04:02 leviFrosty

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}
      />
)

thirandev avatar Apr 29 '24 09:04 thirandev