Hi Tom, Nice write-up. It might make sense to create a set of Wiki pages based on your post so that design can be updated as it evolves.
The most important part is the design goal. The 3.x approach is highly optimized for the "simple" case, but does not support more complicated scenarios. To me, it feels like the proposed new design would support well the advanced use cases, but at the expense of performance/scalability/usability of the simple case. (Simple case being: having one language that does not change). So, I'd add the following two goals: - it should be possible to describe the basic part of "the new NLS" to a plug-in developer in under two paragraphs; - it should scale well enough to not to be a memory or CPU hog for large size applications Sincerely, Oleg Besedin From: Tom Schindl <[email protected]> To: E4 Project developer mailing list <[email protected]> Date: 12/16/2010 12:33 PM Subject: [e4-dev] Set up of my translation scene Sent by: [email protected] 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 _______________________________________________ e4-dev mailing list [email protected] https://dev.eclipse.org/mailman/listinfo/e4-dev
_______________________________________________ e4-dev mailing list [email protected] https://dev.eclipse.org/mailman/listinfo/e4-dev
