0

I'd like to use the C10N library to manage translations in a Java project, but I don't understand how to use it to enable classes generate translated strings.

Please consider the following single-language example.

public interface Pet {
    public String getBreed();
}

public class Dog implements Pet {

    private String breed;

    public Dog(String breed) {
        this.breed = breed;
    }

    public String getBreed() {
        return this.breed;
    }

}

Then, I'd like to change the previous implementation to support i18n.

So, I could declare some translations using C10N:

public interface DogBreeds{
    @En("dalmatian")
    @It("dalmata")
    String dalmatian();

    @En("poodle")
    @It("barboncino")
    String poodle();
}

Now, I'd like to use one of these methods in a class to return a translated String.
The idea is to pass somehow the method to the class and then call it from a class' method.
In other words, the class should be able to return the correct translation based on the current locale, that may change at runtime.

public class Dog implements Pet {

    public Dog(??) {
        // the class should receive all the translations
        // related to a specific breed
    }

    public String getBreed() {
        // here I want to return the correct breed name translation,
        // based on the current locale
        return ??;
    }

}

The Dog class should not access the DogBreeds interface directly. For example, Dog may be part of a library, whereas DogBreeds may be declared in the main project.

The Dog class shouldn't hold just one translation based on the current locale, but all the available translations, so that it can return the correct one if the locale changes.

Is this feasible is a clean way?
If not, what are the alternatives?

Paolo Fulgoni
  • 5,208
  • 3
  • 39
  • 55

2 Answers2

1

I would try:

public class Dog implements Pet {
    private static final DogBreeds BREEDS = C10N.get(DogBreeds.class);    

    public Dog() {
        // no need to pass the translations to the constructor
    }

    public String getBreed() {
        // Since you're wanting to specify a message key dynamically,
        // you need to use getString instead of BREEDS.poodle().
        return BREEDS.getString(myBreed);
    }

}
chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
1

If you would like to separate DogBreed(main project) and Dog(library), then considering that your main projects references the library (and not the other way round), you'll need to introduce some other shared interface in the library, let's say, BreedNameProvider.

public interface BreedNameProvider{
  String dalmatian();
  String poodle();
}

Then you can pass that to Dog class constructor:

public class Dog implements Pet {
 private final BreedNameProvider bnp;
 public Dog(BreedNameProvider bnp) {
   this.bnp = bnp;
 }

 public String getBreed() {
   return bnp.poodle();
 }
}

The implementation of BreedNameProvider can be declared in the main project, so can safely access DogBreeds interface.

Edit: You can use a more gerenic BreedNameProvider, to return the translation you need, with working locale switching at runtime:

//Declared in your library
public interface BreedNameProvider{
  String getBreedName();
}

public class Dog implements Pet {
 private final BreedNameProvider bnp;
 public Dog(BreedNameProvider bnp) {
   this.bnp = bnp;
 }

 public String getBreed() {
   return bnp.getBreedName();
 }
}

In your main project, when instantiating different types of Dog you can just pass an implementation of BreedNameProvider that will return the localized string for the breed you want:

public class MainProject{
  private static final DogBreeds breeds = C10N.get(DogBreeds.class);

  public void createDogs(){
    //Create a poodle
    Dog poodle = new Dog(new BreedNameProvider(){
      @Override
      public String getBreedName(){
        return breeds.poodle();
      }
    });

    //Create dalmatian
    Dog dalmatian = new Dog(new BreedNameProvider(){
      @Override
      public String getBreedName(){
        return breeds.dalmatian();
      }
    });
  }
}

In effect, this is equivalent to "passing a method" to Dog constructor, exactly what you asked for in your original post. In java this is pretty much the only way to do it without sacrificing compile-time safety.

One alternative way to implement BreedNameProvider would be like this:

public class MainProject{
  private static final DogBreeds breeds = C10N.get(DogBreeds.class);

  class CodeBreedNameProvider implements BreedNameProvider{
    private final String breedCode;

    CodeBreedNameProvider(String breedCode){
      this.breedCode = breedCode;
    }

    @Override
    public String getBreedName(){
      switch(breedCode){
        case "poodle": return breeds.poodle();
        case "dalmatian": return breeds.dalmatian();
        //... more if needed
      }
      return "unknown breed code " + breedCode;
    }
  }

  public void createDogs(){
    //Create a poodle
    Dog poodle = new Dog(new CodeBreedNameProvider("poodle"));

    //Create dalmatian
    Dog dalmatian = new Dog(new CodeBreedNameProvider("dalmatian"));
  }
}
rodion
  • 14,729
  • 3
  • 53
  • 55
  • Probably my question isn't clear enough. What I need to do is to instantiate a `Dog` which gets the breed name from `dalmatian()` method and another one which uses `poodle()` (and so on). So I don't want to pass the whole interface, but just one specific translation. If this makes no sense, is there an alternative? For example declaring multiple `C10N` interfaces and passing a specific one... – Paolo Fulgoni Feb 14 '14 at 09:39
  • If you know the breed name just before instantiation of Dog, cant you just pass the breed name to its costructor? If you dont know the name until runtime, you can have a more generic BreedNameProvider#getBreedName() do the logic and return the approprite value. Or maybe I'am not following the question right? – rodion Feb 15 '14 at 07:06
  • I don't want to pass to the class only one string based on the current locale. The `Dog` class should be able to return the breed name based on the current locale, even if the locale changes at runtime. I updated my question, I hope it's a bit more clear now. – Paolo Fulgoni Feb 17 '14 at 08:07
  • Please see the **Edit** in my answer. Hope this clears it up. – rodion Feb 18 '14 at 04:21