I have a JSF 2.0 application which allows the user to change the site's language which should affect both texts and images.
Currently the locale is set in a session bean and each page has with locale set from this session bean. It works great for texts. But I have trouble with images. Currently we are using images like this:
<h:graphicImage name="flag.gif" library="img">
This leads to generation of the following HTML code returned to the user agent:
<img src="/AppRoot/faces/javax.faces.resource/flag.gif?ln=img" .... />
Let's assume that the user requests the page in English. The GET request for the image above is processed by ResourceHandler.handleResourceRequest(). It uses ViewHandler.calculateLocale() to identify the correct locale prefix. I have implemented my own ViewHandler with calculateLocale() that retrieves the locale from the user's session. As a result it correctly creates a resource instance which points to "/resources/english/img/flag.gif". Then the user changes his/her locale to french. When the page is reloaded, the same image URL is rendered and requested. This time ViewHandler.calculateLocale() returns Locale.FRENCH to the ResourceHandler which results in the creation of resource with path "/resources/french/img/flag.gif".
Before streaming the image, according to the specification, the ResourceHandler.handleResourceRequest() must do the following:
•Call Resource.userAgentNeedsUpdate(javax.faces.context.FacesContext). If this method returns false, HttpServletRequest.SC_NOT_MODIFIED must be passed to HttpServletResponse.setStatus(), then handleResourceRequest must immediately return.
It detects that the resource is not updated since the previous browser request - not taking into account that the previous request to this "logical" URL lead to different "physical" resource on the server. And returns HTTP 304 which leads to the previous English image displayed again to the user.
If the page is refreshed with Shift+F5, the french image is correctly downloaded since no "If-Modified-Since" is sent by the user agent.
There is always the possibility to add the locale prefix manually with EL in the library name like this:
<h:graphicImage library="#{userContext.locale}/img" name="flag.gif" />
But I still think that the former approach should work and is cleaner.
I am wondering:
Why doesn't JSF produce a "src" that is the actual path to the image in case we use "name" and "library" attributes for ? JSF has all the information to build the full path on the initial page request - including the locale form the UIViewRoot (no need to implement my own ViewHandler). My assumption is that it is because according to the specification resources can also be put in a JAR in the class path. Still the url to the servlet could be rendered only for retrieving the class path located resources for which no direct url can be given.
Why does the specification state that the generated image "src" attribute should include the library but says nothing about the locale prefix (see Resource.getReqestPath())? The Image src is retrieved by Resource.getRequestPath(). If the prefix was included in the URL french and english images would not be interpreted by the browser as a single "modified" resource.
Any ideas are welcome!
Actually, you can just as well use regular HTML <img>
tag and build your own path.
It seems that it is best to create your own Controller for resolving paths:
<img alt="#{i18n['some.image.title']}" src="#{localizationController.someImage}" />
Localization Controller could read the context path (the base URL of the current application) like this:
String basePath = FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath();
ExternalContext also provides real path name (i.e. the one on disk, if you use exploded format) – see getRealPath()
method, should you ever needed.
Why I prefer this method? It is extremely simple and pretty powerful: one can use Localization Controller not just to provide paths to localized objects like CSS files (or at least main style sheet overrides), client-side scripts and of course images, but also to actually provide dynamic localizable context (i.e. arrays of translatable strings in JavaScript).
To answer your specific questions:
The short answer is I don't know. I'd love to think about it that somebody who designed this API carefully weighted all pros and cons and chose optimal solution (although not satisfying every possible Use Case).
For that I can give you precise answer. Basically, correctly Internationalized application should not contain any image that might depend on Locale, i.e. be culture-specific. This is quite idealistic view of the world but to be honest having image that actually depends on Locale should be rare case.
If I understood your specific problem, you want to switch some specific country flag based on current Locale. In fact, you can just as well use conditional rendering here (render="#{someController.someBooleanMethod}"
) and actually write all image references at once. I know it sucks but this is one viable solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With