You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@wookie.apache.org by sc...@apache.org on 2009/12/15 23:14:58 UTC
svn commit: r891031 - in /incubator/wookie/trunk: ./ ant/lib/
src-tests/org/apache/wookie/tests/ src/org/apache/wookie/beans/
src/org/apache/wookie/manifestmodel/
src/org/apache/wookie/manifestmodel/impl/ src/org/apache/wookie/util/
Author: scottbw
Date: Tue Dec 15 22:14:57 2009
New Revision: 891031
URL: http://svn.apache.org/viewvc?rev=891031&view=rev
Log:
Added the basic "plumbing" for a full l10n implementation as specified by WOOKIE-67 and WOOKIE-79, including the core utils class and its test cases, plus interfaces and required libraries. This stage adds no new functionality; this will follow in subsequent changesets.
Added:
incubator/wookie/trunk/ant/lib/icu4j-4_2_1.jar (with props)
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/LocalizationUtilsTest.java
incubator/wookie/trunk/src/org/apache/wookie/beans/ILocalizedElement.java
incubator/wookie/trunk/src/org/apache/wookie/util/LocalizationUtils.java
Modified:
incubator/wookie/trunk/ivy.xml
incubator/wookie/trunk/src/org/apache/wookie/manifestmodel/ILocalizedEntity.java
incubator/wookie/trunk/src/org/apache/wookie/manifestmodel/impl/LocalizedEntity.java
incubator/wookie/trunk/src/org/apache/wookie/util/WidgetPackageUtils.java
Added: incubator/wookie/trunk/ant/lib/icu4j-4_2_1.jar
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/ant/lib/icu4j-4_2_1.jar?rev=891031&view=auto
==============================================================================
Binary file - no diff available.
Propchange: incubator/wookie/trunk/ant/lib/icu4j-4_2_1.jar
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Modified: incubator/wookie/trunk/ivy.xml
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/ivy.xml?rev=891031&r1=891030&r2=891031&view=diff
==============================================================================
--- incubator/wookie/trunk/ivy.xml (original)
+++ incubator/wookie/trunk/ivy.xml Tue Dec 15 22:14:57 2009
@@ -8,6 +8,8 @@
</configurations>
<dependencies>
+ <dependency org="com.ibm.icu" name="icu4j" rev="4_2_1" conf="deploy->default">
+ </dependency>
<dependency org="antlr" name="antlr" rev="2.7.7" conf="deploy->default">
</dependency>
<dependency org="cglib" name="cglib" rev="2.2" conf="deploy->default">
Added: incubator/wookie/trunk/src-tests/org/apache/wookie/tests/LocalizationUtilsTest.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src-tests/org/apache/wookie/tests/LocalizationUtilsTest.java?rev=891031&view=auto
==============================================================================
--- incubator/wookie/trunk/src-tests/org/apache/wookie/tests/LocalizationUtilsTest.java (added)
+++ incubator/wookie/trunk/src-tests/org/apache/wookie/tests/LocalizationUtilsTest.java Tue Dec 15 22:14:57 2009
@@ -0,0 +1,201 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wookie.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+
+import org.apache.wookie.beans.ILocalizedElement;
+import org.apache.wookie.beans.License;
+import org.apache.wookie.util.LocalizationUtils;
+import org.junit.Test;
+
+public class LocalizationUtilsTest {
+
+ public static final String[] LANG_LIST_1 = {null, "en", "fr", "nl"};
+ public static final String[] LANG_LIST_2 = {"en", "za", "nl", null};
+ public static final String[] LANG_LIST_3 = {"en", null, "nl", null};
+ public static final String[] LANG_LIST_4 = {"en"};
+ public static final String[] LANG_LIST_5 = {"en","fr"};
+ public static final String[] LANG_LIST_6 = {"en","nl"};
+ public static final String[] LANG_LIST_7 = {null, "za", "fr", "nl"};
+ public static final String[] LANG_LIST_8 = {"en-us-POSIX"};
+ public static final String[] LANG_LIST_9 = {null, "en", "en-gb", "en-US-POSIX"};
+
+ public ILocalizedElement[] elements(String[] langs){
+ ArrayList<ILocalizedElement> licenses = new ArrayList<ILocalizedElement>();
+ for (String lang:langs){
+ License license = new License();
+ license.setLang(lang);
+ licenses.add(license);
+ }
+ return licenses.toArray(new License[licenses.size()]);
+ }
+
+ @Test
+ public void defaultOrder1(){
+ ILocalizedElement[] elements = elements(LANG_LIST_1);
+ elements = LocalizationUtils.processElementsByDefaultLocales(elements);
+ assertEquals(null, elements[3].getLang());
+ }
+ @Test
+ public void defaultOrder2(){
+ ILocalizedElement[] elements = elements(LANG_LIST_2);
+ elements = LocalizationUtils.processElementsByDefaultLocales(elements);
+ assertEquals(null, elements[3].getLang());
+ }
+ @Test
+ public void defaultOrder3(){
+ ILocalizedElement[] elements = elements(LANG_LIST_3);
+ elements = LocalizationUtils.processElementsByDefaultLocales(elements);
+ assertEquals(null, elements[2].getLang());
+ assertEquals(null, elements[3].getLang());
+ }
+
+ @Test
+ public void specifiedOrder1(){
+ ILocalizedElement[] elements = elements(LANG_LIST_1);
+ elements = LocalizationUtils.processElementsByLocales(elements, LANG_LIST_4);
+ assertEquals("en", elements[0].getLang());
+ assertEquals(1, elements.length);
+ }
+ @Test
+ public void specifiedOrder2(){
+ ILocalizedElement[] elements = elements(LANG_LIST_2);
+ elements = LocalizationUtils.processElementsByLocales(elements, LANG_LIST_4);
+ assertEquals("en", elements[0].getLang());
+ assertEquals(1, elements.length);
+ }
+ @Test
+ public void specifiedOrder3(){
+ ILocalizedElement[] elements = elements(LANG_LIST_3);
+ elements = LocalizationUtils.processElementsByLocales(elements, LANG_LIST_4);
+ assertEquals("en", elements[0].getLang());
+ assertEquals(1, elements.length);
+ }
+
+ @Test
+ public void noMatch(){
+ ILocalizedElement[] elements = elements(LANG_LIST_7);
+ elements = LocalizationUtils.processElementsByLocales(elements, LANG_LIST_4);
+ assertEquals(null, elements[0].getLang());
+ assertEquals(1, elements.length);
+ }
+
+ @Test
+ public void noMatch2(){
+ ILocalizedElement[] elements = elements(LANG_LIST_1);
+ elements = LocalizationUtils.processElementsByLocales(elements, LANG_LIST_4);
+ assertEquals(1, elements.length);
+ }
+
+ @Test
+ public void specifiedOrderM4(){
+ ILocalizedElement[] elements = elements(LANG_LIST_3);
+ elements = LocalizationUtils.processElementsByLocales(elements, LANG_LIST_6);
+ assertEquals("en", elements[0].getLang());
+ assertEquals("nl", elements[1].getLang());
+ assertEquals(2, elements.length);
+ }
+ @Test
+ public void specifiedOrderM5(){
+ ILocalizedElement[] elements = elements(LANG_LIST_3);
+ elements = LocalizationUtils.processElementsByLocales(elements, LANG_LIST_5);
+ assertEquals("en", elements[0].getLang());
+ assertEquals(1, elements.length);
+ }
+ @Test
+ public void specifiedOrderM6(){
+ ILocalizedElement[] elements = elements(LANG_LIST_1);
+ elements = LocalizationUtils.processElementsByLocales(elements, LANG_LIST_6);
+ assertEquals("en", elements[0].getLang());
+ assertEquals("nl", elements[1].getLang());
+ assertEquals(2, elements.length);
+ }
+ @Test
+ public void specifiedOrderM7(){
+ ILocalizedElement[] elements = elements(LANG_LIST_9);
+ elements = LocalizationUtils.processElementsByLocales(elements, LANG_LIST_8);
+ assertEquals("en-US-POSIX", elements[0].getLang());
+ assertEquals("en", elements[1].getLang());
+ assertEquals(2, elements.length);
+ }
+ @Test
+ public void classCast(){
+ ILocalizedElement[] elements = elements(LANG_LIST_9);
+ elements = LocalizationUtils.processElementsByLocales(elements, LANG_LIST_8);
+ assertEquals("en-US-POSIX", elements[0].getLang());
+ assertEquals("en", elements[1].getLang());
+ assertTrue(elements[0] instanceof License);
+ }
+
+ @Test
+ public void simple(){
+ String[] langs = {"en","fr","za"};
+ for (String lang:langs){
+ assertTrue(LocalizationUtils.isValidLanguageTag(lang));
+ }
+ String[] langs2 = {"waytoolong", "l33t", "26", null, "", " "};
+ for (String lang:langs2){
+ assertFalse(LocalizationUtils.isValidLanguageTag(lang));
+ }
+ }
+ @Test
+ public void region(){
+ String[] langs = {"en-gb","en-us", "ch-229"};
+ for (String lang:langs){
+ assertTrue(LocalizationUtils.isValidLanguageTag(lang));
+ }
+ String[] langs2 = {"en-123456789", "i-klingon"};
+ for (String lang:langs2){
+ assertFalse(LocalizationUtils.isValidLanguageTag(lang));
+ }
+ }
+ @Test
+ public void script(){
+ String[] langs = {"za-hans", "de-Latn"};
+ for (String lang:langs){
+ assertTrue(LocalizationUtils.isValidLanguageTag(lang));
+ }
+ String[] langs2 = {"za-hansa-hans", "de-Latin-x", "en-gb-12345a678-a2cdefgh"};
+ for (String lang:langs2){
+ assertFalse(LocalizationUtils.isValidLanguageTag(lang));
+ }
+ }
+ @Test
+ public void variants(){
+ String[] langs = {"de-Latf-DE","de-Latn-DE-1996", "de-Deva-DE"};
+ for (String lang:langs){
+ assertTrue(LocalizationUtils.isValidLanguageTag(lang));
+ }
+ String[] langs2 = {"x-x-test", "de-Latn-DE-9999999999"};
+ for (String lang:langs2){
+ assertFalse(LocalizationUtils.isValidLanguageTag(lang));
+ }
+ }
+ @Test
+ public void extensions(){
+ String[] langs = {"de-Latf-DE-a-11","de-Latn-DE-c-ab", "de-Deva-DE-n-231", "en-gb-a-manc"};
+ for (String lang:langs){
+ assertTrue(LocalizationUtils.isValidLanguageTag(lang));
+ }
+ String[] langs2 = {"en-gb-a-a", "de-Latn-DE-x-x", "de-Latn-DE-a-123456789"};
+ for (String lang:langs2){
+ assertFalse(LocalizationUtils.isValidLanguageTag(lang));
+ }
+ }
+}
Added: incubator/wookie/trunk/src/org/apache/wookie/beans/ILocalizedElement.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/beans/ILocalizedElement.java?rev=891031&view=auto
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/beans/ILocalizedElement.java (added)
+++ incubator/wookie/trunk/src/org/apache/wookie/beans/ILocalizedElement.java Tue Dec 15 22:14:57 2009
@@ -0,0 +1,29 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wookie.beans;
+
+/**
+ * An element that can be localized by language tag
+ * @author scott
+ *
+ */
+public interface ILocalizedElement {
+
+ /**
+ * Get the language tag for the element. This should conform to BCP47.
+ * @return
+ */
+ public String getLang();
+
+}
Modified: incubator/wookie/trunk/src/org/apache/wookie/manifestmodel/ILocalizedEntity.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/manifestmodel/ILocalizedEntity.java?rev=891031&r1=891030&r2=891031&view=diff
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/manifestmodel/ILocalizedEntity.java (original)
+++ incubator/wookie/trunk/src/org/apache/wookie/manifestmodel/ILocalizedEntity.java Tue Dec 15 22:14:57 2009
@@ -18,5 +18,7 @@
public String getLanguage();
public void setLanguage(String language);
+
+ public boolean isValid();
}
Modified: incubator/wookie/trunk/src/org/apache/wookie/manifestmodel/impl/LocalizedEntity.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/manifestmodel/impl/LocalizedEntity.java?rev=891031&r1=891030&r2=891031&view=diff
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/manifestmodel/impl/LocalizedEntity.java (original)
+++ incubator/wookie/trunk/src/org/apache/wookie/manifestmodel/impl/LocalizedEntity.java Tue Dec 15 22:14:57 2009
@@ -15,6 +15,7 @@
import org.apache.wookie.manifestmodel.ILocalizedEntity;
import org.apache.wookie.manifestmodel.IW3CXMLConfiguration;
+import org.apache.wookie.util.LocalizationUtils;
import org.apache.wookie.util.UnicodeUtils;
import org.jdom.Element;
import org.jdom.Namespace;
@@ -32,6 +33,16 @@
this.language = language;
}
+ /**
+ * Checks whether the language tag for the entity is OK.
+ * A null value is OK, as is a BCP47 tag
+ */
+ public boolean isValid(){
+ if (getLanguage() == null) return true;
+ if (LocalizationUtils.isValidLanguageTag(getLanguage())) return true;
+ return false;
+ }
+
/* (non-Javadoc)
* @see org.apache.wookie.manifestmodel.IManifestModelBase#fromXML(org.jdom.Element)
*/
Added: incubator/wookie/trunk/src/org/apache/wookie/util/LocalizationUtils.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/util/LocalizationUtils.java?rev=891031&view=auto
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/util/LocalizationUtils.java (added)
+++ incubator/wookie/trunk/src/org/apache/wookie/util/LocalizationUtils.java Tue Dec 15 22:14:57 2009
@@ -0,0 +1,179 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wookie.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.wookie.beans.ILocalizedElement;
+
+import com.ibm.icu.util.GlobalizationPreferences;
+import com.ibm.icu.util.ULocale;
+
+/**
+ * Utilities for localization
+ * @author Scott Wilson
+ *
+ */
+public class LocalizationUtils {
+
+ /**
+ * Returns the first (best) match for an element given the set of locales, or null
+ * if there are no suitable elements.
+ * @param locales
+ * @param elements
+ * @return an ILocalizedElement, or null if there are no valid entries
+ */
+ public static ILocalizedElement getLocalizedElement(ILocalizedElement[] elements,String[] locales){
+ processElementsByLocales(elements,locales);
+ if (elements.length == 0) return null;
+ return elements[0];
+ }
+
+ /**
+ * Filters and sorts a list of localized elements using the given locale list
+ *
+ * @param locales
+ * @return
+ */
+ public static ILocalizedElement[] processElementsByLocales(ILocalizedElement[] elements,String[] locales){
+ List<ULocale> localesList = getProcessedLocaleList(locales);
+ Arrays.sort(elements, new LocaleComparator(localesList));
+ return filter(elements,localesList);
+ }
+
+ /**
+ * Sorts an array of localized elements using default locales (*). This places
+ * any localized elements first, then any unlocalized elements
+ * @return
+ */
+ public static ILocalizedElement[] processElementsByDefaultLocales(ILocalizedElement[] elements){
+ Arrays.sort(elements, new LocaleComparator(getDefaultLocaleList()));
+ return elements;
+ }
+
+ /**
+ * Comparator that sorts elements based on priority in the given locale list,
+ * with the assumption that earlier in the list means a higher priority.
+ */
+ static class LocaleComparator implements Comparator<ILocalizedElement>{
+ private List<ULocale> locales;
+ public LocaleComparator(List<ULocale> locales){
+ this.locales = locales;
+ }
+ public int compare(ILocalizedElement o1, ILocalizedElement o2) {
+ // check non-localized values for comparison
+ if (o1.getLang()!=null && o2.getLang() == null) return -1;
+ if (o1.getLang()==null && o2.getLang()!= null) return 1;
+ if (o1.getLang()==null && o2.getLang()==null) return 0;
+
+ // get index position of locales
+ int o1i = -1;
+ int o2i = -1;
+ for (int i=0;i<locales.size();i++){
+ if (o1.getLang().equalsIgnoreCase(locales.get(i).toLanguageTag())) o1i = i;
+ if (o2.getLang().equalsIgnoreCase(locales.get(i).toLanguageTag())) o2i = i;
+ }
+ // put non-matched after matched
+ if (o1i == -1 && o2i > -1) return 1;
+ if (o1i > -1 && o2i == -1) return -1;
+
+ // order by match
+ return o1i - o2i;
+ }
+ }
+
+ /**
+ * Filters a set of elements using the locales and returns a copy of the array
+ * containing only elements that match the locales, or that are not localized
+ * if there are no matches
+ * @param elements
+ * @param locales
+ * @return a copy of the array of elements only containing the filtered elements
+ */
+ protected static ILocalizedElement[] filter(ILocalizedElement[] elements, List<ULocale> locales){
+ for (ILocalizedElement element:elements){
+ String lang = element.getLang();
+ boolean found = false;
+ for(ULocale locale:locales){
+ if (locale.toLanguageTag().equalsIgnoreCase(lang)) found = true;
+ }
+ if (!found && lang != null) elements = (ILocalizedElement[])ArrayUtils.removeElement(elements, element);
+ }
+ // Strip out non-localized elements only if there are localized elements left
+ if (elements.length > 0){
+ if (elements[0].getLang() != null){
+ for (ILocalizedElement element:elements){
+ if (element.getLang()==null) elements = (ILocalizedElement[])ArrayUtils.removeElement(elements, element);
+ }
+ }
+ }
+ return elements;
+ }
+
+
+ /**
+ * Converts an array of language tags to a list of ULocale instances
+ * ordered according to priority, availability and fallback
+ * rules. For example "en-us-posix" will be returned as a List
+ * containing the ULocales for "en-us-posix", "en-us", and "en".
+ * @param locales
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ protected static List<ULocale> getProcessedLocaleList(String[] locales){
+ if (locales == null) return getDefaultLocaleList();
+ GlobalizationPreferences prefs = new GlobalizationPreferences();
+
+ ArrayList<ULocale> ulocales = new ArrayList<ULocale>();
+ for (String locale:locales){
+ ULocale ulocale = ULocale.forLanguageTag(locale);
+ ulocales.add(ulocale);
+ }
+ prefs.setLocales(ulocales.toArray(new ULocale[ulocales.size()]));
+ return prefs.getLocales();
+ }
+
+ /**
+ * Gets the default locales list
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ protected static List<ULocale> getDefaultLocaleList(){
+ GlobalizationPreferences prefs = new GlobalizationPreferences();
+ return prefs.getLocales();
+ }
+
+ /**
+ * Validates a given language tag. Empty and null values are considered false.
+ * @param tag
+ * @return true if a valid language tag, otherwise false
+ */
+ public static boolean isValidLanguageTag(String tag){
+ try {
+ ULocale locale = ULocale.forLanguageTag(tag);
+ if (locale.toLanguageTag() == null) return false;
+ // We don't accept "x" extensions (private use tags)
+ if(locale.getExtension("x".charAt(0))!=null) return false;
+ if (!locale.toLanguageTag().equalsIgnoreCase(tag)) return false;
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+}
Modified: incubator/wookie/trunk/src/org/apache/wookie/util/WidgetPackageUtils.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/util/WidgetPackageUtils.java?rev=891031&r1=891030&r2=891031&view=diff
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/util/WidgetPackageUtils.java (original)
+++ incubator/wookie/trunk/src/org/apache/wookie/util/WidgetPackageUtils.java Tue Dec 15 22:14:57 2009
@@ -81,6 +81,22 @@
return path;
}
+ /**
+ * Return the language tag for a given path
+ * @param path
+ * @return
+ */
+ public static String languageTagForPath(String path){
+ if (path == null) return null;
+ String locale = null;
+ String[] pathComponents = path.split("/");
+ if ("locales".equalsIgnoreCase(pathComponents[0])){
+ if (pathComponents.length < 2) return null;
+ return pathComponents[1];
+ }
+ return locale;
+ }
+
public static String locateDefaultIcon(ZipFile zip, String[] locales){
for (String icon: IW3CXMLConfiguration.DEFAULT_ICON_FILES){
try {