Validating BCP-47 language tag using Java APIs
Asked Answered
H

1

8

As far as I know, the only way to validate whether a given BCP-47 language tag is valid is to use the following idiom:

private static boolean isValid(String tag) {
  try {
    new Locale.Builder().setLanguageTag(tag).build();
    return true;
  } catch (IllformedLocaleException e) {
    return false;
  }
}

However, the downside of this approach is that setLanguageTag throws an exception which has noticeable (in a profile) performance overhead in workloads where locales are checked often.

The setLanguageTag function is implemented using sun.util.locale APIs, and as far as I can tell it's the only place where sun.util.locale.ParseStatus is checked.

What I would like to be able to do is to use a method which has the following semantics:

import sun.util.locale.LanguageTag;
import sun.util.locale.ParseStatus;

private static boolean isValid(String tag) {
  ParseStatus sts = new ParseStatus();
  LanguageTag.parse(tag, sts);
  return !sts.isError();
}

However, it's not possible to check the locale in the above way since it's using sun.* classes directly since it requires additional JDK options to export sun.util.locale from the java.base module.

Is there a way to validate a language tag without using private sun.* APIs while being consistent with the implementation of sun.util.locale.LanguageTag#parse?

Hyacinthe answered 22/3, 2020 at 3:12 Comment(1)
I'd be interesting in a solution to this problem too. Have you found a way?Statistical
D
0

The simplest solution should be:

boolean isValidBCP47 = "und".equals(Locale.forLanguageTag(tag))

The best solution is to make use of java.util.Locale's filtering to handle this for you.

Often we need to fallback to locales. For example; en-JP is what you want for English Speakers visiting a theme park in Japan. However; when en-JP is not present you likely want to fall back to just en. In addition, your platform likely doesn't support every locale and would require a check against a list of supported locales.

Using com.glide.Locale you can do the following:

ArrayList<Locale.LanguageRange> priorityList = new ArrayList<>();
priorityList.add(new Locale.LanguageRange("en-JP"));
priorityList.add(new Locale.LanguageRange("joking")); // invalid tag
priorityList.add(new Locale.LanguageRange("fr")); // unsupported tag
priorityList.add(new Locale.LanguageRange("en"));

ArrayList<String> supportedTags = new ArrayList<>();
supportedTags.add("ja");
supportedTags.add("en-JP");
supportedTags.add("en");

Locale.filterTags(priorityList, supportedTags, Locale.FilteringMode.AUTOSELECT_FILTERING);

// returns ArrayList ["en-JP", "en"]
Dibbrun answered 3/3, 2022 at 12:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.