问题
I've been joyfully using omnifaces' Faces.getLocale()
to aquire the locale used by the currently logged in user (which in turn gets this from a <f:view>
definition). I really like the fallback approach from view to client to system default locale as it fits the requirements for locale selection in my application:
- If a user is logged in, use his language preference (obtained from the backend entity)
- If no user preference can be found, use the highest ranking language from the
Accept-Languages
HTTP header - If no locale has been selected by now, use the system default.
Now I've started using JAX-RS (resteasy implementation) and find it quite difficult to write a service that will provide my backend code with the current user's locale.
I can't use Faces.getLocale()
, since that requires a FacesContext
which isn't present during JAX-RS request processing.
I can't use the @Context SecurityContext
annotation in a @Provider
(which would give me the user preferred locale) or @Context HttpHeaders
(access to the client locale) since JAX-RS only injects those when it uses the provider itself, not when my backend code instantiates the class.
And I don't want to litter my method signatures with Locale
parameters, since virtually everything requires a locale to be present.
To have a concrete example: I have a vcard generator that generates little NOTE fields depending on the user's preferred locale. I can both call the vcard generating method via JSF/EL:
<h:commandLink action="#{vcfGenerator.forPerson(person)}"
value="Go" target="_blank" />
And via a REST service:
@GET @Path('person/{id:[1-9][0-9]*}/vcard')
@Produces('text/vcard')
String exportVcard(@PathParam('id') Long personId, @Context HttpHeaders headers) {
VcfGenerator exporter = Component.getInstance(VcfGenerator) as VcfGenerator
Person person = entityManager.find(Person, personId)
if (! person)
return Response.noContent().build()
def locale = headers.acceptableLanguages[0] ?: Locale.ROOT
return exporter.generateVCF(person, locale).toString()
}
This works (VcfGenerator
has a set of JSF-only methods that use Faces.getLocale()
), but is a pain to maintain. So instead of passing the Locale
object, I'd like to say:
Vcard generateVCF(Person person) {
Locale activeLocale = LocaleProvider.instance().getContext(VcfGenerator.class)
ResourceBundle bundle = ResourceBundle.getBundle("messages", activeLocale, new MyControl())
// use bundle to construct the vcard
}
Has anyone done similar work and can share insights?
回答1:
I know this has been posted a while ago, but as it has not been marked as resolved, here is how I got a workaround working for this specific case:
- First I got a custom ResourceBundle working, as @BalusC described here: http://balusc.blogspot.fr/2010/10/internationalization-in-jsf-with-utf-8.html
Then I updated the constructor in order to detect if a FacesContext is currently being in use, from this :
public Text() { setParent(ResourceBundle.getBundle(BUNDLE_NAME, FacesContext.getCurrentInstance().getViewRoot().getLocale(), UTF8_CONTROL)); }
To This:
public Text() { FacesContext ctx = FacesContext.getCurrentInstance(); setParent(ResourceBundle.getBundle(BUNDLE_NAME, ctx != null ? ctx.getViewRoot().getLocale() : Locale.ENGLISH, UTF8_CONTROL)); }
This now works both in JSF and JAX-RS context.
Hope this help,
来源:https://stackoverflow.com/questions/18594566/implementing-a-locale-provider-that-works-in-jsf-and-jax-rs