Не меняется локализация календаря

Здравствуйте! Столкнулся с проблемой, что в русской локализации календарь для выбора даты (поле типа LocalDate, класс TypedDatePicker<>, xml - datePicker) выдаёт все названия на английском языке (November 2023, Today, Cancel). Пробовал много где искать, как это сделать на уровне приложения так и не понял.
Если отдельно в контроллере экрана задать параметры I18, то работает. Можно конечно это сделать в каждом экране, но в фильтрах экранах списка всё равно отображается английский вариант.

DatePicker.DatePickerI18n russianI18n = new DatePicker.DatePickerI18n();

        russianI18n.setMonthNames(List.of("Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август",
                "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"));
        russianI18n.setWeekdays(List.of("Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"));
        russianI18n.setWeekdaysShort(List.of("Вск", "Пон", "Вт", "Ср", "Чт", "Пт", "Сб"));
        russianI18n.setToday("Сегодня");
        russianI18n.setCancel("Отмена");

        registrationDateField.setI18n(russianI18n);

Подскажите, как быть?

Предлагаю не закрывать вопрос, а дождаться ответа разработчиков на проблему. Или можете завести таску на гите.

Как workaround (Если у вас предполагается использование одного языка)

Можете отнаследовать TypedDatePicker

class MyTypedDatePicker<V : Comparable<*>> : io.jmix.flowui.component.datepicker.TypedDatePicker<V>() {
    
    override fun initComponent() {
        super.initComponent()

        val russianI18n = DatePickerI18n();
        russianI18n.setDateFormat(messages.getMessage("dateFormat"))

        russianI18n.setMonthNames(listOf("Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август",
                "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"))
        russianI18n.setWeekdays(listOf("Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"))
        russianI18n.setWeekdaysShort(listOf("Вск", "Пон", "Вт", "Ср", "Чт", "Пт", "Сб"))
        russianI18n.setToday("Сегодня")
        russianI18n.setCancel("Отмена")
        russianI18n.setFirstDayOfWeek(1)

        setI18n(russianI18n)
    }

}

И в конфигурацию добавить бин ComponentRegistration

    @Bean
    open fun typedDatePickerComponent(): ComponentRegistration {
        return ComponentRegistrationBuilder.create(MyTypedDatePicker::class.java)
                .withComponentLoader("datePicker", DatePickerLoader::class.java)
                .replaceComponent(io.jmix.flowui.component.datepicker.TypedDatePicker::class.java)
                .build()
    }
1 симпатия

Этот код на каком-то другом языке? Попробовал вписать бин вот так:

    @Bean
    ComponentRegistration typedDatePickerComponent(ComponentRegistration componentRegistration) {
        return ComponentRegistrationBuilder.create(MyTypedDatePicker.class)
                .withComponentLoader("datePicker", DatePickerLoader.class)
                .replaceComponent(TypedDatePicker.class)
                .build();
    }

Выдаёт ошибку:

Description:

The dependencies of some of the beans in the application context form a cycle:

   flowui_CustomComponentsRegistry (field protected java.util.List io.jmix.flowui.sys.registration.CustomComponentsRegistry.componentRegistrations)
???????
|  typedDatePickerComponent defined in ru.andrianov.test.TestProjectApplication
???????

Попробовал убрать аргумент из метода, всё заработало, спасибо. Да, это действительно подойдёт для одного языка. При чем мои коллеги вписывали календарь в свои проекты, у них на версии 1.5 (скорей всего) показывает один текущий месяц и на русском языке, без “Today” и “Cancel”.
Пытался нарыть, откуда календарь берет английские названия, так и не нашёл. Если попробовать вывести месяца из текущего TypedDatePicker, выводится NPE.
Возможно есть какие то форматы локализации, которые можно вписать в messages_ru.properties, но я так нигде их и не нашёл.

И, на случай если знаете, на сайте vaadin расписан случай как локализировать пакет (задание параметров как раз оттуда), но там не описано как зарегистрировать бин. Все примеры на сайте vaadin пишутся тупо с аннотацией @Route(“какой-то текст”). Для конкретного экрана это подошло, но не для приложения в целом. Вот ссылка: https://vaadin.com/docs/latest/components/date-picker. Как это можно применять?

Да. Котлин.

Судя по коду TypedDatePicker, а именно initComponent который мы и переопределяем, там не берутся сообщения из пакетов сообщений (только dateFormat). Тут на стороне jmix наверн добавить как раз надо. Со стороны vaadin - скорее всего определяется на стороне js.

Просто в jmix 1.5 используется старая версия Vaadin, и там видимо была поддержка локалей у календаря.
Как я понимаю в vaadin flow это еще не реализовано.

Не очень понимаю что вам конкретно нужно. Пакеты сообщений берет на себя jmix. У них для этого есть JmixI18NProvider
Route - это аннотация для указания url экрана.

Походу вот оно

На странице datePicker самого vaadin для каждого примера создаётся java класс, например DatePickerLocale, DatePickerBasic с соответствующими параметрами в аннотации @Route. Но не понятно как их зарегистрировать и использовать, вот я о чём.

Здорово, спасибо, очень помогли! Я насчёт js не подумал даже, копался во всяких java классах, искал где базовая инициализация идёт или где стандартный языковой пакет находится. Пока ещё не достаточно опыта в подобных делах))

Если вы про

