Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[e4-dev] Set up of my translation scene

Hi,

*Disclaimer: This is a very lengthy mail*

These are the topics I'll discuss in this mail:
a) CORE TRANSLATION FRAMEWORK
b) TRANSLATIONS IN OUR MODEL
c) LANGUAGE CONTEXT / LANGUAGE SWITCHING
d) MODEL CONTRIBUTION TRACKING
e) TRANSLATION IN UI-CODE

On the first sight the system might look complex but in reality it isn't
really. The most important thing is a) the rest builts upon this
foundation and d) has to be done anyways for other stuff like bundle
uninstalls and e) must not be a framework part but it makes translations
fit better in an Eclipse 4.0 world where we defined "Singletons and
Statics are evil"


In the bug following bugs Oleg and I are discussing translations and
things those are related to.

* 306576: Root defect
* 331260: Defines a generic translation infrastructure
* 331010: Model Contribution Tracking

To better understand my arguing I'd like to setup my requirements to NLS
support the Eclipse 4.0 Application Platform.

My main driving points who are influenceing the design:
* I need to get away from only supporting .properties-Files as a
  resource for translations
* I need to switch the locale of a running application without
  restarting
* I see translations of UI-Artifacts as a pure decoration process which
  is an important fact because it removes the need to do all
  translations at application startup

So here's my design-ideas:

CORE TRANSLATION FRAMEWORK:
---------------------------
At the core of the whole system which a extensible service

interface ITranslationService {
  public String translate(String providerId, String key, String
                          language);
}

and

interface ITranslationProvider {
  public String translate(String key, String language);
}

As you might have guessed already the ITranslationService delegates
translations to an ITranslationProvider which means lookup is now
flexible and not only bound to .properties.

By default there's are by default translation providers available for
all BundleLocalization. So in case you want to translate a key "mykey"
from bundle "com.my.personal.bundle" you'll call it like this:

---------------------------8<---------------------------
@Inject
private ITranslationService translation;
translation.translate("com.my.personal.bundle","mykey");
---------------------------8<---------------------------


TRANSLATIONS IN OUR MODEL
-------------------------
In our model attributes we are marking values we need to translate
similar to how we do it in todays plugin.xml by prefixing it with %
which means a Part with a translated name would look like this (I'm
using the XMI-Notation but please keep in mind in the end what is
important is the memory representation of the model).

---------------------------8<---------------------------
<children xsi:type="basic:Part"
          xmi:id="_SeXUDO8EEd6FC9cDb6iV7g"
          elementId="ContactsView"
          contributionURI="platform:/plugin/..."
          label="%contactlist.label"
          iconURI="platform:/plugin/..."
          tooltip="%contactlist.tooltip"/>
---------------------------8<---------------------------

At the very moment the renderer is now creating the UI-Widgets because
of whatever reason it will look up the translation of the
"contactlist.label" and "contactlist.tooltip" using the ITranslationService.

This means the renderer is call the ITranslationService like this
(assuming the Part was contributed by the contacts.demo bundle - take a
look at MODEL CONTRIBUTION TRACKING):

---------------------------8<---------------------------
@Inject
private ITranslationService ts;

@Inject
private IWorkbenchLocaleService ls;

public void render(MPart p) {
  CTabItem item = ...
  item.setText(ts.translate(p.getContributor(),p.getLabel(),getLang()));
}

private String getLang() {
  // See WORKBENCH LANGUAGE CONTEXT
  return ls.getLocale();
}
---------------------------8<---------------------------

This means the default translation strategy is to do the translations
using the BundleLocalization-Service for the lookup but if someone don't
want the translations to come from .properties and e.g. has installed a
ITranslationProvider under the key "mytranslationprovider" one can put
overrule this default by putting the information into the model in the
following way:

---------------------------8<---------------------------
<children xsi:type="basic:Part"
          xmi:id="_SeXUDO8EEd6FC9cDb6iV7g"
          elementId="ContactsView"
          contributionURI="platform:/plugin/..."
          label="%mytranslationprovider#contactlist.label"
          iconURI="platform:/plugin/..."
          tooltip="%mytranslationprovider#contactlist.tooltip"/>
---------------------------8<---------------------------


LANGUAGE CONTEXT / LANGUAGE SWITCHING:
--------------------------------------
In my opinion there has to be a lanuage context which I think is best
suited for our UI stuff to be at workbench level.

We'll provide a service named like this at the workbench level

---------------------------8<---------------------------
interface IWorkbenchLocaleService {
  public void setLocale(String locale);
  public String getLocale();
}
---------------------------8<---------------------------

If the locale is switched by the user we are delivering an event through
our EventBroker and so our render engine can react upon it.


MODEL CONTRIBUTION TRACKING:
----------------------------
And at this very moment 331010 comes into play because the current
information in the model is not enough to look up the translation
because it needs to know from which model element the translation came from.

To answer this we need remember where elements can come from:
a) From the root e4xmi
b) From a fragment e4xmi
c) From a processor e4xmi
d) Totally programmatic (e.g. Addons, Handlers, ...)

a, b, c are quite easy to solve because for a & b we are the ones who
load them and merge them into the model so we can adjust the
contributor, c is quite easy because we know exactly that we are now
launching an processor and can run and track added elements.

The really hard part is d) because those things can happen at any time
and we are not in charge of them.

There are 2 solutions and i think both are needed here:

a) The compat layer should has to use the Standard EMF-Factories and
   reflect the contribution information from the corresponding
   IConfigurationElements

b) All other code will go through custom factory implementations which
   is bundle aware

   public class BundleawareBasicFactory implements MBasicFactory {
     private Bundle b;

     public void createPart() {
        MPart p = MBasicFactory.INSTANCE.createPart();
        // remember the contributor
     }
   }


TRANSLATION IN UI-CODE:
-----------------------
The current solution is to use NLS and static fields like this:

---------------------------8<---------------------------
public class MyMessages {
  public static String myMessage;

  static {
    NLS.initialize(....);
  }
}

Label l ....
l.setText(MyMessages.myMessage);
---------------------------8<---------------------------

which is nice and small but because the informations are stored in
static fields there can be only one language available make this not
useable in multi instance environments and we'll do something we always
said is Eclipse 4.0 will not do it emphasizes the usage of STATICS.

In contrast to the current solution my proposal looks like this:

public class MyMessages {
  public String myMessage;
}

and will be consumed in our UI code like this:

---------------------------8<---------------------------
@Inject
@Translation
private MyMessages myMessage;

Label l ....
l.setText();
---------------------------8<---------------------------

The default lookup of translations is equal to the one of NLS but there
are major differences:
a) No singletons anymore hence multiple locales can be supported in one
   OSGI-Env

b) In case of a language switch through IWorkbenchLocaleService a new
   MyMessages instance can be injected

c) We can plug ourselves into the ITranslationService by annotating the
   MyMessages class like this:

   @Messages(providerid="mytranslationprovider")
   public class MyMessages {
     public String myMessage;
   }

If you managed to follow me until here I hope you found this background
information useful.

Tom

-- 
B e s t S o l u t i o n . a t                        EDV Systemhaus GmbH
------------------------------------------------------------------------
tom schindl                                        geschaeftsfuehrer/CEO
------------------------------------------------------------------------
eduard-bodem-gasse 5/1    A-6020 innsbruck      phone    ++43 512 935834


Back to the top