在 JSF 中 - 获取客户端的区域设置(在浏览器的时区中显示时间)

2022-01-11 00:00:00 localization javascript jsf jsf-2

我正在用 JSF 2.0 编写一个 webapp,它将时间戳作为信息的一部分显示给用户.我希望用户看到本地化到他们的位置(浏览器的语言环境)的时间戳.

I'm writing a webapp in JSF 2.0 that displays Timestamps as part of the information to the user. I would like the user to see the timestamps localized to their location (browser's locale).

到目前为止,无论我做什么,时间戳都会显示为本地化到服务器的时区.

So far, whatever I did, the timestamps would always appear localized to the timezone of the server.

我尝试使用以下方法获取语言环境:

I tried getting the locale with these methods:

Locale browserLocale = FacesContext.getCurrentInstance().getViewRoot().getLocale();

Locale browserLocale = FacesContext.getCurrentInstance().getExternalContext().getRequestLocale();

两者都返回了服务器的语言环境.

Both returned the server's locale.

然后我使用带有 SimpleDateFormat 的语言环境对象来打印时间戳.

I then use the locale object with SimpleDateFormat to print timestamps.

我是否使用了正确的 API?
我在某处读到您必须使用客户端代码(Javascript)来获取浏览器的时区.那是对的吗?你会怎么做?

Am I using the correct API?
I've read somewhere that you have to use client side code (Javascript) to get the browser's timezone. Is that correct? How would you do that?

感谢'n' Advance.

Thank's 'n' Advance.

更新发现 this (jsTimezoneDetect) JavaScript 代码.但我仍然不确定如何将时区转换为 Java Locale Object

UPDATE found this (jsTimezoneDetect) JavaScript code. But I'm still not sure how to translate the timezone to a Java Locale Object

推荐答案

您可能应该使用内置格式化标签而不是 SimpleDateFormat.您的问题暗示您想向国际用户显示日期和时间,在这种情况下,您应该真正使用用户的本地格式(您知道,它们往往不同).

You probably should use built-in formatting tag instead of SimpleDateFormat. Your question implies that you want to show date and time to International user, and in this case you should really use user's local format (they tend to differ, you know).

就时区而言,它与国际化和本地化无关,即美国有几个不同的时区.您可以在这里使用两种方法:

In case of time zone, it has nothing to do with Internationalization and Localization, i.e. there are several different time zones in USA. There are two approaches you can use here:

  1. 在用户配置文件中存储时区信息(如果有的话).这是最简单的方法,允许您使用内置的 <f:convertDateTime> 标签.

从网络浏览器获取时区信息.您可以像 Ben 的示例一样通过 Ajax 请求获取它.从技术上讲,您也可以在此处使用 <f:convertDateTime> 标签.

Get time zone information from web browser. You can get it via Ajax request just like in Ben's example. Technically you can also use <f:convertDateTime> tag here.

您可以以某种常见的、与区域设置无关(或者如果您愿意,也可以是不变的)格式发送 UTC 时间戳,在客户端对其进行解析以创建 JavaScript 的日期对象和带有 全球化.

You can send the timestamps in UTC in some common, locale-independent (or invariant if you prefer) format, parse it on the client side to create JavaScript's date object and format for locale with Globalize.

下面会有一些例子,但让我先解释一下.

Some examples will follow but let me explain something first.

Locale browserLocale = FacesContext.getCurrentInstance().getViewRoot().getLocale();

这将为您提供网络浏览器的区域设置(但不是时区,因为这与区域设置不相关).它实际上会读取 HTTP Accept-Language 标头的内容并选择最佳的语言环境.如果它不适合您,请确保您在 faces-config.xml 中正确设置了支持的语言环境.通过最好的语言环境,我知道它将尝试使用用户最喜欢的语言环境(如果您的应用程序支持),然后是第二好的,依此类推.如果不支持任何区域设置,它将回退到您的应用程序的默认区域设置(同样,faces-config.xml 必须具有此设置)或如果缺少此设置(或者至少我是这么认为的,这有点道理).

