|
17 | 17 | import static org.htmlunit.BrowserVersionFeatures.JS_INTL_V8_BREAK_ITERATOR; |
18 | 18 |
|
19 | 19 | import java.lang.reflect.Method; |
| 20 | +import java.util.ArrayList; |
| 21 | +import java.util.IllformedLocaleException; |
| 22 | +import java.util.LinkedHashSet; |
| 23 | +import java.util.List; |
20 | 24 | import java.util.Map; |
| 25 | +import java.util.Set; |
21 | 26 |
|
22 | 27 | import org.htmlunit.BrowserVersion; |
| 28 | +import org.htmlunit.corejs.javascript.Context; |
| 29 | +import org.htmlunit.corejs.javascript.Function; |
23 | 30 | import org.htmlunit.corejs.javascript.FunctionObject; |
| 31 | +import org.htmlunit.corejs.javascript.NativeArray; |
24 | 32 | import org.htmlunit.corejs.javascript.Scriptable; |
25 | 33 | import org.htmlunit.corejs.javascript.ScriptableObject; |
| 34 | +import org.htmlunit.corejs.javascript.TopLevel; |
26 | 35 | import org.htmlunit.javascript.HtmlUnitScriptable; |
27 | 36 | import org.htmlunit.javascript.JavaScriptEngine; |
28 | 37 | import org.htmlunit.javascript.configuration.AbstractJavaScriptConfiguration; |
29 | 38 | import org.htmlunit.javascript.configuration.ClassConfiguration; |
30 | 39 | import org.htmlunit.javascript.configuration.JsxClass; |
| 40 | +import org.htmlunit.javascript.configuration.JsxStaticFunction; |
31 | 41 |
|
32 | 42 | /** |
33 | 43 | * A JavaScript object for {@code Intl}. |
@@ -95,4 +105,72 @@ private static void defineStaticFunctions(final ClassConfiguration config, |
95 | 105 | } |
96 | 106 | } |
97 | 107 | } |
| 108 | + |
| 109 | + /** |
| 110 | + * Returns an array containing the canonical locale names. |
| 111 | + * Duplicates will be omitted and elements will be validated as structurally valid language tags. |
| 112 | + * |
| 113 | + * @param cx the current context |
| 114 | + * @param thisObj the scriptable this |
| 115 | + * @param args the arguments |
| 116 | + * @param funObj the function object |
| 117 | + * @return an array of canonical locale names |
| 118 | + * |
| 119 | + * @see <a href="https://tc39.es/ecma402/#sec-intl.getcanonicallocales">spec</a> |
| 120 | + */ |
| 121 | + @JsxStaticFunction |
| 122 | + public static Object getCanonicalLocales(final Context cx, final Scriptable thisObj, |
| 123 | + final Object[] args, final Function funObj) { |
| 124 | + if (args.length == 0 || JavaScriptEngine.isUndefined(args[0])) { |
| 125 | + return cx.newArray(TopLevel.getTopLevelScope(thisObj), new Object[0]); |
| 126 | + } |
| 127 | + |
| 128 | + final Object localesArgument = args[0]; |
| 129 | + if (localesArgument == null) { |
| 130 | + throw JavaScriptEngine.typeError("Cannot convert null to object"); |
| 131 | + } |
| 132 | + |
| 133 | + final List<String> languageTags = new ArrayList<>(); |
| 134 | + if (localesArgument instanceof String s) { |
| 135 | + languageTags.add(s); |
| 136 | + } |
| 137 | + else if (localesArgument instanceof Scriptable scriptable) { |
| 138 | + if ("String".equals(scriptable.getClassName()) || scriptable instanceof Locale) { |
| 139 | + languageTags.add(scriptable.toString()); |
| 140 | + } |
| 141 | + else if (scriptable instanceof NativeArray array) { |
| 142 | + for (int i = 0; i < array.getLength(); i++) { |
| 143 | + final Object elem = array.get(i); |
| 144 | + if (elem instanceof String s) { |
| 145 | + languageTags.add(s); |
| 146 | + } |
| 147 | + else if (elem instanceof Locale) { |
| 148 | + languageTags.add(elem.toString()); |
| 149 | + } |
| 150 | + else if (elem instanceof ScriptableObject) { |
| 151 | + languageTags.add(JavaScriptEngine.toString(elem)); |
| 152 | + } |
| 153 | + else { |
| 154 | + throw JavaScriptEngine.typeError("Invalid element in locales argument"); |
| 155 | + } |
| 156 | + } |
| 157 | + } |
| 158 | + else { |
| 159 | + languageTags.add(JavaScriptEngine.toString(localesArgument)); |
| 160 | + } |
| 161 | + } |
| 162 | + |
| 163 | + final Set<String> canonicalLocales = new LinkedHashSet<>(languageTags.size()); |
| 164 | + for (final String tag : languageTags) { |
| 165 | + try { |
| 166 | + canonicalLocales.add( |
| 167 | + new java.util.Locale.Builder().setLanguageTag(tag).build().toLanguageTag()); |
| 168 | + } |
| 169 | + catch (final IllformedLocaleException e) { |
| 170 | + throw JavaScriptEngine.rangeError("Invalid language tag: " + tag); |
| 171 | + } |
| 172 | + } |
| 173 | + |
| 174 | + return cx.newArray(TopLevel.getTopLevelScope(thisObj), canonicalLocales.toArray()); |
| 175 | + } |
98 | 176 | } |
0 commit comments