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 {