This will give you web browser's locale (but not time zone, since this is not locale related). It will actually read the contents of HTTP Accept-Language header and choose the best possible locale. If it is not working for you, please make sure that you have correctly set supported locales in your faces-config.xml. By best possible Locale, I understand that it will try to use user's most preferred Locale (if that's supported by your application), then second best and so on. If none of the Locales is supported, it will fall-back to your application's default Locales (again, faces-config.xml has to have this setting) or to server's default Locale if this setting is missing (or at least I think so, it kind of makes sense).

Locale browserLocale = FacesContext.getCurrentInstance().getExternalContext().getRequestLocale();

这将为您提供来自 Accept-Language 标头的顶级语言环境.请检查您的网络浏览器的设置 - 它几乎无法为您提供服务器区域设置,除非它们与您的网络浏览器完全相同.当且仅当 JVM 不支持任何 Web 浏览器的 Locale(这似乎不太可能)时,它才能为您提供服务器的默认值.

This one will give you the top Locale from Accept-Language header. Please check your web browser's settings - there is almost no way for it to give you the server Locale, unless they are exactly the same as your web browser's. It can give you server's defaults if and only if, none of the web browser's Locale is supported by JVM (which seems a bit unlikely).

顺便说一句.FacesContext.getCurrentInstance().getExternalContext().getRequestLocales() 将为您提供迭代器,以便您可以手动迭代 Accept-Language 标头中的区域设置列表.只是为了让你知道,你可能不应该使用它(UIViewRoot 真的足够了).

BTW. FacesContext.getCurrentInstance().getExternalContext().getRequestLocales() will give you the Iterator so you can manually iterate through the list of Locales in Accept-Language header. It is just to let you know, you probably should not use it (UIViewRoot is really enough).

现在,假设您有一些 bean,其中包含用户配置文件和为您提供时区的方法.这种方法比 Ajax 调用更好,因为两个不同的时区可能具有相同的 UTC 偏移量,但在不同的日期切换夏令时(换句话说,某些时间戳会打印不正确).无论如何,在这种情况下,您可以像这样格式化您的时间戳(日期也来自某个 bean):

Now, suppose you have some bean with the user profile and the method which will give you the time zone. This method is better than Ajax call, in the sense that it might happen that two different time zones have the same UTC offset but switch Daylight Saving Time on different date (in other words some timestamps would be printed incorrectly). Anyway, in case like this, you can format your time-stamp like this (date also come from some bean):

<h:outputText value="#{someBean.timestamp}">
  <f:convertDateTime type="both" dateStyle="default" timeStyle="default" timeZone="#{userProfile.timeZone}" />
</h:outputtext>

现在让我解释一下属性:

Now let me explain the attributes:

  • type - 要显示的内容,均表示日期和时间
  • dateStyle - 日期样式(短、中、长、完整、默认).您真的应该在这里使用默认值,因为这将为每个区域设置使用最合适的格式
  • timeStyle - 类似于日期样式,但用于时间部分
  • timeZone - 采用 UTC 偏移量(因此您无需转换任何内容)或时区名称(即 America/Los_Angeles).

标签默认使用当前视图语言环境,所以你不必担心这部分,特别是如果你正确设置了语言环境支持.

The tag will use current view Locale by default, so you do not have to worry about this part, especially if you set up Locale support correctly.

将它与 Ajax 相结合(请参阅 Ben 的回答)会很容易(我认为).

Combining it with Ajax (see Ben's answer) would be easy enough (I think).

我还提到你可以写出不变的日期,在客户端解析它们,然后用 Globalize 格式化它们.假设您已经解析了日期(这取决于您要使用的表示形式,因此我将跳过这部分),可以这样做:

I also mentioned that you can write out invariant dates, parse them on the client-side and then format them with Globalize. Assuming that you have parsed date already (it depends on the representation you want to use, so I will skip this part), it could be done like that:

// you also have to assign the culture based on UIViewRoot locale and send it out with JavaScript
Globalize.culture(theLocale);
var formattedDateTime = Globalize.format(parsedDateTime, "f"); // this will use short date time format

与 Java 不同,Globalize 只有短 ("f") 和长 ("F") 日期和时间格式.所以我决定使用短的.
还请记住,Globalize 文化是通过连字符而不是下划线分隔的,因此您需要fr-CA",而不是fr_CA".
如果您想使用该方法并需要更具体的示例,请告诉我.

Unlike Java, Globalize have only short ("f") and long ("F") date and time formats. So I decided on using short one.
Please also keep in mind, that Globalize cultures are separated via hyphen, not underscore, so you need "fr-CA", not "fr_CA" for example.
Please let me know if you want to use that method and need more concrete example.

相关文章