From cb5310c9840100f7a5a298439cd31e44fc260635 Mon Sep 17 00:00:00 2001 From: Henning Staib Date: Sun, 15 Aug 2010 15:13:05 +0000 Subject: [PATCH] improve Message parser robustness and I18N for Message bodies (fixes SMACK-207 and SMACK-307) git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@11824 b35dd754-fafc-0310-a699-88a17e54d16e --- .../jivesoftware/smack/packet/Message.java | 120 +++-- .../org/jivesoftware/smack/packet/Packet.java | 15 +- .../smack/util/PacketParserUtils.java | 40 +- .../smack/util/PacketParserUtilsTest.java | 451 +++++++++++++++++- 4 files changed, 538 insertions(+), 88 deletions(-) diff --git a/source/org/jivesoftware/smack/packet/Message.java b/source/org/jivesoftware/smack/packet/Message.java index 0e9bcfc69..e5e9a11e5 100644 --- a/source/org/jivesoftware/smack/packet/Message.java +++ b/source/org/jivesoftware/smack/packet/Message.java @@ -127,8 +127,12 @@ public class Message extends Packet { } /** - * Returns the body of the message, or null if the body has not been set. The body + * Returns the default body of the message, or null if the body has not been set. The body * is the main message contents. + *

+ * The default body of a message is the body that corresponds to the message's language. + * (see {@link #getLanguage()}) or if no language is set to the applications default + * language (see {@link Packet#getDefaultLanguage()}). * * @return the body of the message. */ @@ -146,11 +150,15 @@ public class Message extends Packet { * @since 3.0.2 */ public String getBody(String language) { - language = parseXMLLang(language); + Body body = getMessageBody(language); + return body == null ? null : body.message; + } + + private Body getMessageBody(String language) { + language = determineLanguage(language); for (Body body : bodies) { - if ((body.langauge == null && language == null) - || (body != null && body.langauge.equals(language))) { - return body.message; + if (language.equals(body.language)) { + return body; } } return null; @@ -174,7 +182,7 @@ public class Message extends Packet { */ public void setBody(String body) { if (body == null) { - removeBody(""); + removeBody(""); // use empty string because #removeBody(null) is ambiguous return; } addBody(null, body); @@ -190,11 +198,7 @@ public class Message extends Packet { * @since 3.0.2 */ public Body addBody(String language, String body) { - if (body == null) { - throw new NullPointerException("Body must be specified"); - } - language = parseXMLLang(language); - + language = determineLanguage(language); Body messageBody = new Body(language, body); bodies.add(messageBody); return messageBody; @@ -207,10 +211,9 @@ public class Message extends Packet { * @return true if a body was removed and false if it was not. */ public boolean removeBody(String language) { - language = parseXMLLang(language); - + language = determineLanguage(language); for (Body body : bodies) { - if (language.equals(body.langauge)) { + if (language.equals(body.language)) { return bodies.remove(body); } } @@ -235,10 +238,11 @@ public class Message extends Packet { * @since 3.0.2 */ public Collection getBodyLanguages() { - List languages = new ArrayList(bodies.size()); + Body defaultBody = getMessageBody(null); + List languages = new ArrayList(); for (Body body : bodies) { - if (!parseXMLLang(body.langauge).equals(getDefaultLanguage())) { - languages.add(body.langauge); + if (!body.equals(defaultBody)) { + languages.add(body.language); } } return Collections.unmodifiableCollection(languages); @@ -270,7 +274,7 @@ public class Message extends Packet { * @return the xml:lang of this Message. * @since 3.0.2 */ - private String getLanguage() { + public String getLanguage() { return language; } @@ -284,6 +288,24 @@ public class Message extends Packet { this.language = language; } + private String determineLanguage(String language) { + + // empty string is passed by #setSubject() and #setBody() and is the same as null + language = "".equals(language) ? null : language; + + // if given language is null check if message language is set + if (language == null && this.language != null) { + return this.language; + } + else if (language == null) { + return getDefaultLanguage(); + } + else { + return language; + } + + } + public String toXML() { StringBuilder buf = new StringBuilder(); buf.append("").append(StringUtils.escapeForXML(subject)).append(""); } // Add the body in the default language - if (getBody() != null) { - buf.append("").append(StringUtils.escapeForXML(getBody())).append(""); + Body defaultBody = getMessageBody(null); + if (defaultBody != null) { + buf.append("").append(StringUtils.escapeForXML(defaultBody.message)).append(""); } // Add the bodies in other languages for (Body body : getBodies()) { // Skip the default language - if (DEFAULT_LANGUAGE.equals(body.getLanguage()) || body.getLanguage() == null) { + if(body.equals(defaultBody)) continue; - } buf.append(""); buf.append(StringUtils.escapeForXML(body.getMessage())); buf.append(""); @@ -379,29 +401,26 @@ public class Message extends Packet { public static class Body { private String message; - private String langauge; + private String language; private Body(String language, String message) { + if (language == null) { + throw new NullPointerException("Language cannot be null."); + } if (message == null) { throw new NullPointerException("Message cannot be null."); } - this.langauge = language; + this.language = language; this.message = message; } /** - * Returns the language of this message body. If the language is null, then, no language - * was specified. + * Returns the language of this message body. * * @return the language of this message body. */ public String getLanguage() { - if (DEFAULT_LANGUAGE.equals(langauge)) { - return null; - } - else { - return langauge; - } + return language; } /** @@ -413,26 +432,29 @@ public class Message extends Packet { return message; } - - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) { return false; } - - Body body = (Body) o; - - if (langauge != null ? !langauge.equals(body.langauge) : body.langauge != null) { - return false; - } - return message.equals(body.message); - - } - public int hashCode() { - int result; - result = message.hashCode(); - result = 31 * result + (langauge != null ? langauge.hashCode() : 0); + final int prime = 31; + int result = 1; + result = prime * result + this.language.hashCode(); + result = prime * result + this.message.hashCode(); return result; } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Body other = (Body) obj; + // simplified comparison because language and message are always set + return this.language.equals(other.language) && this.message.equals(other.message); + } + } /** diff --git a/source/org/jivesoftware/smack/packet/Packet.java b/source/org/jivesoftware/smack/packet/Packet.java index 39d00125b..883462b1b 100644 --- a/source/org/jivesoftware/smack/packet/Packet.java +++ b/source/org/jivesoftware/smack/packet/Packet.java @@ -416,18 +416,15 @@ public abstract class Packet { return this.xmlns; } - protected static String parseXMLLang(String language) { - if (language == null || "".equals(language)) { - language = DEFAULT_LANGUAGE; - } - return language; - } - - protected static String getDefaultLanguage() { + /** + * Returns the default language used for all messages containing localized content. + * + * @return the default language + */ + public static String getDefaultLanguage() { return DEFAULT_LANGUAGE; } - public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; diff --git a/source/org/jivesoftware/smack/util/PacketParserUtils.java b/source/org/jivesoftware/smack/util/PacketParserUtils.java index a35b03782..6b945460a 100644 --- a/source/org/jivesoftware/smack/util/PacketParserUtils.java +++ b/source/org/jivesoftware/smack/util/PacketParserUtils.java @@ -68,8 +68,15 @@ public class PacketParserUtils { message.setFrom(parser.getAttributeValue("", "from")); message.setType(Message.Type.fromString(parser.getAttributeValue("", "type"))); String language = getLanguageAttribute(parser); + + // determine message's default language + String defaultLanguage = null; if (language != null && !"".equals(language.trim())) { - message.setLanguage(language); + message.setLanguage(language); + defaultLanguage = language; + } + else { + defaultLanguage = Packet.getDefaultLanguage(); } // Parse sub-elements. We include extra logic to make sure the values @@ -77,7 +84,6 @@ public class PacketParserUtils { // in arbitrary sub-elements. boolean done = false; String subject = null; - String body; String thread = null; Map properties = null; while (!done) { @@ -92,8 +98,15 @@ public class PacketParserUtils { } else if (elementName.equals("body")) { String xmlLang = getLanguageAttribute(parser); - body = parser.nextText(); - message.addBody(xmlLang, body); + if (xmlLang == null) { + xmlLang = defaultLanguage; + } + + String body = parseContent(parser); + + if (message.getBody(xmlLang) == null) { + message.addBody(xmlLang, body); + } } else if (elementName.equals("thread")) { if (thread == null) { @@ -131,6 +144,25 @@ public class PacketParserUtils { return message; } + /** + * Returns the content of a tag as string regardless of any tags included. + * + * @param parser the XML pull parser + * @return the content of a tag as string + * @throws XmlPullParserException if parser encounters invalid XML + * @throws IOException if an IO error occurs + */ + private static String parseContent(XmlPullParser parser) + throws XmlPullParserException, IOException { + String content = ""; + int parserDepth = parser.getDepth(); + while (!(parser.next() == XmlPullParser.END_TAG && parser + .getDepth() == parserDepth)) { + content += parser.getText(); + } + return content; + } + /** * Parses a presence packet. * diff --git a/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java b/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java index a243cc932..565ca2556 100644 --- a/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java +++ b/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java @@ -7,46 +7,432 @@ */ package org.jivesoftware.smack.util; -import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import static junit.framework.Assert.*; +import static org.custommonkey.xmlunit.XMLAssert.*; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Locale; +import java.util.Properties; + +import org.custommonkey.xmlunit.DetailedDiff; +import org.custommonkey.xmlunit.Diff; +import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; import org.junit.Test; import org.xmlpull.mxp1.MXParser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import java.io.IOException; -import java.io.StringReader; +import com.jamesmurty.utils.XMLBuilder; /** * */ public class PacketParserUtilsTest { + + private static Properties outputProperties = new Properties(); + { + outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); + } + + @Test + public void singleMessageBodyTest() throws Exception { + String defaultLanguage = Packet.getDefaultLanguage(); + String otherLanguage = determineNonDefaultLanguage(); + + String control; + + // message has default language, body has no language + control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .a("xml:lang", defaultLanguage) + .e("body") + .t(defaultLanguage) + .asString(outputProperties); + + Message message = (Message) PacketParserUtils + .parseMessage(getParser(control)); + + assertEquals(defaultLanguage, message.getBody()); + assertTrue(message.getBodyLanguages().isEmpty()); + assertEquals(defaultLanguage, message.getBody(defaultLanguage)); + assertNull(message.getBody(otherLanguage)); + assertXMLEqual(control, message.toXML()); + + // message has non-default language, body has no language + control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .a("xml:lang", otherLanguage) + .e("body") + .t(otherLanguage) + .asString(outputProperties); + + message = (Message) PacketParserUtils.parseMessage(getParser(control)); + + assertEquals(otherLanguage, message.getBody()); + assertTrue(message.getBodyLanguages().isEmpty()); + assertEquals(otherLanguage, message.getBody(otherLanguage)); + assertNull(message.getBody(defaultLanguage)); + assertXMLEqual(control, message.toXML()); + + // message has no language, body has no language + control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .e("body") + .t(defaultLanguage) + .asString(outputProperties); + + message = (Message) PacketParserUtils.parseMessage(getParser(control)); + + assertEquals(defaultLanguage, message.getBody()); + assertTrue(message.getBodyLanguages().isEmpty()); + assertEquals(defaultLanguage, message.getBody(defaultLanguage)); + assertNull(message.getBody(otherLanguage)); + assertXMLEqual(control, message.toXML()); + + // message has no language, body has default language + control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .e("body") + .a("xml:lang", defaultLanguage) + .t(defaultLanguage) + .asString(outputProperties); + + message = (Message) PacketParserUtils.parseMessage(getParser(control)); + + assertEquals(defaultLanguage, message.getBody()); + assertTrue(message.getBodyLanguages().isEmpty()); + assertEquals(defaultLanguage, message.getBody(defaultLanguage)); + assertNull(message.getBody(otherLanguage)); + + // body attribute xml:lang is unnecessary + assertXMLNotEqual(control, message.toXML()); + + // message has no language, body has non-default language + control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .e("body") + .a("xml:lang", otherLanguage) + .t(otherLanguage) + .asString(outputProperties); + + message = (Message) PacketParserUtils.parseMessage(getParser(control)); + + assertNull(message.getBody()); + assertFalse(message.getBodyLanguages().isEmpty()); + assertTrue(message.getBodyLanguages().contains(otherLanguage)); + assertEquals(otherLanguage, message.getBody(otherLanguage)); + assertNull(message.getBody(defaultLanguage)); + assertXMLEqual(control, message.toXML()); + + // message has default language, body has non-default language + control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .a("xml:lang", defaultLanguage) + .e("body") + .a("xml:lang", otherLanguage) + .t(otherLanguage) + .asString(outputProperties); + + message = (Message) PacketParserUtils.parseMessage(getParser(control)); + + assertNull(message.getBody()); + assertFalse(message.getBodyLanguages().isEmpty()); + assertTrue(message.getBodyLanguages().contains(otherLanguage)); + assertEquals(otherLanguage, message.getBody(otherLanguage)); + assertNull(message.getBody(defaultLanguage)); + assertXMLEqual(control, message.toXML()); + + // message has non-default language, body has default language + control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .a("xml:lang", otherLanguage) + .e("body") + .a("xml:lang", defaultLanguage) + .t(defaultLanguage) + .asString(outputProperties); + + message = (Message) PacketParserUtils.parseMessage(getParser(control)); + + assertNull(message.getBody()); + assertFalse(message.getBodyLanguages().isEmpty()); + assertTrue(message.getBodyLanguages().contains(defaultLanguage)); + assertEquals(defaultLanguage, message.getBody(defaultLanguage)); + assertNull(message.getBody(otherLanguage)); + assertXMLEqual(control, message.toXML()); + + } + + @Test + public void multipleMessageBodiesTest() throws Exception { + String defaultLanguage = Packet.getDefaultLanguage(); + String otherLanguage = determineNonDefaultLanguage(); + + String control; + Message message; + + // message has default language, first body no language, second body other language + control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .a("xml:lang", defaultLanguage) + .e("body") + .t(defaultLanguage) + .up() + .e("body") + .a("xml:lang", otherLanguage) + .t(otherLanguage) + .asString(outputProperties); + + message = (Message) PacketParserUtils + .parseMessage(getParser(control)); + + assertEquals(defaultLanguage, message.getBody()); + assertEquals(otherLanguage, message.getBody(otherLanguage)); + assertEquals(2, message.getBodies().size()); + assertEquals(1, message.getBodyLanguages().size()); + assertTrue(message.getBodyLanguages().contains(otherLanguage)); + assertXMLEqual(control, message.toXML()); + + // message has default language, first body no language, second body default language + control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .a("xml:lang", defaultLanguage) + .e("body") + .t(defaultLanguage) + .up() + .e("body") + .a("xml:lang", defaultLanguage) + .t(defaultLanguage + "2") + .asString(outputProperties); + + message = (Message) PacketParserUtils + .parseMessage(getParser(control)); + + assertEquals(defaultLanguage, message.getBody()); + assertEquals(defaultLanguage, message.getBody(defaultLanguage)); + assertEquals(1, message.getBodies().size()); + assertEquals(0, message.getBodyLanguages().size()); + assertXMLNotEqual(control, message.toXML()); + + // message has non-default language, first body no language, second body default language + control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .a("xml:lang", otherLanguage) + .e("body") + .t(otherLanguage) + .up() + .e("body") + .a("xml:lang", defaultLanguage) + .t(defaultLanguage) + .asString(outputProperties); + + message = (Message) PacketParserUtils + .parseMessage(getParser(control)); + + assertEquals(otherLanguage, message.getBody()); + assertEquals(defaultLanguage, message.getBody(defaultLanguage)); + assertEquals(2, message.getBodies().size()); + assertEquals(1, message.getBodyLanguages().size()); + assertTrue(message.getBodyLanguages().contains(defaultLanguage)); + assertXMLEqual(control, message.toXML()); + + // message has no language, first body no language, second body default language + control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .e("body") + .t(defaultLanguage) + .up() + .e("body") + .a("xml:lang", defaultLanguage) + .t(defaultLanguage + "2") + .asString(outputProperties); + + message = (Message) PacketParserUtils + .parseMessage(getParser(control)); + + assertEquals(defaultLanguage, message.getBody()); + assertEquals(defaultLanguage, message.getBody(defaultLanguage)); + assertEquals(1, message.getBodies().size()); + assertEquals(0, message.getBodyLanguages().size()); + assertXMLNotEqual(control, message.toXML()); + + // message has no language, first body no language, second body other language + control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .e("body") + .t(defaultLanguage) + .up() + .e("body") + .a("xml:lang", otherLanguage) + .t(otherLanguage) + .asString(outputProperties); + + message = (Message) PacketParserUtils + .parseMessage(getParser(control)); + + assertEquals(defaultLanguage, message.getBody()); + assertEquals(defaultLanguage, message.getBody(defaultLanguage)); + assertEquals(otherLanguage, message.getBody(otherLanguage)); + assertEquals(2, message.getBodies().size()); + assertEquals(1, message.getBodyLanguages().size()); + assertXMLEqual(control, message.toXML()); + + // message has no language, first body no language, second body no language + control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .e("body") + .t(defaultLanguage) + .up() + .e("body") + .t(otherLanguage) + .asString(outputProperties); + + message = (Message) PacketParserUtils + .parseMessage(getParser(control)); + + assertEquals(defaultLanguage, message.getBody()); + assertEquals(defaultLanguage, message.getBody(defaultLanguage)); + assertEquals(1, message.getBodies().size()); + assertEquals(0, message.getBodyLanguages().size()); + assertXMLNotEqual(control, message.toXML()); + + } + + @Test + public void invalidMessageBodyContainingTagTest() throws Exception { + String control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .a("xml:lang", "en") + .e("body") + .a("xmlns", "http://www.w3.org/1999/xhtml") + .e("span") + .a("style", "font-weight: bold;") + .t("Bad Message Body") + .asString(outputProperties); + + try { + Message message = (Message) PacketParserUtils.parseMessage(getParser(control)); + String body = "" + + "Bad Message Body"; + assertEquals(body, message.getBody()); + + assertXMLNotEqual(control, message.toXML()); + + DetailedDiff diffs = new DetailedDiff(new Diff(control, message.toXML())); + + // body has no namespace URI, span is escaped + assertEquals(4, diffs.getAllDifferences().size()); + } catch(XmlPullParserException e) { + fail("No parser exception should be thrown" + e.getMessage()); + } + + } + + @Test + public void invalidXMLInMessageBody() throws Exception { + String validControl = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .a("xml:lang", "en") + .e("body") + .t("Good Message Body") + .asString(outputProperties); + + String invalidControl = validControl.replace("Good Message Body", "Bad Body"); + + try { + PacketParserUtils.parseMessage(getParser(invalidControl)); + fail("Exception should be thrown"); + } catch(XmlPullParserException e) { + assertTrue(e.getMessage().contains("end tag name ")); + } + + invalidControl = validControl.replace("Good Message Body", "Bad Body"); + + try { + PacketParserUtils.parseMessage(getParser(invalidControl)); + fail("Exception should be thrown"); + } catch(XmlPullParserException e) { + assertTrue(e.getMessage().contains("end tag name ")); + } + + invalidControl = validControl.replace("Good Message Body", "Bad Body"); + + try { + PacketParserUtils.parseMessage(getParser(invalidControl)); + fail("Exception should be thrown"); + } catch(XmlPullParserException e) { + assertTrue(e.getMessage().contains("end tag name ")); + } + + } + @Test public void multipleMessageBodiesParsingTest() throws Exception { - final String messageBody1 = "This is a test of the emergency broadcast system, 1."; - final String lang2 = "ru"; - final String messageBody2 = "This is a test of the emergency broadcast system, 2."; - final String lang3 = "sp"; - final String messageBody3 = "This is a test of the emergency broadcast system, 3."; - - StringBuilder controlBuilder = new StringBuilder(); - controlBuilder.append("") - .append("") - .append(messageBody1) - .append("") - .append("") - .append(messageBody2) - .append("") - .append("") - .append(messageBody3) - .append("") - .append(""); - String control = controlBuilder.toString(); + String control = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .a("xml:lang", "en") + .e("body") + .t("This is a test of the emergency broadcast system, 1.") + .up() + .e("body") + .a("xml:lang", "ru") + .t("This is a test of the emergency broadcast system, 2.") + .up() + .e("body") + .a("xml:lang", "sp") + .t("This is a test of the emergency broadcast system, 3.") + .asString(outputProperties); + Packet message = PacketParserUtils.parseMessage(getParser(control)); assertXMLEqual(control, message.toXML()); } @@ -60,4 +446,17 @@ public class PacketParserUtilsTest { } return parser; } + + private String determineNonDefaultLanguage() { + String otherLanguage = "jp"; + Locale[] availableLocales = Locale.getAvailableLocales(); + for (int i = 0; i < availableLocales.length; i++) { + if (availableLocales[i] != Locale.getDefault()) { + otherLanguage = availableLocales[i].getLanguage().toLowerCase(); + break; + } + } + return otherLanguage; + } + }