Search code examples
javaplayframeworkplayframework-2.0playframework-2.5

How to inject MessagesApi outside a controller?


I have a simple class service and it is being injected beautifully on my application. However, I am trying to inject the messages api to read a few keys on my message files but I am getting the same error:

1) Could not find a suitable constructor in play.i18n.Messages. Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private. at play.i18n.Messages.class(Messages.java:61)

public class SampleServiceImpl implements SampleService {
  private MessagesApi messages;

  @Inject
  public SampleServiceImpl(MessagesApi messages){
     this.messages = messages;
  }
}

@ImplementedBy(SampleServiceImpl.class)
    public interface SampleService {
}

Is the a way to do that by DI?

Edit:

I was able to get the value by doing this but it does not look elegant, any options ?

messages.get(new Lang(new Locale("en")), "ticket.form.title")

Solution

  • The reason of such "non-elegancy" is that language (and Messages) depends on the request.

    The Default behavior is that Messages detect current language on the base of cookie, available languages and default language.

    Someware under the hood: Messages messages = messagesApi.preferred(request());

    • Will select a language from the request, based on the languages available, and fallback to the default language if none of the candidates are available.

    Fortunately, there is a special method that you can use to initialize Messages with the language you want:

    import play.i18n.MessagesApi;
    import play.i18n.Messages;
    import play.i18n.Lang;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Locale;
    
    ...
    
    Locale englishLocale = new Locale("en");
    Lang englishLang = new Lang(englishLocale);
    List<Lang> preferredLangs = Arrays.asList(englishLang);
        
    Messages messagesCustom = messagesApi.preferred(preferredLangs);
    
    // the time for elegancy 
    messages.at("ticket.form.title");
    

    I advise you to create tiny MessagesApiCustom service, that will do this few strings of code in the initialization time and then will proxy the at method to the messages.at, so it will look like:

    public class SampleServiceImpl implements SampleService {
      private MessagesApiCustom messages;
    
      @Inject
      public SampleServiceImpl(MessagesApiCustom messages){
         this.messages = messages;
      }
    
      private void doSomeStuff(){
        Strign message = messages.at("message.key")
      }
    }
    

    You can go further, and implement language selection based on annotation:

     @Named("FR")
     private MessagesApiCustom messages;
    

    Of course, if you need the dynamic language selection, then just use the method that is already present in Play.