All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.mmm.util.nls.api.package-info Maven / Gradle / Ivy

/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0 */
/**
 * Provides the API for the native language support (NLS). 
 * 

NLS API

Applications that should be used by people all over the world need native language support * (NLS). The developers task is the internationalization (i18n) where the application has to be written in a * way that the code is (mostly) independent from locale-specific informations. This is a challenging task that affects * many aspects like GUI-dialogs as well as all text-messages displayed to the end-user. The NLS provided here only * addresses the internationalization of text-messages in a way that allows localization (l10n) to the users * locale.
*

The Problem

*

* *

*

* Java already comes with great i18n support. But IMHO there are some tiny peaces missing to complete the great puzzle * of NLS:
* There is almost no support if an application needs NLS that is handling multiple users with different locales * concurrently (e.g. a web-application).
* You will typically store your messages in a {@link java.util.ResourceBundle}. Now if you store the technical key of * the bundle in a message or exception the receiver needs the proper {@link java.util.ResourceBundle} to decode it or * he ends up with a cryptic message he can NOT understand (e.g. as illustrated by the screenshot).
* On the other hand you need to know the locale of the receiver to do the l10n when creating the message or exception * with the proper text. This may lead to sick design such as static hacks. Also if you have to translate the text at * the creation of the message every receiver has to live with this language. Especially for logging this is a big * problem. An operator will be lost in space if he gets such logfiles: * *

 * [2000-01-31 23:59:00,000][ERROR][n.s.m.u.n.a.MasterService] The given value (256) has to be in the range from 0 to 100.
 * [2000-01-31 23:59:01,000][WARN ][n.s.m.u.n.a.MasterService] Der Benutzername oder das Passwort sind ungültig.
 * [2000-01-31 23:59:02,000][ERROR][n.s.m.u.n.a.MasterService] 文件不存在。
 * [2000-01-31 23:59:03,000][FATAL][n.s.m.u.n.a.MasterService] ข้อผิดพลาดที่ไม่คาดคิดได้เกิดขึ้น
 * 
*

*

The Solution

The solution is quite simple:
* We simply bundle the message in default language together with the separated dynamic arguments in one container * object that is called {@link net.sf.mmm.util.nls.api.NlsMessage}. For exceptions there is additional support via * {@link net.sf.mmm.util.exception.api.NlsException} and {@link net.sf.mmm.util.exception.api.NlsRuntimeException}. * Here is an example to clarify the idea of {@link net.sf.mmm.util.nls.api.NlsMessage}: The i18n message is * "Hi {name}! How are you?" and the dynamic argument is the users name e.g. "Lilli". Now if we store these informations * together we have all we need. To get the localized message we simply translate the i18n message to the proper * language and then fill in the arguments. If we can NOT translate we always have the message in default language which * is "Hi Lilli! How are you?".
* But how do we translate the i18n message without artificial intelligence? The answer is quite easy: *

Recommended Approach: {@link net.sf.mmm.util.nls.api.NlsBundle}

The recommended approach is to create an * interface derived from {@link net.sf.mmm.util.nls.api.NlsBundle}. For each message you define a method that takes the * arguments to fill in and returns an {@link net.sf.mmm.util.nls.api.NlsMessage}. Via annotations you provide the * default message for each method. * *
 * package foo.bar;
 *
 * public interface NlsBundleFooBarRoot extends {@link net.sf.mmm.util.nls.api.NlsBundle} {
 *
 *   @{@link net.sf.mmm.util.nls.api.NlsBundleMessage}("Hi {name}! How are you?")
 *   {@link net.sf.mmm.util.nls.api.NlsMessage} messageSayHi(@{@link javax.inject.Named}("name") String name);
 *
 *   @{@link net.sf.mmm.util.nls.api.NlsBundleMessage}("Sorry. The login \"{login}\" is already in use. Please choose a different login.")
 *   {@link net.sf.mmm.util.nls.api.NlsMessage} errorLoginInUse(@{@link javax.inject.Named}("login") String login);
 * }
 * 
* * From your code you now can do this: * *
 * String userName = "Lilli";
 * {@link net.sf.mmm.util.nls.api.NlsMessage} msg = {@link net.sf.mmm.util.nls.api.NlsAccess#getBundleFactory()}.{@link
 * net.sf.mmm.util.nls.api.NlsBundleFactory#createBundle(Class)
 * createBundle}(NlsBundleFooBarRoot.class).messageSayHi(userName);
 * String textDefault = msg.{@link net.sf.mmm.util.nls.api.NlsMessage#getLocalizedMessage() getLocalizedMessage}());
 * String textDe = msg.{@link net.sf.mmm.util.nls.api.NlsMessage#getLocalizedMessage(java.util.Locale)
 * getLocalizedMessage}({@link java.util.Locale}.GERMANY));
 * 
* * For the error message create an exception like this: * *
 * public class LoginAlreadyInUseException extends {@link net.sf.mmm.util.exception.api.NlsRuntimeException} {
 *   public LoginAlreadyInUseException(String usedLogin) {
 *
 *     super({@link net.sf.mmm.util.exception.api.NlsRuntimeException
 *     createBundle}(NlsBundleFooBarRoot.class).errorLoginInUse(usedLogin));
 *   }
 * }
 * 
* * For further details see {@link net.sf.mmm.util.nls.api.NlsBundle}.
*
* For localization you can create property files with the translations of your NLS-bundle. E.g. * {@code foo/bar/NlsBundleFooBar_de.properties} with this content: * *
 * messageSayHi = Hallo {name}! Wie geht es Dir?
 * errorLoginInUse = Es tut uns leid. Das Login "{login}" ist bereits vergeben. Bitte wählen Sie ein anderes Login.
 * 
* * Unlike the Java defaults, here resource bundles are read in UTF-8 encoding and allow to use named parameters as * alternative to indexes what makes it easier for localizers. There are even more advanced features such as recursive * translation of arguments and choice format type. See {@link net.sf.mmm.util.nls.api.NlsMessage} for further details. * Also our solution supports specific environments such as GWT (google web toolkit) what makes it very interoperable. * * In order to support you with creating and maintaining the localized properties, this solution also comes with the * {@code net.sf.mmm.util.nls.base.ResourceBundleSynchronizer}.
* The advantage is that also the bundle name and key are available in the {@link net.sf.mmm.util.nls.api.NlsMessage} * and that this approach is GWT compatible when using {@code mmm-util-gwt}. However, there is still our legacy * approach. *

Alternate Approach (legacy): {@link net.sf.mmm.util.nls.base.AbstractResourceBundle}

Simply create a * subclass of {@link net.sf.mmm.util.nls.base.AbstractResourceBundle} that declares public string constants: * *
 * package foo.bar;
 *
 * public class FooBarResourceBundle extends {@link net.sf.mmm.util.nls.base.AbstractResourceBundle} {
 *   public static final String MSG_SAY_HI = "Hi {name}! How are you?";
 *   public static final String ERR_LOGIN_IN_USE = "Sorry. The login \"{login}\" is " +
 *     "already in use. Please choose a different login.";
 * }
 * 
* * From your code you only need to create the {@link net.sf.mmm.util.nls.api.NlsMessage NlsMessage}
using this * constants: * *
 * String userName = "Lilli";
 * {@link net.sf.mmm.util.nls.api.NlsMessage} msg = {@link net.sf.mmm.util.nls.api.NlsAccess#getFactory()
 * }.{@link net.sf.mmm.util.nls.api.NlsMessageFactory#create(String, String, Object)
 * create}(FooBarResourceBundle.MSG_SAY_HI, "name", userName);
 * String textDefault = msg.{@link net.sf.mmm.util.nls.api.NlsMessage#getLocalizedMessage() getLocalizedMessage}());
 * String textDe = msg.{@link net.sf.mmm.util.nls.api.NlsMessage#getLocalizedMessage(java.util.Locale)
 * getLocalizedMessage}({@link java.util.Locale}.GERMANY));
 * 
* * For the error message create an exception like this: * *
 * public class LoginAlreadyInUseException extends {@link net.sf.mmm.util.exception.api.NlsRuntimeException} {
 *   public LoginAlreadyInUseException(String usedLogin) {
 *     super(MyResourceBundle.ERR_LOGIN_IN_USE, toMap(KEY_NAME, usedLogin));
 *   }
 * }
 * 
* * For the automatic reverse-lookup create the file {@code META-INF/net.sf.mmm/nls-bundles} with the fully qualified * name of your bundle-class (foo.bar.FooBarResourceBundle) as content.
* For localization you can create property files as described above in the recommended approach. * * In order to support you with creating and maintaining the localized properties, this solution also comes with the * {@code net.sf.mmm.util.nls.base.ResourceBundleSynchronizer}. *

Conclusion

As we have seen the NLS provided here makes it very easy for developers to write and maintain * internationalized code. While messages are created throughout the code they only need to be localized for the * end-user in the client and at service-endpoints. Only at these places you need to figure out the users locale (see * {@link net.sf.mmm.util.session.base.UserSessionProvider}). *
    *
  • The {@link net.sf.mmm.util.nls.api.NlsMessage} allows to store an internationalized message together with actual * arguments to fill in.
  • *
  • The arguments can be arbitrary objects including {@link net.sf.mmm.util.nls.api.NlsMessage}s.
  • *
  • There are powerful ways to format these arguments including variable expressions for optional arguments or plural * forms. See {@link net.sf.mmm.util.nls.api.NlsMessage} for advanced examples.
  • *
  • In addition to numbered arguments that have been deprecated we also support named arguments. This makes * maintenance of the messages a lot easier. Your localizers will love you for choosing this solution.
  • *
  • Resource bundle properties are read in UTF-8 encoding making it easier for localizers as they do not have to * escape characters to unicode number sequences.
  • *
  • The localization (translation to native language) is easily performed by * {@link net.sf.mmm.util.nls.api.NlsMessage#getLocalizedMessage(java.util.Locale)}.
  • *
  • For exceptions there is additional support via {@link net.sf.mmm.util.exception.api.NlsThrowable}.
  • *
*/ package net.sf.mmm.util.nls.api;




© 2015 - 2024 Weber Informatics LLC | Privacy Policy