Resource bundles are implemented in the JDK class java.util.ResourceBundle.
The Javadoc
for ResourceBundle
provides a wealth of information regarding the usage of resource
bundles. This document will repeat some of the information in the
Javadoc, then explain the text2gui library's own system for creating
resouce bundles which provides additional capabilities.
Resource Bundles as Implemented by java.util.ResourceBundle
Resource
Bundles 的 實
施
以 java.util.ResourceBundle
What Resource Bundles Do Resource Bundles 能 做什麼 ?
Basically, resource bundles are used to provide a map from string keys
to values that depend on the desired locale.
基本上, resource bundles 是使用於提供一張圖表, 從詞句得到依照你所想要的地點的價值,.
Resource bundles are
usually read in from properties
files that look like this:
Resource bundles 通常是從properties的
文件中讀入, 就像下列 如此 ;
key1=value1 key2=value2 # .... '#' starts a comment --
more key / value pairs
follow
Assuming the above file backs the resource bundle bundle, a call to bundle.getObject("key1") would
return the string "value1".
假 設是 以上的
文件供應resource bundle bundle,當程式員召喚 bundle.getObject("key1"),
resource bundle bundle 將會送回
詞句 "value1".
Where to Put Resource Bundles
在那裡存放 Resource Bundles ?
Let's say that the bundle is to be named com.acme.junk.MyResourceBundle.
Commonly, the name of the resource bundle has the same name qualifier (com.acme.junk. in this example) as
the code that uses it. So presumably, the bundle is going to be used by
some class in the package com.acme.junk.
Resource bundles are searched by changing all the dots to slashes, then
appending the changed name to each path in the class path. Since the
path containing the com.acme.junk
package is going to be in the classpath when a class in the package is
executed, the resource bundle should
be saved in the same directory as the class that uses it.
Because the file above is a properties
file, it should be given the name "MyResourceBundle.properties".
讓我們將 bundle命名為com.acme.junk.MyResourceBundle 。通常的 resource
bundles的名字有同樣的命名合格者(在這個例子是com.acme.junk. ).
我們就使用它做為電碼。那麼據推測, 這個bundle將被包裹com.acme.junk裡的一些類型使用。Resource
bundles被搜尋,以改變所有小點成叉,
然後在classpath的每個path中添附被更改的名字。因為path包含com.acme.junk 包裹是在classpath
當同類的在包裹被執行, resource bundles應該被保存在用它的類型的目錄。由於文件上面是properties文件,
它應該被命名為"MyResourceBundle.properties" 。
Class Resource Bundles
類型 Resource Bundles
Another way that resource bundles can be created is by compiling a Java
class that extends java.util.ResourceBundle.
The fully qualified class name must be exactly the same as the name of
the resource bundle. For example, we could compile a class named OtherResourceBundle in the package foo.bar. Then we could retrieve the
bundle by the name "foo.bar.OtherResourceBundle".
The advantage of resource bundles implemented by class files is that
they can vend non-string objects for values. The disadvantage is that
they must be
recompiled whenever changed. For this reason, code that uses the
text2gui library will almost always backs resource bundles with properties files.
以其它方式, resource bundles可能被創造是由編輯延伸java.util.ResourceBundle 的Java
class。這完全合格類型的名稱必須與resource bundles的名稱是如出一轍。例如,
我們能編輯此類型被命名為OtherResourceBundle 在包裹foo.bar
。然後我們能以"foo.bar.OtherResourceBundle"的名稱來尋回bundle 。以類型實施的resource
bundle的好處是
所以它們能提供
非字句的對象為價值。缺點是, 每次改變時它們必得再次編輯 。因此使用text2gui library的電 碼 總是以properties 的文件支持
resource
bundles。
Retrieving Resource Bundles
尋回 Resource Bundles
How exactly do we retrieve a bundle by name? Using the java.util package, we can call ResourceBundle.getBundle(String name,
Locale locale), or simply ResourceBundle.getBundle(String
name) if we want to use the default locale. In our above
example, the line
我們如何正確的以名稱尋回 bundle ? 以 java.util
的
包裹
, 我們 可以召喚 ResourceBundle.getBundle(String
name,
Locale locale),
或是假如我們想用原有的地點,可以簡
單的用 ResourceBundle.getBundle(String
name).
Value Inheritance / Overriding
Mechanism
價值繼承/ 凌駕的機制
How does a resource bundle vend different values depending on the
locale? It's a bit complicated, but basically, the locale passed to ResouceBundle.getBundle(String name,
Locale locale) determines what other bundles to look for. If
say, the locale was for Japan, the additional bundles com.acme.junk.MyResourceBundle_ja
and com.acme.junk.MyResourceBundle_ja_JP
would be also be loaded if present. ("ja" is the language code for
Japanese, and "JP" is the country code for Japan). Now a inheritance
hierarchy is created:
(Actually this is only a subset of the inheritance hierarchy -- see java.util.ResourceBundle for the
real details).
(實際上這只是繼承階層的子集--請看 java.util.ResourceBundle
的 細節)
When a key is looked up with a call to bundle.getObject(), the most
specific bundle is checked first. In this case we would check com.acme.junk.MyResourceBundle_ja_JP.
If the key is defined, it is returned. Otherwise, it checks the next
most specific bundle, com.acme.junk.MyResourceBundle_ja.
This procedure is repeated until all the bundles are searched. The
general idea is that keys defined in more specific bundles override
those defined in less specific bundles. This is a powerful mechanism
because it allows locale-specific keys to be overridden while
locale-independent keys are inherited.
An Example
舉個例子
As an example, we put 3 files in the same directory, which is in our
classpath.
我們將三個文件放在同一目錄,它是在我們的類型的路線裡
HelloResourceBundle_ja.properties:
language=Nihongo hello=Konnichi wa!
HelloResourceBundle.properties:
language=English hello=Hello! email=loser@msn.com
Hello.java:
import java.util.ResourceBundle;
public class Hello { public Hello() {
bundle = ResourceBundle.getBundle(getClass().getName() +
"ResourceBundle"); }
public static void
main(String[] args) { Hello h
= new Hello();
h.sayIt(); }
ResourceBundle bundle; }
Running "java Hello" on a computer in America produces:
使用美國產品的電腦 執行 "java Hello"
ENGLISH Hello! from loser@msn.com
This is because no resource bundle are available for "Hello_en" or
"Hello_en_US". ("en" is the language code for English). Only the base
resource bundle is available, so all values come from there.
這是因為沒有resource bundle是可用的 "Hello_en" 或"Hello_en_US" 。("en" 是語言代碼為英語)
。唯一基本的resource bundle是可用的, 因此所有價值來自那裡。
Running "java Hello" on a computer in Japan produces:
使用日本產品的電腦 執行 "java Hello"
Nihongo Konnichi wa! from loser@msn.com
since "Hello_ja.properties" is searched before "Hello.properties". The
last line is the same because the email
key was not overridden.
從"Hello_ja.properties" 被搜尋在"Hello.properties" 之前; 。最後線是相同的因為電子郵件的詞句並未凌駕。
To get the same effect on any computer in the world, we need to specify
that we want the locale for Japan instead of the default locale:
Also, notice how the name of the bundle was constructed. We use the
fully qualified class name, followed by "ResourceBundle". In this
example, this would resolve to "HelloResourceBundle",
since the class is in the anonymous package. If we changed the package
to com.acme.junk, and
assuming we move the to the corresponding directory,
the above line would use the name "com.acme.junk.HelloResourceBundle",
as desired. So getting resource bundles like this protects the code
from requiring additional changes if you decide to change packages.
並且, 注意怎麼bundle的名稱被建立了.我們使用完全合格的分類名, 被"ResourceBundle" 接著; 。在這個例子,
這會解決"HelloResourceBundle",
因為類型是在匿名包裝裏。如果我們改變了包裹到com.acme.junk, 和假設我們移動resource
bundles向對應的目錄,
上述的線會使用正如你想要的名字"com.acme.junk.HelloResourceBundle", 。如果您決定改變包裹,
如此得到resource bundles像這樣保護代碼免受要求另外的變動。
properties syntax properties句法
We already created and used resource bundles based on properties files, but actually
the syntax is a bit more complicated than just plain text. So that properties files can support
text in multiple languages, the
properties file is encoded in a special format which allows for
escape sequences that describe Unicode characters. In this format, the
characters '\', ':', '=', '!' and '#' must be escaped with a
backslash. Also, properties files can contain comments -- lines
beginning with an unescaped '#'
or '!' are ignored. Here
is a snippet that demonstrates the syntax:
# This is a comment. Everything
on this line will be thrown away! truism=2 + 3 \= 5\!
If the above file is used to back are resource bundle, the key "truism" will map to the string
"2 + 3 = 5!".
如果上述文件被使用支持resource bundle, 這詞句"truism" 將以"2 + 3 = 5!"表述 。
請看Javadoc 為java.util.Properties 對於更多資訊關於properties句法-- 但注意text2gui
使用輕微地修改過的句法叫的多行的properties,
以後再在來描述。
Here's a hint for i18n developers on Windows systems. It's easiest to
enter in foreign text with an Input Method Editor (IME) installed in
Windows. A properties
file that provides text for a foreign language can initially be edited
with Notepad. When saving the file, save using the Unicode (NOT Unicode big endian) encoding.
Let's assume you give the file the name "infile.props". Then use the native2ascii
utility supplied with the Java Development Kit like this:
This will output the file "MyResourceBundle.properties" that has the
characters in "infile.props", but properly encoded for use by the Java
library classes.
這將輸出文件"MyResourceBundle.properties" 那兒有字符在"infile.props", 但是Java
library
classes 適當的加碼以供使用。
text2gui Extensions to Resource Bundles
Resource bundles are a great idea, but their implementation as is
leaves some features to be desired. These features, described below,
have been implemented in the text2gui library.
Resource bundles是一個好主意,但他們的實施, 就像葉子的一些特點被渴望, 。在以下描述,這些特點, ,
被實施在text2gui library裡。
Extension of the Inheritance Hierarchy
Inheritance Hierarchy的延伸
First of all, the inheritance hierarchy as implemented by ResourceBundle.getBundle() only
includes resource bundles with the same base name (HelloResourceBundle in the above
example). However, we might want to inherit key / values from a
resource bundle with a different name. Such a bundle might contain
strings used in a variety of applications, such as "OK", "Cancel",
"Yes", "No". Of course, these strings would change depending on the
locale passed to the bundle creation method. Let's call this bundle foo.bar.CommonResourceBundle and
define it as:
首先繼承階層依照由ResourceBundle.getBundle()
實施,只有包含resource bundles用同樣基本的名字(HelloResourceBundle 在上述例子) 。但是,
我們也許想要繼承詞句/價值從resource resource bundle以一個另外的名字。這樣resource
bundle也許包含字句被使用在各種各樣的應用, 譬如"OK", "Cancel",
"Yes", "No" 。當然, 這些字句會改變根據地點通過對 bundle創作方法。Let's
叫這bundle foo.bar.CommonResourceBundle把它定義為
: ok=OK cancel=Cancel yes=Yes no=No
We also have a Spanish version with simple name
"CommonResourceBundle_es": 我們也有西班牙版的用簡名"CommonResourceBundle_es":
ok=Acepta cancel=Cancele yes=Sí
We need a way for a bundle such as AudioPlayerResourceBundle
to inherit the keys of foo.bar.CommonResourceBundle.
To do this we set the parentBundle
property in AudioPlayerResourceBundle:
我們需要一個方式為bundle譬如AudioPlayerResourceBundle
繼承詞句foo.bar.CommonResourceBundle 。為了做這我們設置parentBundle
property在AudioPlayerResourceBundle:
parentBundle=foo.bar.CommonResourceBundle
play.text=Play # ... more properties here
There is also a Spanish version in "AudioPlayerResourceBundle_es": 我們也有西班牙版的在"AudioPlayerResourceBundle_es":
play.text=Toca # ... más properties
aquí
Now we just need a way to get a resource bundle object that recognizes
the parentBundle property
and constructs an inheritance hierarchy like this, assuming the desired
locale is for Argentina:
我們現在需要一種方式得到認可parentBundle 物產和修建繼承階層像這樣的resource bundle對象, 假設渴望的地點是為阿根廷:
com.taco.util.ChainedResourceBundleFactory
does just this, so a bundle with this hierarchy can be retrieved by
calling
com.taco.util.ChainedResourceBundleFactory 就像這樣做, 因此bundle以這個階層可以召喚招來
bundle =
ChainedResourceBundleFactory.DEFAULT_INSTANCE.getBundle("AudioPlayerResourceBundle",
new Locale("es", "AR"));
Now bundle.getObject("Play")
returns "Toca", while
retbundle.getObject("yes")returns
"Sí".
現在bundle.getObject("Play")帶回"Toca",而retbundle.getObject("yes")帶回 "Sí".
com.taco.util.ChainedResourceBundleFactory
also has the ability to create a bundle with a hierarchy
specified by string. The string is composed of bundle names,
separated by semicolons. Bundles names occuring earlier in the string
have their corresponding bundles checked before bundles specified later
in the string. For example,
creates a hierarchy like this:
創造一個階層像這樣: AudioPlayerResourceBundle_zh_TW AudioPlayerResourceBundle_zh AudioPlayerResourceBundle foo.bar.CommonResourceBundle_zh_TW foo.bar.CommonResourceBundle_zh foo.bar.CommonResourceBundle OtherResourceBundle_zh_TW OtherResourceBundle_zh OtherResourceBundle com.acme.junk.WastedResourceBundle_zh_TW com.acme.junk.WastedResourceBundle_zh com.acme.junk.WastedResourceBundle Note the appearance of foo.bar.CommonResourceBundle
even though it wasn't specified in the string. The parentBundle property is still
respected. foo.bar.CommonResourceBundle
and its locale-specific friends are considered part of AudioPlayerResourceBundle so it is
checked before bundles later in the string.
注意foo.bar.CommonResourceBundle 的出現即使它不是
被指定在詞句裏。parentBundle
物產仍然是受尊敬的foo.bar.CommonResourceBundle
並且它的地點具體朋友們被認為是一部分的AudioPlayerResourceBundle 因此它被檢查在bundle之前而在詞句之後。
The parentBundle property
also may be set to a ';'
separated list of resource bundles, to get multiple inheritence. If
multiple resource
bundles inherit from a common resource bundle, each common resource
bundle will only be searched once. Also there is no danger of infinite
recursion if a bundle inherits from itself, directly or indirectly.
parentBundle property
也許還被設置對';' resource bundles被分離的名單, 得到多繼承 。如果多種resource
bundles繼承從普遍的resource bundle, 各個普遍的resource
bundle只將被搜尋一次。並且如果是bundle繼承從本身,沒有直接地或間接地無限遞歸的危險 。
Support for Different Implementations of ResourceBundle
對於不同的ResourceBundle實施的維護
Another limitation of ResourceBundle.getBundle()
is that there are only two ways that a resource bundle can be loaded
其它ResourceBundle.getBundle() 局限是, 只有二種方式resource
bundle可能被裝載::
By finding a properties
file
By finding a Java class
1.以找尋properties文
件
2. 以找尋Java 類型
Furthermore, the syntax for a properties
file is not ideal for code
segments. In the syntax, every property value that takes multiple lines
needs to use a line continuation marker '\' as the very last character
on each line before the last one. Because the text2gui library relies
heavily on BeanShell scripts and many other long strings as property
values, the ordinary properties
file syntax would be overly burdensome
to the programmer. A modification of the properties file syntax was
created, called multi-line properties.
The multi-line properties
syntax
is different from the properties syntax in two ways:
If a property value ends the line within a Java braced context
(inside an unclosed ", ', (, {, or [), the next line is
automatically concatenated with the last line. This process continues
until all Java punctuation has been closed. Of course, brace characters
that occur in a quoted or commented context are not treated as changing
the brace
level. Also, a brace character can be escaped with a backslash ('\') to indicate that it does
not start a braced context .
If a non-escaped backslash ('\')
is detected, and only whitespace
follows it, it will be treated as a line continuation marker. This
alleviates the frustration of ensuring the backslash is the very last
character of a line that is followed by another one.
These two modifications make writing multi-line property values
considerably easier. Now we can write:
這兩種修改使文字多行的物產價值可觀地更加容易。現在我們能寫道:
okButton.actionListeners.0={ return new
ActionListener() {
public void actionPerfomed(ActionEvent event) {
// Assume the dialog that this button belongs
to is in the global "dialog". getGlobal("dialog",
argMap).dispose();
} }; }
which would use the entired contents of the braces, including
the braces themselves, as the property value for the
okButton.action key, because the string remains in a braced
context until the last line.
By default, com.taco.util.ChainedResourceBundleFactory
interprets properties
files it finds using com.taco.util.MultiLineProperties,
which supports the multi-line syntax described above.
If you have existing properties
files, the vast majority of them will be interpreted in the same way
using the multi-line syntax. The only problem to watch out for is lines
containing unclosed opening brace characters, which will make the lines
after them all be part of the same property value, until a closing
brace character is found. To work around this, escape brace characters
with a backslash ('\') if
you don't want them to start a braced context.
如果您有現有的properties文件,
大多數他們將被解釋相似使用多行的句法。唯一的問題提防是包含沒關閉的 開頭括號字符,
將做在他們以後全部是同樣物產價值的一部分的一行, 直到一個關閉的括號字符被發現。想避開這問題, 以斜線('\') 逃脫括號字符,如果您不
要他們開始括號的內文。
The resource bundle factory classes in the text2gui also provide
support for an additional implementation of ResourceBundle: one based on javax.swing.UIManager, which holds
icons, borders, etc. for the current UI. This resource bundle contains
all key / value pairs in UIManager,
for which the key is a string. To retrieve this bundle, use "UIManager" as the bundle name.
這個resource bundle工廠類型在text2gui 並且為ResourceBundle提供支持另外的應用:
你根據了javax.swing.UIManager, 它持有小圖像、邊線等 等,
為當前的UI 。這resource bundle包含所有詞句或是/價值成 對的在UIManager中,
詞句是詞句。在此詞句就是詞句.想要檢索這bundle,使用"UIManager" 作為bundle的名稱.
Finally, through subclassing, it is possible to provide your own
implementation of ResourceBundle
based on a name. One application of this ability might be to provide an
implementation of ResourceBundle
based on key / values defined in an XML file, if the properties syntax does not suit
you. See the Javadoc for com.taco.util.ResourceBundleFactory._loadOrphanBundle() for details
on how to do this.
終於, 通過次類型, 它是可能提供您自己
以名稱為根據,ResourceBundle的應用。這能力的一種應用也許將提供ResourceBundle 的實施根據詞句或/價值被定義在XML
文件,
如果properties句法不適合您。關於怎樣對做這,
請看Javadoc
為com.taco.util.ResourceBundleFactory._loadOrphanBundle() 的細節。
Bundle Cache Invalidation
Bundle Cache 的失效
A feature built into java.util.ResourceBundle.getBundle()
is resource bundle caching. That is, if a bundle name is requested more
than once, the same bundle is returned. This avoids the expensive
process of reloading the bundle, which may involving parsing a file or
loading a class. This is normally a good thing, but consider an
application whose GUI is defined by a resource bundle. While it's
running, once it loads the bundle, that bundle cannot change. If the
application allowed the user to upgrade its interface by specifying
overwriting the file backing the bundle, or if a programmer edited the
file backing the bundle, those changes cannot take effect until the
application was restarted.
一些功能被建立入java.util.ResourceBundle.getBundle() 是resource bundle
caching。那是如果bundle名字被請用多於一次,
同樣的bundle返回。這樣避免再裝bundle的昂貴的過程, 它可以介入解析文件或裝載類型。這通常是一件好事, 但考慮GUI
由resource bundle定義的應用。當它在執行時, 一旦它裝載bundle,
bundle無法改變。如果應用允許用戶升級它的交接,由指定重寫文件以支持bundle, 或如果程式員編輯了這文件以支持bundle,
那些變動就無法採取作用直到應用被重新開始了。
The text2gui library's resource bundle factories also cache resource
bundles, but they allow the bundle cache to be invalidated, so that the
next load of a bundle re-reads the file backing the bundle. This can be
done by callingChainedResourceBundleFactory.invalidateBundles().
Now that bundles can replaced at run-time, applications can upgrade
themselves without restarting.
Summary
結論
Resource bundles are a powerful way of providing values at runtime.
Values can easily be overridden depending on the locale, so resource
bundles are an ideal solution to internationalization problems. With
the extensions in the text2gui library, several bundles can be combined
in a structured way and alternate syntaxes for describing bundles can
be supported. One extremely useful alternate syntax, multi-line
properties, is already
built into the text2gui system for loading
bundles. com.taco.util.ChainedResourceBundleFactory
is the only class that most programmers will need to access
these features.