Smack 4.4.6: Invalid xml:lang attribute in stanza sending when user changes android language 'regional preference' settings

Reported by aTalk user: Incompatible xml:lang value · Issue #206 · cmeng-git/atalk-android · GitHub

It was found that when an android user changes the device language ‘regional preference’, the customised preference setting is tagged along the Local.getDefault() value. Smack uses this value as xml:lang attribute as it, and causes problem in sending stanza.

However if user chooses other UI language for aTalk e.g. English instead of the Android System Default, then thing works OK.

Below is the lang attribute value being sent when both the temperature and day settings in regional preference are changed:

2023-11-06 08:25:26.090 16440-16547/org.atalk.hmos D/SMACK: SENT (0): 
    <stream:stream xmlns='jabber:client' to='atalk.sytes.net' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xml:lang='en-US-#u-fw-sun-mu-celsius'>
2023-11-06 08:25:26.097 16440-16548/org.atalk.hmos D/SMACK: RECV (0): ?xml version='1.0'?>
    <stream:stream id='6299765005438818111' version='1.0' xml:lang='en' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client'>
    <stream:error>
      <invalid-xml xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
      <text xml:lang='en' xmlns='urn:ietf:params:xml:ns:xmpp-streams'>
        Bad value of attribute &apos;xml:lang&apos; in tag &lt;stream:stream/&gt; qualified by namespace &apos;jabber:client&apos;
      </text>
    </stream:error>
2023-11-06 08:25:26.105 16440-16539/org.atalk.hmos E/(ProtocolProviderServiceJabberImpl.java:1208)#connectAndLogin: Encounter problem during XMPPConnection: invalid-xml You can read more about the meaning of this stream error at http://xmpp.org/rfcs/rfc6120.html#streams-error-conditions
    <stream:error><invalid-xml xmlns='urn:ietf:params:xml:ns:xmpp-streams'/><text xml:lang='en'>Bad value of attribute &apos;xml:lang&apos; in tag &lt;stream:stream/&gt; qualified by namespace &apos;jabber:client&apos;</text></stream:error>
2023-11-06 08:25:26.107 16440-16539/org.atalk.hmos E/(ProtocolProviderServiceJabberImpl.java:728)#register: Error registering: XMPPError: policy-violation - modify [invalid-xml You can read more about the meaning of this stream error at http://xmpp.org/rfcs/rfc6120.html#streams-error-conditions
    <stream:error><invalid-xml xmlns='urn:ietf:params:xml:ns:xmpp-streams'/><text xml:lang='en'>Bad value of attribute &apos;xml:lang&apos; in tag &lt;stream:stream/&gt; qualified by namespace &apos;jabber:client&apos;</text></stream:error>]
2023-11-06 08:25:26.108 16440-16551/org.atalk.hmos W/aTalk: [116] org.jivesoftware.smack.AbstractXMPPConnection.callConnectionClosedOnErrorListener() Connection XMPPTCPConnection[not-authenticated] (0) closed with error
    org.jivesoftware.smack.XMPPException$StreamErrorException: invalid-xml You can read more about the meaning of this stream error at http://xmpp.org/rfcs/rfc6120.html#streams-error-conditions
    <stream:error><invalid-xml xmlns='urn:ietf:params:xml:ns:xmpp-streams'/><text xml:lang='en'>Bad value of attribute &apos;xml:lang&apos; in tag &lt;stream:stream/&gt; qualified by namespace &apos;jabber:client&apos;</text></stream:error>
        at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets(XMPPTCPConnection.java:984)
        at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$700(XMPPTCPConnection.java:916)
        at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run(XMPPTCPConnection.java:939)
        at java.lang.Thread.run(Thread.java:1012)

Today I leared that the Locale toString representation can return ‘extensions’. I wonder if instead of using toString, we should use somehthing like the toLanguageTag Locale (Java Platform SE 8 )

This likely affects Openfire too.

As far as I can tell, XML 1.0 (Fifth Edition) § 2.12 Language Identification refers to BCP 47 for the values of xml:lang.

Now

xml:lang=‘en-US-#u-fw-sun-mu-celsius

seems to communicate that the first weekday (fw) is sunday and that the measurement unit (mu) is celsis.

Java’s Locale API is accepts this:

scala> Locale.forLanguageTag("en-US-#u-fw-sun-mu-celsius")
val res0: java.util.Locale = en_US

However, I am skeptical of the pound sign (#) in the locale/language String. Because my understanding of BCP 47 is that its ABNF does not allow for it.

Any any case, you should be able to override the default locale use by Smack via Smack’s ConnectionConfiguration.

Following are the test results for the various function calls:

Locale.forLanguageTag("en-US-#u-fw-sun-mu-celsius") will return:
- an empty Locale i.e. toString() = ""
locale.toLanguageTag() will return:
- "en-US-u-fw-sun-mu-celsius"

aTalk will use the proposed method:

override the default locale use by Smack via Smack’s ConnectionConfiguration

aTalk implements LocaleHelper.java class for user UI language selection; the getXmlLocale() will return the xmlLocale i.e. with the 'regional preference strip off for use in ConnectionConfiguration .

    // mLocale will have 'regional preference' value stripped off; mainly use for smack xml:lang
    private static Locale xmlLocale = Locale.getDefault();

    public static Locale getXmlLocale() {
        return xmlLocale;
    }

    /**
     * Update the app local as per specified language.
     *
     * @param context Base Context (ContextImpl)
     * @param language the new UI language
     * #return The new ContextImpl for use by caller
     */
    public static Context wrap(Context context, String language) {
        Configuration config = context.getResources().getConfiguration();

        Locale locale;
        if (TextUtils.isEmpty(language)) {
            // System default may contain regional preference i.e. 'en-US-#u-fw-sun-mu-celsius'
            locale = Resources.getSystem().getConfiguration().getLocales().get(0);

            language = locale.toString();
            int idx = language.indexOf("_");
            xmlLocale = (idx == -1) ? locale : new Locale(language.substring(0, 2), language.substring(idx + 1, idx + 3));
        }
        else {
            if (language.length() == 5 && language.charAt(2) == '_') {
                // language is in the form: en_US
                locale = new Locale(language.substring(0, 2), language.substring(3));
            }
            else {
                locale = new Locale(language);
            }
            xmlLocale = locale;
        }

        config.setLayoutDirection(locale);
        config.setLocale(locale);

        // Timber.d(new Exception(), "set locale: %s: %s", language, context);
        return context.createConfigurationContext(config);
    }

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.