wix / react-native-calendars

React Native Calendar Components 🗓️ 📆
MIT License
9.44k stars 2.93k forks source link

i18n: how to use calendar in multiple locales? #169

Open slorber opened 7 years ago

slorber commented 7 years ago

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?

tautvilas commented 6 years ago

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

slorber commented 6 years ago

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

wumke commented 6 years ago

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 commented 6 years ago

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

slorber commented 6 years ago

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

pinpong commented 6 years ago

+1

piny4man commented 6 years ago

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

slorber commented 6 years ago

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.

piny4man commented 6 years ago

Thanks @slorber it works! đź‘Ś

anhtuank7c commented 5 years ago

This module should support i18n like moment

k2an commented 4 years ago

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

yt-dev commented 4 years ago

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.

systemride commented 4 years ago

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.

yt-dev commented 4 years ago

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.

mayconmesquita commented 3 years ago

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 };
danielkv commented 3 years ago

@mayconmesquita thanks!

urie-ozaki commented 2 years ago

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.

Irungaray commented 1 year ago

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 };
Akeuuh commented 1 year ago

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}
      />
)
regalstreak commented 1 year ago

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

Tobbe commented 10 months ago

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]);
leviFrosty commented 6 months ago

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'
thirandev commented 4 months ago

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