i18nタグを追加。
@@ -0,0 +1,34 @@ | ||
1 | +package jp.sf.amateras.functions.utils; | |
2 | + | |
3 | +import java.util.Locale; | |
4 | +import java.util.MissingResourceException; | |
5 | + | |
6 | +import junit.framework.TestCase; | |
7 | + | |
8 | +public class TextUtilsTest extends TestCase { | |
9 | + | |
10 | + public void testGetText_1() { | |
11 | + assertEquals("message1", TextUtils.getText("key1", new Locale("en", "US"))); | |
12 | + assertEquals("メッセージ1", TextUtils.getText("key1", new Locale("ja", "JP"))); | |
13 | + } | |
14 | + | |
15 | + public void testGetText_2() { | |
16 | + assertEquals("message2", TextUtils.getText("key2", new Locale("en", "US"))); | |
17 | + assertEquals("message2", TextUtils.getText("key2", new Locale("ja", "JP"))); | |
18 | + } | |
19 | + | |
20 | + public void testGetText_3() { | |
21 | + assertEquals("key3", TextUtils.getText("key3", new Locale("en", "US"))); | |
22 | + assertEquals("key3", TextUtils.getText("key3", new Locale("ja", "JP"))); | |
23 | + } | |
24 | + | |
25 | + public void testGetText_4() { | |
26 | + TextUtils.setBundleName("hoge"); | |
27 | + try { | |
28 | + TextUtils.getText("key1", new Locale("en", "US")); | |
29 | + fail(); | |
30 | + } catch(MissingResourceException ex){ | |
31 | + assertEquals("Can't find bundle for base name hoge, locale en_US", ex.getMessage()); | |
32 | + } | |
33 | + } | |
34 | +} |
@@ -0,0 +1,20 @@ | ||
1 | +package jp.sf.amateras.functions; | |
2 | + | |
3 | +import javax.servlet.http.HttpServletRequest; | |
4 | + | |
5 | +import jp.sf.amateras.functions.utils.RequestUtils; | |
6 | +import jp.sf.amateras.functions.utils.TextUtils; | |
7 | + | |
8 | +/** | |
9 | + * Webアプリケーションの国際化のための関数を提供します。 | |
10 | + * | |
11 | + * @author Naoki Takezoe | |
12 | + */ | |
13 | +public class I18N { | |
14 | + | |
15 | + public static String text(String key){ | |
16 | + HttpServletRequest request = RequestUtils.getRequest(); | |
17 | + return TextUtils.getText(key, request.getLocale()); | |
18 | + } | |
19 | + | |
20 | +} |
@@ -0,0 +1,74 @@ | ||
1 | +package jp.sf.amateras.functions.utils; | |
2 | + | |
3 | +import java.util.Locale; | |
4 | +import java.util.Map; | |
5 | +import java.util.MissingResourceException; | |
6 | +import java.util.ResourceBundle; | |
7 | +import java.util.concurrent.ConcurrentHashMap; | |
8 | + | |
9 | +/** | |
10 | + * 国際化されたメッセージを取得するためのユーティリティクラスです。 | |
11 | + * | |
12 | + * @author Naoki Takezoe | |
13 | + */ | |
14 | +public class TextUtils { | |
15 | + | |
16 | + private static String bundleName = "messages"; | |
17 | + | |
18 | + private static ResourceBundle defaultBundle = null; | |
19 | + | |
20 | + private static Map<Locale, ResourceBundle> bundleMap = new ConcurrentHashMap<Locale, ResourceBundle>(); | |
21 | + | |
22 | + /** | |
23 | + * 使用するResourceBundleのバンドル名を設定します。 | |
24 | + * デフォルトではmessagesというバンドル名が使用されます。 | |
25 | + * | |
26 | + * @param bundleName バンドル名 | |
27 | + */ | |
28 | + public static void setBundleName(String bundleName){ | |
29 | + TextUtils.bundleName = bundleName; | |
30 | + } | |
31 | + | |
32 | + /** | |
33 | + * 国際化されたメッセージを取得します。 | |
34 | + * <p> | |
35 | + * 引数で指定したロケールに対応するResourceBundleが存在しない場合や、 | |
36 | + * 対応するResourceBundleに指定したキーを持つメッセージが存在しない場合は | |
37 | + * また、デフォルトのResourceBundleで定義されたメッセージが返却されます。 | |
38 | + * キーに対応するエントリが存在しない場合はキーの文字列をそのまま返却します。 | |
39 | + * </p> | |
40 | + * <p> | |
41 | + * デフォルトのResourceBundleが存在しない場合はMissingResourceExceptionがスローされます。 | |
42 | + * </p> | |
43 | + * | |
44 | + * @param key メッセージのキー | |
45 | + * @param locale ロケール | |
46 | + * @return キーに対応するメッセージ | |
47 | + * @throw MissingResourceException デフォルトのResourceBundleが存在しない場合 | |
48 | + */ | |
49 | + public static String getText(String key, Locale locale) throws MissingResourceException { | |
50 | + if(defaultBundle == null){ | |
51 | + defaultBundle = ResourceBundle.getBundle(bundleName, new Locale("")); | |
52 | + } | |
53 | + | |
54 | + ResourceBundle bundle = bundleMap.get(locale); | |
55 | + if(bundle == null){ | |
56 | + bundle = ResourceBundle.getBundle(bundleName, locale); | |
57 | + if(bundle.getLocale().getCountry().equals(locale.getCountry()) || | |
58 | + bundle.getLocale().getLanguage().equals(locale.getLanguage())){ | |
59 | + bundleMap.put(locale, bundle); | |
60 | + } else { | |
61 | + bundle = defaultBundle; | |
62 | + } | |
63 | + } | |
64 | + | |
65 | + String text = ""; | |
66 | + try { | |
67 | + text = bundle.getString(key); | |
68 | + } catch(MissingResourceException ex){ | |
69 | + text = key; | |
70 | + } | |
71 | + return text; | |
72 | + } | |
73 | + | |
74 | +} |
@@ -17,6 +17,7 @@ | ||
17 | 17 | import jp.sf.amateras.functions.utils.RequestUtils; |
18 | 18 | import jp.sf.amateras.functions.utils.ResponseUtils; |
19 | 19 | import jp.sf.amateras.functions.utils.StringUtils; |
20 | +import jp.sf.amateras.functions.utils.TextUtils; | |
20 | 21 | |
21 | 22 | /** |
22 | 23 | * Java Standard EL Functionsを使用する場合はこのフィルタを<tt>web.xml</tt>に登録する必要があります。 |
@@ -32,7 +33,7 @@ | ||
32 | 33 | * <url-pattern>*</url-pattern> |
33 | 34 | * <dispatcher>REQUEST</dispatcher> |
34 | 35 | * </filter-mapping> </pre> |
35 | - * | |
36 | + * | |
36 | 37 | * このフィルタは以下の処理を行います。 |
37 | 38 | * <ul> |
38 | 39 | * <li> |
@@ -46,7 +47,7 @@ | ||
46 | 47 | * リクエストごとにJava Standard EL Functionsが提供する暗黙変数を<code>HttpServletRequest</code>の属性にセットします。 |
47 | 48 | * </li> |
48 | 49 | * </ul> |
49 | - * | |
50 | + * | |
50 | 51 | * このフィルタによって設定される暗黙変数は以下の通りです。 |
51 | 52 | * <table border="1"> |
52 | 53 | * <tr><th>変数名</th><th>説明</th></tr> |
@@ -55,22 +56,24 @@ | ||
55 | 56 | * <td>アプリケーションのコンテキストパス(pageContext.request.contextPathと等価です)</td> |
56 | 57 | * </tr> |
57 | 58 | * </table> |
58 | - * | |
59 | + * | |
59 | 60 | * @author Naoki Takezoe |
60 | 61 | */ |
61 | 62 | public class FunctionsFilter implements Filter { |
62 | - | |
63 | + | |
63 | 64 | private static final String DATE_PATTERN = "datePattern"; |
64 | - | |
65 | + | |
65 | 66 | private static final String DATETIME_PATTERN = "datetimePattern"; |
66 | - | |
67 | + | |
67 | 68 | private static final String TIME_PATTERN = "timePattern"; |
68 | - | |
69 | + | |
69 | 70 | private static final String DEFAULT_ENCODING = "defaultEncoding"; |
70 | - | |
71 | + | |
72 | + private static final String BUNDLE_NAME = "bundleName"; | |
73 | + | |
71 | 74 | public void destroy() { |
72 | 75 | } |
73 | - | |
76 | + | |
74 | 77 | /** |
75 | 78 | * <tt>functions.properties</tt>を読み込み、設定内容を反映します。 |
76 | 79 | */ |
@@ -82,18 +85,19 @@ | ||
82 | 85 | DateUtils.setDatetimePattern(properties.getProperty(DATETIME_PATTERN)); |
83 | 86 | DateUtils.setTimePattern(properties.getProperty(TIME_PATTERN)); |
84 | 87 | StringUtils.setDefaultEncoding(properties.getProperty(DEFAULT_ENCODING)); |
88 | + TextUtils.setBundleName(properties.getProperty(BUNDLE_NAME)); | |
85 | 89 | } |
86 | 90 | } |
87 | 91 | |
88 | 92 | public void doFilter(ServletRequest request, ServletResponse response, |
89 | 93 | FilterChain chain) throws IOException, ServletException { |
90 | - | |
94 | + | |
91 | 95 | RequestUtils.setRequest((HttpServletRequest) request); |
92 | 96 | ResponseUtils.setResponse((HttpServletResponse) response); |
93 | - | |
97 | + | |
94 | 98 | // コンテキストパスを$contextという変数名でセット |
95 | 99 | request.setAttribute("context", ((HttpServletRequest) request).getContextPath()); |
96 | - | |
100 | + | |
97 | 101 | try { |
98 | 102 | chain.doFilter(request, response); |
99 | 103 | } finally { |