| Languages.java |
1 /**
2 * Distribution License:
3 * JSword is free software; you can redistribute it and/or modify it under
4 * the terms of the GNU Lesser General Public License, version 2.1 or later
5 * as published by the Free Software Foundation. This program is distributed
6 * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
7 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8 * See the GNU Lesser General Public License for more details.
9 *
10 * The License is available on the internet at:
11 * http://www.gnu.org/copyleft/lgpl.html
12 * or by writing to:
13 * Free Software Foundation, Inc.
14 * 59 Temple Place - Suite 330
15 * Boston, MA 02111-1307, USA
16 *
17 * © CrossWire Bible Society, 2005 - 2016
18 *
19 */
20 package org.crosswire.common.util;
21
22 import java.io.IOException;
23 import java.net.URL;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Locale;
27 import java.util.Map;
28 import java.util.MissingResourceException;
29 import java.util.ResourceBundle;
30 import java.util.Set;
31
32 import org.crosswire.jsword.book.Books;
33 import org.crosswire.jsword.internationalisation.LocaleProviderManager;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38 * A utility class that converts bcp-47 codes as supported by {@link Language} to their
39 * localized language name.
40 *
41 * @see gnu.lgpl.License The GNU Lesser General Public License for details.
42 * @author DM Smith
43 */
44 public final class Languages {
45
46 /**
47 * Make the class a true utility class by having a private constructor.
48 */
49 private Languages() {
50 }
51
52 /**
53 * Get the language name for the BCP-47 specification of the language.
54 *
55 * @param code the BCP-47 specification for the language
56 * @return the name of the language
57 */
58 public static String getName(String code) {
59 // Returning the code is the fallback for lookup
60 String name = code;
61 try {
62 ResourceBundle langs = getLocalisedCommonLanguages();
63 if (langs != null) {
64 name = langs.getString(code);
65 }
66 } catch (MissingResourceException e) {
67 // This is allowed
68 }
69 return name;
70 }
71
72 /**
73 * Gets the localised common languages. Caching here, is done to prevent extra logging
74 * happening every time we miss the iso639 ResourceBundle
75 * and end up having to lookup the iso639full
76 *
77 * @return the localised common languages
78 */
79 private static ResourceBundle getLocalisedCommonLanguages() {
80 Locale locale = LocaleProviderManager.getLocale();
81 ResourceBundle langs = localisedCommonLanguages.get(locale);
82 if (langs == null) {
83 synchronized (Languages.class) {
84 langs = localisedCommonLanguages.get(locale);
85 if (langs == null) {
86 langs = initLanguages(locale);
87 if (langs != null) {
88 localisedCommonLanguages.put(locale, langs);
89 }
90 }
91 }
92 }
93 return langs;
94 }
95
96 private static ResourceBundle initLanguages(Locale locale) {
97 try {
98 return ResourceBundle.getBundle("iso639", locale, CWClassLoader.instance());
99 } catch (MissingResourceException e) {
100 log.info("Unable to find language in iso639 bundle", e);
101 }
102 return null;
103 }
104
105 /**
106 * Provide a fallback lookup against a huge list of all languages.
107 * The basic list has a few hundred languages. The full list has
108 * over 7000. As a fallback, this file is not internationalized.
109 */
110 public static final class AllLanguages {
111 /**
112 * This is a singleton class. Do not allow construction.
113 */
114 private AllLanguages() { }
115
116 /**
117 * Get the language name for the code. If the language name is not known
118 * then return the code.
119 *
120 * @param languageCode the language code
121 * @return the name for the language.
122 */
123 public static String getName(String languageCode) {
124 if (instance != null) {
125 String name = instance.get(languageCode);
126 if (name != null) {
127 return name;
128 }
129 }
130 return languageCode;
131 }
132
133 /**
134 * Do lazy loading of the huge file of languages.
135 * Note: It is OK for it not to be present.
136 */
137 private static PropertyMap instance;
138 static {
139 try {
140 instance = ResourceUtil.getProperties("iso639full");
141 log.debug("Loading iso639full.properties file");
142 } catch (IOException e) {
143 log.info("Unable to load iso639full.properties", e);
144 }
145 }
146 }
147
148 /**
149 * Provide a fallback lookup against a huge list of all languages.
150 * The basic list has a few hundred languages. The full list has
151 * over 7000. As a fallback, this file is not internationalized.
152 */
153 public static final class RtoL {
154 /**
155 * This is a singleton class. Do not allow construction.
156 */
157 private RtoL() { }
158
159 /**
160 * Determine whether this language is a Left-to-Right or a Right-to-Left
161 * language. If the language has a script, it is used for the determination.
162 * Otherwise, check the language.
163 * <p>
164 * Note: This is problematic. Languages do not have direction.
165 * Scripts do. Further, there are over 7000 living languages, many of which
166 * are written in Right-to-Left scripts and are not listed here.
167 * </p>
168 *
169 * @param script the iso15924 script code, must be in Title case
170 * @param lang the iso639 language code, must be lower case
171 * @return true if the language is Right-to-Left
172 */
173 public static boolean isRtoL(String script, String lang) {
174 if (script != null) {
175 return rtol.contains(script);
176 }
177 if (lang != null) {
178 return rtol.contains(lang);
179 }
180 return false;
181 }
182
183 /**
184 * Do lazy loading of the huge file of languages.
185 * Note: It is OK for it not to be present.
186 */
187 private static Set rtol = new HashSet();
188 /**
189 * load RtoL data
190 */
191 static {
192 try {
193 URL index = ResourceUtil.getResource(Translations.class, "rtol.txt");
194 String[] list = NetUtil.listByIndexFile(NetUtil.toURI(index));
195 log.debug("Loading iso639full.properties file");
196 for (int i = 0; i < list.length; i++) {
197 rtol.add(list[i]);
198 }
199 } catch (IOException ex) {
200 log.info("Unable to load rtol.txt", ex);
201 }
202 }
203 }
204
205 private static Map<Locale, ResourceBundle> localisedCommonLanguages = new HashMap<Locale, ResourceBundle>();
206
207 /**
208 * The log stream
209 */
210 protected static final Logger log = LoggerFactory.getLogger(Books.class);
211 }
212