@Route("date-picker-locale")
public class DatePickerLocale extends Div {
    public DatePickerLocale() {
        Locale finnishLocale = new Locale("fi", "FI");

        DatePicker datePicker = new DatePicker("Select a date:");
        datePicker.setLocale(finnishLocale);
        datePicker.setValue(LocalDate.now(ZoneId.systemDefault()));
        datePicker.setHelperText(
                "Date picker configured to use Finnish date format");

        add(datePicker);
    }

}

То здесь DatePickerLocale - это класс экрана. в конструкторе создается datePicker DatePicker datePicker = new DatePicker("Select a date:"); и добавляется на экран add(datePicker);

Vaadin сам по себе не использует Spring или что-то типа того.
Вы просто создаете Класс экрана (у него как раз и используется @Route) и наполняете его объектами.
Вот пример из туториала.

Всякие дескрипторы и инджектирование компонентов в экран из дескрипторв сделано на стороне jmix.

Не думаю что вам нужно думать над логикой работы ваадина, по регистрации его компонентов. Нужно разбираться с тем как jmix его использует. Если только вы не хотите перейти на чистый Vaadin.

А как дождаться тут ответа от разработчиков?)

@gorelov Глеб. Можете посмотреть?)

Спасибо, пока пользуюсь первым воркаутом))

Спасибо, что сообщили о проблеме, завел задачу на GitHub. Думаю, что не будем дожидаться Vaadin и поправим на серверной части компонента у себя.

Глеб

workaround работает на 2.0.2.
Апгрейдишься на 2.1 - не работает.
хм…

Как я понимаю в 2.1.0 Обновили загрузку компонентов из xml.

Тот метод что я предложил все еще работает для фильтров.

В остальном скорее всего нужно переопределить
ComponentLoaderSupport#loadDatePickerI18n

Прошу прощения, но можно подробней как это сделать? Моих знаний пока не хватает(

Переопределяете функцию loadDatePickerI18n в бине ComponentLoaderSupport
https://docs.jmix.ru/jmix/modularity/extension.html#beans

Переопределил как в примере:

    @Primary
    @Bean
    CustomComponentLoaderSupport customComponentLoaderSupport(Context context) {
        return new CustomComponentLoaderSupport(context);
    }

Вот сам кастом:

@Component
public class CustomComponentLoaderSupport extends ComponentLoaderSupport {

    public CustomComponentLoaderSupport(Context context) {
        super(context);
    }

    @Override
    public void loadDatePickerI18n(Element element, Consumer<DatePicker.DatePickerI18n> setter) {

        super.loadDatePickerI18n(element, setter);

        DatePicker.DatePickerI18n russianI18n = new DatePicker.DatePickerI18n();

        russianI18n.setMonthNames(List.of("Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август",
                "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"));
        russianI18n.setWeekdays(List.of("Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"));
        russianI18n.setWeekdaysShort(List.of("Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"));
        russianI18n.setToday("Сегодня");
        russianI18n.setCancel("Отмена");
        russianI18n.setFirstDayOfWeek(1);

    }
}

Но выдаёт ошибку, не найден параметр конструктора 0. В какие места я ему @Autowired не пихал, не помогло(

У CustomComponentLoaderSupport @Component не нужен. Вы же Bean определяете для него.

И мб в customComponentLoaderSupport нужно добавить @Scope(BeanDefinition.SCOPE_PROTOTYPE). Ведь ComponentLoaderSupport везде создается при помощи applicationContext.getBean(ComponentLoaderSupport.class, context);

Не получилось, та же ошибка((.

Description:

Parameter 0 of method customComponentLoaderSupport in ru.Application required a bean of type 'io.jmix.flowui.xml.layout.ComponentLoader$Context' that could not be found.


Action:

Consider defining a bean of type 'io.jmix.flowui.xml.layout.ComponentLoader$Context' in your configuration.

Как я понимаю, ему нужен этот бин, но он его почему-то не видит, хотя оригинальный класс его видит. Возможно это как-то связано с видимостью в пакетах или чем-то подобным…

Тогда уберите определение бина совсем.
В CustomComponentLoaderSupport поставьте аннотации как ComponentLoaderSupport ток у Component value не добавляйте. В пропертях добавьте jmix.core.exclude-beans=flowui_ComponentLoaderSupport

Так у меня заработало.

И еще. думаю в loadDatePickerI18n не нужен super.loadDatePickerI18n(element, setter);
Там надо вызвать loadDateFormat(russianI18n, element) и в конце добавить setter.accept(russianI18n)

@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
class CustomComponentLoaderSupport(
        context: ComponentLoader.Context
) : ComponentLoaderSupport(context) {

    override fun loadDatePickerI18n(element: Element, setter: Consumer<DatePickerI18n>) {
        val russianI18n = DatePickerI18n()
        loadDateFormat(russianI18n, element)

        russianI18n.setMonthNames(listOf("Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август",
                "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"))
        russianI18n.setWeekdays(listOf("Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"))
        russianI18n.setWeekdaysShort(listOf("Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"))
        russianI18n.setToday("Сегодня")
        russianI18n.setCancel("Отмена")
        russianI18n.setFirstDayOfWeek(1)

        setter.accept(russianI18n)
    }
}

image

Пс. Он не видит context потому-что бина ComponentLoader.Context нету. Это простой класс который передается при создании бина applicationContext.getBean(ComponentLoaderSupport.class, context);