Простой способ использования параметризованных сообщений пользовательского интерфейса в Wicket?

Wicket имеет гибкую систему интернационализации StringResourceModel javadocs, например:

WeatherStation ws = new WeatherStation();
add(new Label("weatherMessage", new StringResourceModel(
    "weather.${currentStatus}", this, new Model<String>(ws)));

Но я хочу что-то действительно просто, и не смог найти хороший пример этого.

Рассмотрим этот тип пользовательского интерфейса в файле .properties:

msg=Value is {0}

В частности, я бы не хотел создавать объект модели (с геттерами для значений, которые нужно заменить, например, WeatherStation в приведенном выше примере) только для этой цели. Это просто избыточно, если у меня уже есть значения в локальных переменных, и в противном случае нет необходимости в таком объекте.

Здесь глупый способ "грубой силы" заменить {0} на правильное значение:

String value = ... // contains the dynamic value to use
add(new Label("message", getString("msg").replaceAll("\\{0\\}", value)));

Есть ли чистый, более Wicket-y способ сделать это (это не намного длиннее, чем указано выше) ?

Ответ 1

Я думаю, что наиболее последовательный WICKETY способ может быть достигнут путем улучшения ответа Jonik с MessageFormat:

.properties

msg=Saving record {0} with value {1}

.java

add(new Label("label", MessageFormat.format(getString("msg"),obj1,obj2)));
//or
info(MessageFormat.format(getString("msg"),obj1,obj2));

Почему мне это нравится:

  • Чистое, простое решение
  • Использует простую Java и ничего больше
  • Вы можете заменить столько значений, сколько хотите
  • Работа с ярлыками, информация(), проверка и т.д.
  • Это не полностью wickety, но это согласуется с калитки, поэтому вы можете повторно использовать эти свойства с помощью StringResourceModel.

Примечания:

если вы хотите использовать Модели, вам просто нужно создать простую модель, которая переопределяет функцию toString модели следующим образом:

abstract class MyModel extends AbstractReadOnlyModel{
    @Override
    public String toString()
    {
        if(getObject()==null)return "";
        return getObject().toString();
    }
}

и передать его как аргумент MessageFormat.

Я не знаю, почему Wicket не поддерживает Model в сообщении обратной связи. но если он был поддержан, не было причин использовать эти решения, и вы могли бы использовать StringResourceModel всюду.

Ответ 2

Взгляните на пример 4 в javaadoc StringResourceModel - вы можете передать нулевую модель и явные параметры:

add(new Label("message",
         new StringResourceModel(
             "msg", this, null, value)));

msg=Value is {0}

Ответ 3

Там путь, который, хотя все еще предполагает создание модели, не требует bean с геттером.

учитывая это сообщение в файле свойств:

msg=${} persons

Здесь, как заменить placeholder значением, будь то локальная переменная, поле или литерал:

add(new Label("label", new StringResourceModel("msg", new Model<Serializable>(5))));

Ответ 4

Когда вы столкнулись с чем-то вроде описанного в вопросе, я бы теперь использовал:

.properties

msg=Saving record %s with value %d

Java:

add(new Label("label", String.format(getString("msg"), record, value)));

Почему мне это нравится:

  • Чистое, простое решение
  • Использует обычная Java и ничего больше
  • Вы можете заменить столько значений, сколько хотите (в отличие от трюка ${}). Изменить: хорошо, если вам действительно нужно поддерживать многие языки, где замененные значения могут быть в другом порядке, String.format() не подходит. Вместо этого с использованием MessageFormat - это аналогичный подход, который правильно поддерживает это.

Отказ от ответственности: это "слишком очевидно", но это проще других решений (и, безусловно, лучше, чем мой оригинальный replaceAll() хак). Я изначально искал путь "калитки", в то время как этот вид обходит Wicket &mdash, а затем, кого это волнует?: -)

Ответ 5

Создание модели для вашей метки действительно Wicket Way. Тем не менее, вы можете легко справиться со случайными функциями утилиты. Здесь я использую:

/**
 * Creates a resource-based label with fixed arguments that will never change. Arguments are wrapped inside of a
 * ConvertingModel to provide for automatic conversion and translation, if applicable.
 * 
 * @param The component id
 * @param resourceKey The StringResourceModel resource key to use
 * @param component The component from which the resourceKey should be resolved
 * @param args The values to use for StringResourceModel property substitutions ({0}, {1}, ...).
 * @return the new static label
 */
public static Label staticResourceLabel(String id, String resourceKey, Component component, Serializable... args) {
    @SuppressWarnings("unchecked")
    ConvertingModel<Serializable>[] models = new ConvertingModel[args.length];
    for ( int i = 0; i < args.length; i++ ) {
        models[i] = new ConvertingModel<Serializable>( new Model<Serializable>( args[i] ), component );
    }
    return new CustomLabel( id, new StringResourceModel( resourceKey, component, null, models ) );
}

Детали, которые я затушевываю, следующие:

  • Я создал свой собственный ConvertingModel, который автоматически преобразует объекты в их представление String на основе IConverters, доступных для данного компонента
  • Я создал свой собственный CustomLabel, который применяет пользовательскую посылку текста меток (как описано в этом ответе)

С помощью пользовательского IConverter, например, для объекта Temperature, вы можете иметь что-то вроде:

Properties key:
temperature=The current temperature is ${0}.

Page.java code:
// Simpler version of method where wicket:id and resourceKey are the same
add( staticResourceLabel( "temperature", new Temperature(5, CELSIUS) ) );

Page.html:
<span wicket:id='temperature'>The current temperature is 5 degrees Celsius.</span>

Недостатком этого подхода является то, что у вас больше нет прямого доступа к классу Label, вы не можете подклассифицировать его для переопределения isVisible() или подобных вещей. Но для моих целей он работает 99% времени.

Ответ 6

Если в вашем Компоненте есть Модель, которая содержит объект со значениями, к которым вы хотите получить доступ из ваших заполнителей в качестве замены, вы можете написать:

new StringResourceModel("salutation.text", getModel());

Давайте представим, что тип возвращаемого значения getModel() - IModel<User>, а User содержит такие поля, как firstName и lastName. В этом случае вы можете легко получить доступ к полям firstName и lastName внутри строки вашего свойства:

salutation.text=Hej ${firstName} ${lastName}, have a nice day!

Дополнительную информацию вы можете найти здесь: https://ci.apache.org/projects/wicket/apidocs/8.x/org/apache/wicket/model/StringResourceModel.html#StringResourceModel-java.lang.String-org.apache.wicket.model.IModel-