1 package org.unicode.cldr.unittest; 2 3 import com.google.common.collect.ImmutableSet; 4 import com.ibm.icu.dev.test.TestFmwk; 5 import java.io.IOException; 6 import java.util.ArrayList; 7 import java.util.Arrays; 8 import java.util.Collection; 9 import java.util.HashMap; 10 import java.util.HashSet; 11 import java.util.LinkedHashMap; 12 import java.util.List; 13 import java.util.Map; 14 import java.util.Map.Entry; 15 import java.util.Set; 16 import java.util.TreeSet; 17 import java.util.stream.Collectors; 18 import org.unicode.cldr.test.ExampleGenerator; 19 import org.unicode.cldr.test.ExampleGenerator.UnitLength; 20 import org.unicode.cldr.util.CLDRConfig; 21 import org.unicode.cldr.util.CLDRFile; 22 import org.unicode.cldr.util.CldrUtility; 23 import org.unicode.cldr.util.Factory; 24 import org.unicode.cldr.util.GrammarInfo; 25 import org.unicode.cldr.util.GrammarInfo.CaseValues; 26 import org.unicode.cldr.util.GrammarInfo.GrammaticalFeature; 27 import org.unicode.cldr.util.GrammarInfo.GrammaticalScope; 28 import org.unicode.cldr.util.GrammarInfo.GrammaticalTarget; 29 import org.unicode.cldr.util.Pair; 30 import org.unicode.cldr.util.PathStarrer; 31 import org.unicode.cldr.util.SupplementalDataInfo; 32 import org.unicode.cldr.util.SupplementalDataInfo.PluralInfo; 33 import org.unicode.cldr.util.SupplementalDataInfo.PluralInfo.Count; 34 import org.unicode.cldr.util.SupplementalDataInfo.PluralType; 35 import org.unicode.cldr.util.UnitPathType; 36 import org.unicode.cldr.util.With; 37 38 public class TestExampleGenerator extends TestFmwk { 39 40 boolean showTranslationPaths = 41 CldrUtility.getProperty("TestExampleGenerator:showTranslationPaths", false); 42 43 private static final SupplementalDataInfo SDI = SupplementalDataInfo.getInstance(); 44 CLDRConfig info = CLDRConfig.getInstance(); 45 main(String[] args)46 public static void main(String[] args) { 47 new TestExampleGenerator().run(args); 48 } 49 testCurrency()50 public void testCurrency() { 51 String[][] tests = { 52 { 53 "fr", 54 "one", 55 "〖❬1,23 ❭value-one〗〖❬0,00 ❭value-one〗", 56 "〖❬1,23❭_❬dollar des États-Unis❭〗〖❬1,23❭_❬euro❭〗〖❬0,00❭_❬dollar des États-Unis❭〗〖❬0,00❭_❬euro❭〗" 57 }, 58 { 59 "fr", 60 "other", 61 "〖❬2,34 ❭value-other〗〖❬3,45 ❭value-other〗", 62 "〖❬2,34❭_❬dollars des États-Unis❭〗〖❬2,34❭_❬euros❭〗〖❬3,45❭_❬dollars des États-Unis❭〗〖❬3,45❭_❬euros❭〗" 63 }, 64 {"en", "one", "〖❬1 ❭Bermudan dollar〗", "〖❬1❭ ❬US dollar❭〗〖❬1❭ ❬euro❭〗"}, 65 { 66 "en", 67 "other", 68 "〖❬1.23 ❭Bermudan dollars〗〖❬0.00 ❭Bermudan dollars〗", 69 "〖❬1.23❭ ❬US dollars❭〗〖❬1.23❭ ❬euros❭〗〖❬0.00❭ ❬US dollars❭〗〖❬0.00❭ ❬euros❭〗" 70 }, 71 }; 72 String sampleCurrencyPatternPrefix = 73 "//ldml/numbers/currencyFormats[@numberSystem=\"latn\"]/unitPattern[@count=\""; 74 String sampleCurrencyPrefix = 75 "//ldml/numbers/currencies/currency[@type=\"BMD\"]/displayName[@count=\""; 76 String sampleTemplateSuffix = "\"]"; 77 78 for (String[] row : tests) { 79 ExampleGenerator exampleGenerator = getExampleGenerator(row[0]); 80 String value = "value-" + row[1]; 81 82 String path = sampleCurrencyPrefix + row[1] + sampleTemplateSuffix; 83 String result = 84 ExampleGenerator.simplify(exampleGenerator.getExampleHtml(path, value), false); 85 assertEquals(row[0] + "-" + row[1] + "-BMD", row[2], result); 86 87 value = "{0}_{1}"; 88 path = sampleCurrencyPatternPrefix + row[1] + sampleTemplateSuffix; 89 result = ExampleGenerator.simplify(exampleGenerator.getExampleHtml(path, value), false); 90 assertEquals(row[0] + "-" + row[1] + "-pat", row[3], result); 91 } 92 } 93 94 /** 95 * Only add to this if the example should NEVER appear. <br> 96 * WARNING - do not disable the test by putting in too broad a match. Make sure the paths are 97 * reasonably granular. 98 */ 99 static final Set<String> DELIBERATE_EXCLUDED_EXAMPLES = 100 ImmutableSet.of( 101 "//ldml/layout/orientation/characterOrder", 102 "//ldml/layout/orientation/lineOrder", 103 "//ldml/characters/moreInformation", 104 "//ldml/numbers/symbols[@numberSystem=\"([^\"]*+)\"]/infinity", 105 "//ldml/numbers/symbols[@numberSystem=\"([^\"]*+)\"]/list", 106 "//ldml/numbers/symbols[@numberSystem=\"([^\"]*+)\"]/nan", 107 "//ldml/numbers/currencies/currency[@type=\"([^\"]*+)\"]/displayName", 108 "//ldml/localeDisplayNames/measurementSystemNames/measurementSystemName[@type=\"([^\"]*+)\"]", 109 // old format 110 "//ldml/numbers/symbols/infinity", 111 "//ldml/numbers/symbols/list", 112 "//ldml/numbers/symbols/nan", 113 "//ldml/posix/messages/nostr", 114 "//ldml/posix/messages/yesstr", 115 "//ldml/contextTransforms/contextTransformUsage[@type=\"([^\"]*+)\"]/contextTransform[@type=\"([^\"]*+)\"]", 116 "//ldml/characters/exemplarCharacters", 117 "//ldml/characters/exemplarCharacters[@type=\"([^\"]*+)\"]", 118 "//ldml/characters/parseLenients.*", 119 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/months/monthContext[@type=\"([^\"]*+)\"]/monthWidth[@type=\"([^\"]*+)\"]/month[@type=\"([^\"]*+)\"]", 120 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/days/dayContext[@type=\"([^\"]*+)\"]/dayWidth[@type=\"([^\"]*+)\"]/day[@type=\"([^\"]*+)\"]", 121 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/quarters/quarterContext[@type=\"([^\"]*+)\"]/quarterWidth[@type=\"([^\"]*+)\"]/quarter[@type=\"([^\"]*+)\"]", 122 "//ldml/dates/fields/field[@type=\"([^\"]*+)\"]/displayName", 123 "//ldml/dates/fields/field[@type=\"([^\"]*+)\"]/relative[@type=\"([^\"]*+)\"]", 124 "//ldml/dates/fields/field[@type=\"([^\"]*+)\"]/relativeTime[@type=\"([^\"]*+)\"]/relativeTimePattern[@count=\"([^\"]*+)\"]", 125 "//ldml/dates/fields/field[@type=\"([^\"]*+)\"]/relativePeriod", 126 "//ldml/dates/fields/field[@type=\"([^\"]*+)\"]/displayName[@alt=\"([^\"]*+)\"]", 127 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/cyclicNameSets/cyclicNameSet[@type=\"([^\"]*+)\"]/cyclicNameContext[@type=\"([^\"]*+)\"]/cyclicNameWidth[@type=\"([^\"]*+)\"]/cyclicName[@type=\"([^\"]*+)\"]", 128 "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\"([^\"]*+)\"]", 129 "//ldml/numbers/minimalPairs/ordinalMinimalPairs[@ordinal=\"([^\"]*+)\"]", 130 "//ldml/characters/parseLenients[@scope=\"([^\"]*+)\"][@level=\"([^\"]*+)\"]/parseLenient[@sample=\"([^\"]*+)\"]"); 131 // Only add to above if the example should NEVER appear. 132 133 /** 134 * Add to this if the example SHOULD appear, but we don't have it yet. <br> 135 * TODO Add later 136 */ 137 static final Set<String> TEMPORARY_EXCLUDED_EXAMPLES = 138 ImmutableSet.of( 139 "//ldml/numbers/currencyFormats/currencySpacing/beforeCurrency/currencyMatch", 140 "//ldml/numbers/currencyFormats/currencySpacing/beforeCurrency/surroundingMatch", 141 "//ldml/numbers/currencyFormats/currencySpacing/beforeCurrency/insertBetween", 142 "//ldml/numbers/currencyFormats/currencySpacing/afterCurrency/currencyMatch", 143 "//ldml/numbers/currencyFormats/currencySpacing/afterCurrency/surroundingMatch", 144 "//ldml/numbers/currencyFormats/currencySpacing/afterCurrency/insertBetween", 145 "//ldml/numbers/currencyFormats/currencyPatternAppendISO", // TODO see 146 // CLDR-14831 147 "//ldml/numbers/currencyFormats[@numberSystem=\"([^\"]*+)\"]/currencySpacing/beforeCurrency/currencyMatch", 148 "//ldml/numbers/currencyFormats[@numberSystem=\"([^\"]*+)\"]/currencySpacing/beforeCurrency/surroundingMatch", 149 "//ldml/numbers/currencyFormats[@numberSystem=\"([^\"]*+)\"]/currencySpacing/beforeCurrency/insertBetween", 150 "//ldml/numbers/currencyFormats[@numberSystem=\"([^\"]*+)\"]/currencySpacing/afterCurrency/currencyMatch", 151 "//ldml/numbers/currencyFormats[@numberSystem=\"([^\"]*+)\"]/currencySpacing/afterCurrency/surroundingMatch", 152 "//ldml/numbers/currencyFormats[@numberSystem=\"([^\"]*+)\"]/currencySpacing/afterCurrency/insertBetween", 153 "//ldml/numbers/currencyFormats[@numberSystem=\"([^\"]*+)\"]/currencyPatternAppendISO", // TODO see CLDR-14831 154 "//ldml/localeDisplayNames/variants/variant[@type=\"([^\"]*+)\"]", 155 "//ldml/localeDisplayNames/keys/key[@type=\"([^\"]*+)\"]", 156 "//ldml/localeDisplayNames/types/type[@key=\"([^\"]*+)\"][@type=\"([^\"]*+)\"]", 157 "//ldml/localeDisplayNames/types/type[@key=\"([^\"]*+)\"][@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", 158 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraNames/era[@type=\"([^\"]*+)\"]", 159 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraAbbr/era[@type=\"([^\"]*+)\"]", 160 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraNarrow/era[@type=\"([^\"]*+)\"]", 161 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateFormats/dateFormatLength[@type=\"([^\"]*+)\"]/dateFormat[@type=\"([^\"]*+)\"]/datetimeSkeleton", 162 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/timeFormats/timeFormatLength[@type=\"([^\"]*+)\"]/timeFormat[@type=\"([^\"]*+)\"]/datetimeSkeleton", 163 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateFormats/dateFormatLength[@type=\"([^\"]*+)\"]/datetimeSkeleton", 164 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/timeFormats/timeFormatLength[@type=\"([^\"]*+)\"]/datetimeSkeleton", 165 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/appendItems/appendItem[@request=\"([^\"]*+)\"]", 166 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/intervalFormats/intervalFormatFallback", 167 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/intervalFormats/intervalFormatItem[@id=\"([^\"]*+)\"]/greatestDifference[@id=\"([^\"]*+)\"]", 168 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraNames/era[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", 169 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraAbbr/era[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", 170 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraNarrow/era[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", 171 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/months/monthContext[@type=\"([^\"]*+)\"]/monthWidth[@type=\"([^\"]*+)\"]/month[@type=\"([^\"]*+)\"][@yeartype=\"([^\"]*+)\"]", 172 "//ldml/dates/timeZoneNames/gmtZeroFormat", 173 "//ldml/numbers/minimumGroupingDigits", 174 "//ldml/numbers/symbols/timeSeparator", 175 "//ldml/numbers/symbols[@numberSystem=\"([^\"]*+)\"]/timeSeparator", 176 "//ldml/units/unitLength[@type=\"([^\"]*+)\"]/unit[@type=\"([^\"]*+)\"]/displayName", 177 "//ldml/units/unitLength[@type=\"([^\"]*+)\"]/unit[@type=\"([^\"]*+)\"]/perUnitPattern", 178 "//ldml/units/unitLength[@type=\"([^\"]*+)\"]/coordinateUnit/coordinateUnitPattern[@type=\"([^\"]*+)\"]", 179 "//ldml/units/unitLength[@type=\"([^\"]*+)\"]/coordinateUnit/displayName", 180 "//ldml/characterLabels/characterLabelPattern[@type=\"([^\"]*+)\"]", 181 "//ldml/characterLabels/characterLabelPattern[@type=\"([^\"]*+)\"][@count=\"([^\"]*+)\"]", 182 "//ldml/characterLabels/characterLabel[@type=\"([^\"]*+)\"]", 183 "//ldml/typographicNames/axisName[@type=\"([^\"]*+)\"]", 184 "//ldml/typographicNames/styleName[@type=\"([^\"]*+)\"][@subtype=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", 185 "//ldml/typographicNames/styleName[@type=\"([^\"]*+)\"][@subtype=\"([^\"]*+)\"]", 186 "//ldml/typographicNames/featureName[@type=\"([^\"]*+)\"]", 187 "//ldml/localeDisplayNames/subdivisions/subdivision[@type=\"([^\"]*+)\"]", 188 "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/long/standard", // Error: 189 // (TestExampleGenerator.java:245) No background: <Coordinated Universal Time> 190 // 〖Coordinated Universal Time〗 191 "//ldml/personNames/nameOrderLocales[@order=\"([^\"]*+)\"]", // TODO CLDR-15384 192 "//ldml/personNames/foreignSpaceReplacement[@xml:space=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", // TODO CLDR-15384 193 "//ldml/personNames/foreignSpaceReplacement[@xml:space=\"([^\"]*+)\"]", // TODO 194 "//ldml/personNames/foreignSpaceReplacement[@alt=\"([^\"]*+)\"]", 195 "//ldml/personNames/foreignSpaceReplacement", // TODO CLDR-15384 196 "//ldml/personNames/nativeSpaceReplacement[@xml:space=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", // TODO CLDR-15384 197 "//ldml/personNames/nativeSpaceReplacement[@xml:space=\"([^\"]*+)\"]", // TODO 198 "//ldml/personNames/nativeSpaceReplacement[@alt=\"([^\"]*+)\"]", 199 "//ldml/personNames/nativeSpaceReplacement", // TODO CLDR-15384 200 "//ldml/personNames/initialPattern[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", // TODO CLDR-15384 201 "//ldml/personNames/initialPattern[@type=\"([^\"]*+)\"]", // TODO CLDR-15384 202 "//ldml/personNames/personName[@order=\"([^\"]*+)\"][@length=\"([^\"]*+)\"][@usage=\"([^\"]*+)\"][@formality=\"([^\"]*+)\"]/namePattern[@alt=\"([^\"]*+)\"]", // TODO CLDR-15384 203 "//ldml/personNames/sampleName[@item=\"([^\"]*+)\"]/nameField[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", // TODO CLDR-15384 204 "//ldml/personNames/sampleName[@item=\"([^\"]*+)\"]/nameField[@type=\"([^\"]*+)\"]", // TODO CLDR-15384 205 "//ldml/personNames/parameterDefault[@parameter=\"([^\"]*+)\"]" // TODO 206 // CLDR-15384 207 ); 208 // Add to above if the example SHOULD appear, but we don't have it yet. TODO Add later 209 210 /** 211 * Only add to this if the background should NEVER appear. <br> 212 * The background is used when the element is used as part of another format. <br> 213 * WARNING - do not disable the test by putting in too broad a match. Make sure the paths are 214 * reasonably granular. 215 */ 216 static final Set<String> DELIBERATE_OK_TO_MISS_BACKGROUND = 217 ImmutableSet.of( 218 "//ldml/numbers/defaultNumberingSystem", 219 "//ldml/numbers/otherNumberingSystems/native", 220 // TODO fix formatting 221 "//ldml/characters/exemplarCharacters", 222 "//ldml/characters/exemplarCharacters[@type=\"([^\"]*+)\"]", 223 // TODO Add background 224 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/timeFormats/timeFormatLength[@type=\"([^\"]*+)\"]/timeFormat[@type=\"([^\"]*+)\"]/pattern[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", // CLDR-16606 225 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/availableFormats/dateFormatItem[@id=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", // CLDR-16606 226 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateFormats/dateFormatLength[@type=\"([^\"]*+)\"]/dateFormat[@type=\"([^\"]*+)\"]/pattern[@type=\"([^\"]*+)\"]", 227 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/timeFormats/timeFormatLength[@type=\"([^\"]*+)\"]/timeFormat[@type=\"([^\"]*+)\"]/pattern[@type=\"([^\"]*+)\"]", 228 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/availableFormats/dateFormatItem[@id=\"([^\"]*+)\"]", 229 "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/exemplarCity", 230 "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/exemplarCity[@alt=\"([^\"]*+)\"]", 231 "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/long/daylight", 232 "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/short/generic", 233 "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/short/standard", 234 "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/short/daylight", 235 "//ldml/dates/timeZoneNames/metazone[@type=\"([^\"]*+)\"]/long/generic", 236 "//ldml/dates/timeZoneNames/metazone[@type=\"([^\"]*+)\"]/long/standard", 237 "//ldml/dates/timeZoneNames/metazone[@type=\"([^\"]*+)\"]/long/daylight", 238 "//ldml/units/durationUnit[@type=\"([^\"]*+)\"]/durationUnitPattern"); 239 // Only add to above if the background should NEVER appear. 240 241 /** 242 * Add to this if the background SHOULD appear, but we don't have them yet. <br> 243 * The background is used when the element is used as part of another format. <br> 244 * TODO Add later 245 */ 246 static final Set<String> TEMPORARY_OK_TO_MISS_BACKGROUND = 247 ImmutableSet.of( 248 "//ldml/numbers/defaultNumberingSystem", 249 "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/availableFormats/dateFormatItem[@id=\"([^\"]*+)\"][@count=\"([^\"]*+)\"]", 250 "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/long/standard", 251 "//ldml/dates/timeZoneNames/metazone[@type=\"([^\"]*+)\"]/short/generic", 252 "//ldml/dates/timeZoneNames/metazone[@type=\"([^\"]*+)\"]/short/standard", 253 "//ldml/dates/timeZoneNames/metazone[@type=\"([^\"]*+)\"]/short/daylight", 254 "//ldml/personNames/personName[@order=\"([^\"]*+)\"][@length=\"([^\"]*+)\"][@formality=\"([^\"]*+)\"]/namePattern", 255 "//ldml/personNames/personName[@order=\"([^\"]*+)\"][@length=\"([^\"]*+)\"][@usage=\"([^\"]*+)\"][@formality=\"([^\"]*+)\"]/namePattern"); // CLDR-15384 256 // Add to above if the background SHOULD appear, but we don't have them yet. TODO Add later 257 TestAllPaths()258 public void TestAllPaths() { 259 ExampleGenerator exampleGenerator = getExampleGenerator("en"); 260 PathStarrer ps = new PathStarrer(); 261 Set<String> seen = new HashSet<>(); 262 CLDRFile cldrFile = exampleGenerator.getCldrFile(); 263 TreeSet<String> target = new TreeSet<>(cldrFile.getComparator()); 264 cldrFile.fullIterable().forEach(target::add); 265 for (String path : target) { 266 String plainStarred = ps.set(path); 267 String value = cldrFile.getStringValue(path); 268 if (value == null 269 || path.endsWith("/alias") 270 || path.startsWith("//ldml/identity") 271 || DELIBERATE_EXCLUDED_EXAMPLES.contains(plainStarred)) { 272 continue; 273 } 274 if (TEMPORARY_EXCLUDED_EXAMPLES.contains(plainStarred)) { 275 if (logKnownIssue( 276 "Cldrbug:6342", 277 "Need an example for each path used in context: " + plainStarred)) { 278 continue; 279 } 280 continue; 281 } 282 String example = exampleGenerator.getExampleHtml(path, value); 283 String javaEscapedStarred = "\"" + plainStarred.replace("\"", "\\\"") + "\","; 284 if (example == null) { 285 if (!seen.contains(javaEscapedStarred)) { 286 errln("No example:\t<" + value + ">\t" + javaEscapedStarred); 287 } 288 } else { 289 String simplified = ExampleGenerator.simplify(example, false); 290 291 if (simplified.contains("null")) { 292 if (true || !seen.contains(javaEscapedStarred)) { 293 // debug 294 exampleGenerator.getExampleHtml(path, value); 295 ExampleGenerator.simplify(example, false); 296 297 errln( 298 "'null' in message:\t<" 299 + value 300 + ">\t" 301 + simplified 302 + "\t" 303 + javaEscapedStarred); 304 // String example2 = 305 // exampleGenerator.getExampleHtml(path, value); // for 306 // debugging 307 } 308 } else if (!simplified.startsWith("〖")) { 309 if (!seen.contains(javaEscapedStarred)) { 310 errln( 311 "Funny HTML:\t<" 312 + value 313 + ">\t" 314 + simplified 315 + "\t" 316 + javaEscapedStarred); 317 } 318 } else if (!simplified.contains("❬") 319 && !DELIBERATE_OK_TO_MISS_BACKGROUND.contains(plainStarred)) { 320 if (!seen.contains(javaEscapedStarred)) { 321 322 if (TEMPORARY_OK_TO_MISS_BACKGROUND.contains(plainStarred) 323 && logKnownIssue( 324 "Cldrbug:6342", 325 "Make sure that background appears: " 326 + simplified 327 + "; " 328 + plainStarred)) { 329 continue; 330 } 331 332 errln( 333 "No background:\t<" 334 + value 335 + ">\t" 336 + simplified 337 + "\t" 338 + javaEscapedStarred); 339 } 340 } 341 } 342 seen.add(javaEscapedStarred); 343 } 344 } 345 TestUnits()346 public void TestUnits() { 347 ExampleGenerator exampleGenerator = getExampleGenerator("en"); 348 String staticMeterExample = 349 "〖1 meter ≡ 1,000 millimeter〗〖1 meter ≈ 1.0936 yard (US/UK)〗〖1 meter ≡ 1/1000 kilometer〗〖1 meter ≈ 621.37×10ˆ-6 mile (US/UK)〗"; 350 String staticMeterExampleJp = 351 "〖1 meter ≡ 1,000 millimeter〗〖1 meter ≡ 3.025 jo-jp (JP)〗〖1 meter ≈ 1.0936 yard (US/UK)〗〖1 meter ≈ 0.0023341 ri-jp (JP)〗〖1 meter ≡ 1/1000 kilometer〗〖1 meter ≈ 621.37×10ˆ-6 mile (US/UK)〗"; 352 String staticMeterExampleRi = 353 "〖1 ri-jp (JP) ≡ 1,296 jo-jp (JP)〗〖1 ri-jp (JP) ≈ 468.54 yard (US/UK)〗〖1 ri-jp (JP) ≈ 428.43 meter〗〖1 ri-jp (JP) ≈ 0.42843 kilometer〗〖1 ri-jp (JP) ≈ 0.26621 mile (US/UK)〗"; 354 355 checkValue( 356 "Length m", 357 staticMeterExample, 358 exampleGenerator, 359 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"length-meter\"]/displayName"); 360 checkValue( 361 "Duration hm", 362 "〖5:37〗", 363 exampleGenerator, 364 "//ldml/units/durationUnit[@type=\"hm\"]/durationUnitPattern"); 365 checkValue( 366 "Length m", 367 "〖❬1❭ meter〗〖〗" + staticMeterExample, 368 exampleGenerator, 369 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"one\"]"); 370 checkValue( 371 "Length m", 372 "〖❬1.5❭ meters〗〖〗" + staticMeterExample, 373 exampleGenerator, 374 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"other\"]"); 375 checkValue( 376 "Length m", 377 "〖❬1.5❭ m〗〖〗" + staticMeterExample, 378 exampleGenerator, 379 "//ldml/units/unitLength[@type=\"short\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"other\"]"); 380 checkValue( 381 "Length m", 382 "〖❬1.5❭m〗〖〗" + staticMeterExample, 383 exampleGenerator, 384 "//ldml/units/unitLength[@type=\"narrow\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"other\"]"); 385 386 // The following are to ensure that we properly generate an example when we have a 387 // non-winning value 388 checkValue( 389 "Length m", 390 "〖❬1.5❭ badmeter〗〖〗" + staticMeterExample, 391 exampleGenerator, 392 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"other\"]", 393 "{0} badmeter"); 394 395 ExampleGenerator exampleGeneratorDe = getExampleGenerator("de"); 396 checkValue( 397 "Length m", 398 "〖❬1,5❭ badmeter〗〖❬Anstatt 1,5❭ badmeter❬ …❭〗〖❌ ❬… für 1,5❭ badmeter❬ …❭〗〖〗" 399 + staticMeterExample, 400 exampleGeneratorDe, 401 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"other\"][@case=\"genitive\"]", 402 "{0} badmeter"); 403 404 ExampleGenerator exampleGeneratorJa = getExampleGenerator("ja"); 405 checkValue( 406 "Length m", 407 "〖❬1.5❭m〗〖〗" + staticMeterExampleJp, 408 exampleGeneratorJa, 409 "//ldml/units/unitLength[@type=\"narrow\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"other\"]"); 410 checkValue( 411 "Length ri", 412 "〖❬1.5❭ 里〗〖〗" + staticMeterExampleRi, 413 exampleGeneratorJa, 414 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"length-ri-jp\"]/unitPattern[@count=\"other\"]"); 415 } 416 417 /** 418 * Check that the expected exampleGenerator example is produced for the parameters, with the 419 * value coming from the file. 420 */ checkValue( String message, String expected, ExampleGenerator exampleGenerator, String path)421 private void checkValue( 422 String message, String expected, ExampleGenerator exampleGenerator, String path) { 423 checkValue(message, expected, exampleGenerator, path, null); 424 } 425 426 /** Check that the expected exampleGenerator example is produced for the parameters */ checkValue( String message, String expected, ExampleGenerator exampleGenerator, String path, String value)427 private void checkValue( 428 String message, 429 String expected, 430 ExampleGenerator exampleGenerator, 431 String path, 432 String value) { 433 final CLDRFile cldrFile = exampleGenerator.getCldrFile(); 434 value = value != null ? value : cldrFile.getStringValue(path); 435 String actual = exampleGenerator.getExampleHtml(path, value); 436 assertEquals( 437 cldrFile.getLocaleID() + ": " + message, 438 expected, 439 ExampleGenerator.simplify(actual, false)); 440 } 441 TestCompoundUnit()442 public void TestCompoundUnit() { 443 String[][] tests = { 444 {"per", "LONG", "one", "〖❬1 meter❭ per ❬second❭〗"}, 445 {"per", "SHORT", "one", "〖❬1 m❭/❬sec❭〗"}, 446 {"per", "NARROW", "one", "〖❬1m❭/❬s❭〗"}, 447 {"per", "LONG", "other", "〖❬1.5 meters❭ per ❬second❭〗"}, 448 {"per", "SHORT", "other", "〖❬1.5 m❭/❬sec❭〗"}, 449 {"per", "NARROW", "other", "〖❬1.5m❭/❬s❭〗"}, 450 {"times", "LONG", "one", "〖❬1 newton❭-❬meter❭〗"}, 451 {"times", "SHORT", "one", "〖❬1 N❭⋅❬m❭〗"}, 452 {"times", "NARROW", "one", "〖❬1N❭⋅❬m❭〗"}, 453 {"times", "LONG", "other", "〖❬1.5 newton❭-❬meters❭〗"}, 454 {"times", "SHORT", "other", "〖❬1.5 N❭⋅❬m❭〗"}, 455 {"times", "NARROW", "other", "〖❬1.5N❭⋅❬m❭〗"}, 456 }; 457 checkCompoundUnits("en", tests); 458 // reenable these after Arabic has meter translated 459 // String[][] tests2 = { 460 // {"LONG", "few", "〖❬1 meter❭ per ❬second❭〗"}, 461 // }; 462 // checkCompoundUnits("ar", tests2); 463 } 464 checkCompoundUnits(String locale, String[][] tests)465 private void checkCompoundUnits(String locale, String[][] tests) { 466 ExampleGenerator exampleGenerator = getExampleGenerator(locale); 467 for (String[] test : tests) { 468 String actual = 469 exampleGenerator.handleCompoundUnit( 470 UnitLength.valueOf(test[1]), test[0], Count.valueOf(test[2])); 471 assertEquals("CompoundUnit", test[3], ExampleGenerator.simplify(actual, true)); 472 } 473 } 474 TestTranslationPaths()475 public void TestTranslationPaths() { 476 for (String locale : Arrays.asList("en", "el", "ru")) { 477 CLDRFile cldrFile = CLDRConfig.getInstance().getCldrFactory().make(locale, true); 478 ExampleGenerator exampleGenerator = getExampleGenerator(locale); 479 480 for (UnitPathType pathType : UnitPathType.values()) { 481 for (String width : Arrays.asList("long", "short", "narrow")) { 482 if (pathType == UnitPathType.gender && !width.equals("long")) { 483 continue; 484 } 485 for (String unit : pathType.sampleShortUnitType) { 486 String path = 487 pathType.getTranslationPath( 488 cldrFile, width, unit, "one", "nominative", null); 489 String value = cldrFile.getStringValue(path); 490 if (value != null) { 491 String example = exampleGenerator.getExampleHtml(path, value); 492 if (assertNotNull(locale + "/" + path, example)) { 493 String simplified = ExampleGenerator.simplify(example, false); 494 if (showTranslationPaths) { 495 warnln( 496 locale 497 + ", " 498 + width 499 + ", " 500 + pathType.toString() 501 + " ==>" 502 + simplified); 503 } 504 } else { 505 // for debugging 506 example = exampleGenerator.getExampleHtml(path, value); 507 } 508 } 509 } 510 } 511 } 512 } 513 } 514 TestCompoundUnit2()515 public void TestCompoundUnit2() { 516 String[][] tests = { 517 {"de", "LONG", "other", "Quadrat{0}", "〖❬1,5 ❭Quadrat❬meter❭〗"}, 518 {"en", "SHORT", "one", "z{0}", "〖❬1 ❭z❬m❭〗"}, 519 {"en", "LONG", "other", "zetta{0}", "〖❬1.5 ❭zetta❬meters❭〗"}, 520 {"en", "SHORT", "one", "{0}²", "〖❬1 m❭²〗"}, 521 {"en", "LONG", "other", "square {0}", "〖❬1.5 ❭square ❬meters❭〗"}, 522 {"de", "SHORT", "one", "z{0}", "〖❬1 ❭z❬m❭〗"}, 523 {"de", "LONG", "other", "Zetta{0}", "〖❬1,5 ❭Zetta❬meter❭〗"}, 524 {"de", "SHORT", "one", "{0}²", "〖❬1 m❭²〗"}, 525 {"de", "LONG", "other", "Quadrat{0}", "〖❬1,5 ❭Quadrat❬meter❭〗"}, 526 }; 527 for (String[] test : tests) { 528 529 ExampleGenerator exampleGenerator = getExampleGenerator(test[0]); 530 531 String actual = 532 exampleGenerator.handleCompoundUnit1( 533 UnitLength.valueOf(test[1]), Count.valueOf(test[2]), test[3]); 534 assertEquals("CompoundUnit", test[4], ExampleGenerator.simplify(actual, true)); 535 } 536 } 537 TestCompoundUnit3()538 public void TestCompoundUnit3() { 539 final Factory cldrFactory = CLDRConfig.getInstance().getCldrFactory(); 540 String[][] tests = { 541 // locale, path, value, expected-example 542 { 543 "en", 544 "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1", 545 "LOCALE", 546 "〖square ❬meters❭〗" 547 }, // 548 { 549 "en", 550 "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"]", 551 "LOCALE", 552 "〖❬1 ❭square ❬meter❭〗" 553 }, // 554 { 555 "en", 556 "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"]", 557 "LOCALE", 558 "〖❬1.5 ❭square ❬meters❭〗" 559 }, // 560 { 561 "en", 562 "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1", 563 "LOCALE", 564 "〖❬m❭²〗" 565 }, 566 { 567 "en", 568 "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"]", 569 "LOCALE", 570 "〖❬1m❭²〗" 571 }, 572 { 573 "en", 574 "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"]", 575 "LOCALE", 576 "〖❬1.5m❭²〗" 577 }, 578 579 // warning, french patterns has U+00A0 in them 580 { 581 "fr", 582 "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1", 583 "Square {0}", 584 "〖Square ❬mètres❭〗" 585 }, 586 { 587 "fr", 588 "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"]", 589 "square {0}", 590 "〖❬1,5 ❭square ❬mètre❭〗" 591 }, 592 { 593 "fr", 594 "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"]", 595 "squares {0}", 596 "〖❬3,5 ❭squares ❬mètres❭〗" 597 }, 598 { 599 "fr", 600 "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1", 601 "LOCALE", 602 "〖❬m❭²〗" 603 }, 604 { 605 "fr", 606 "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"]", 607 "LOCALE", 608 "〖❬1,5m❭²〗" 609 }, 610 { 611 "fr", 612 "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"]", 613 "LOCALE", 614 "〖❬3,5m❭²〗" 615 }, 616 }; 617 618 int lineCount = 0; 619 for (String[] test : tests) { 620 621 final String localeID = test[0]; 622 final String xpath = test[1]; 623 String value = test[2]; 624 String expected = test[3]; 625 626 ExampleGenerator exampleGenerator = getExampleGenerator(localeID); 627 628 if (value.equals("LOCALE")) { 629 value = cldrFactory.make(localeID, true).getStringValue(xpath); 630 } 631 String actual = exampleGenerator.getExampleHtml(xpath, value); 632 assertEquals( 633 ++lineCount + ") " + localeID + ", CompoundUnit3", 634 expected, 635 ExampleGenerator.simplify(actual, false)); 636 } 637 } 638 639 HashMap<String, ExampleGenerator> ExampleGeneratorCache = new HashMap<>(); 640 getExampleGenerator(String locale)641 private ExampleGenerator getExampleGenerator(String locale) { 642 ExampleGenerator result = ExampleGeneratorCache.get(locale); 643 if (result == null) { 644 final CLDRFile nativeCldrFile = info.getCLDRFile(locale, true); 645 result = new ExampleGenerator(nativeCldrFile, info.getEnglish()); 646 ExampleGeneratorCache.put(locale, result); 647 } 648 return result; 649 } 650 TestEllipsis()651 public void TestEllipsis() { 652 ExampleGenerator exampleGenerator = getExampleGenerator("it"); 653 String[][] tests = { 654 {"initial", "〖…❬iappone❭〗"}, 655 {"medial", "〖❬Svizzer❭…❬iappone❭〗"}, 656 {"final", "〖❬Svizzer❭…〗"}, 657 {"word-initial", "〖… ❬Giappone❭〗"}, 658 {"word-medial", "〖❬Svizzera❭ … ❬Giappone❭〗"}, 659 {"word-final", "〖❬Svizzera❭ …〗"}, 660 }; 661 for (String[] pair : tests) { 662 checkValue( 663 exampleGenerator, 664 "//ldml/characters/ellipsis[@type=\"" + pair[0] + "\"]", 665 pair[1]); 666 } 667 } 668 checkValue(ExampleGenerator exampleGenerator, String path, String expected)669 private void checkValue(ExampleGenerator exampleGenerator, String path, String expected) { 670 String value = exampleGenerator.getCldrFile().getStringValue(path); 671 String result = 672 ExampleGenerator.simplify(exampleGenerator.getExampleHtml(path, value), false); 673 assertEquals("Ellipsis", expected, result); 674 } 675 simplify(String exampleHtml)676 public static String simplify(String exampleHtml) { 677 return ExampleGenerator.simplify(exampleHtml, false); 678 } 679 TestClip()680 public void TestClip() { 681 assertEquals("Clipping", "bc", ExampleGenerator.clip("abc", 1, 0)); 682 assertEquals("Clipping", "ab", ExampleGenerator.clip("abc", 0, 1)); 683 assertEquals( 684 "Clipping", "b\u0308c\u0308", ExampleGenerator.clip("a\u0308b\u0308c\u0308", 1, 0)); 685 assertEquals( 686 "Clipping", "a\u0308b\u0308", ExampleGenerator.clip("a\u0308b\u0308c\u0308", 0, 1)); 687 } 688 TestPaths()689 public void TestPaths() { 690 showCldrFile(info.getEnglish()); 691 showCldrFile(info.getCLDRFile("fr", true)); 692 } 693 TestMiscPatterns()694 public void TestMiscPatterns() { 695 ExampleGenerator exampleGenerator = getExampleGenerator("it"); 696 checkValue( 697 "At least", 698 "〖≥❬99❭〗", 699 exampleGenerator, 700 "//ldml/numbers/miscPatterns[@numberSystem=\"latn\"]/pattern[@type=\"atLeast\"]"); 701 checkValue( 702 "Range", 703 "〖❬99❭-❬144❭〗", 704 exampleGenerator, 705 "//ldml/numbers/miscPatterns[@numberSystem=\"latn\"]/pattern[@type=\"range\"]"); 706 // String actual = exampleGenerator.getExampleHtml( 707 // "//ldml/numbers/miscPatterns[@type=\"arab\"]/pattern[@type=\"atLeast\"]", 708 // "at least {0}", Zoomed.IN); 709 // assertEquals("Invalid format", 710 // "<div class='cldr_example'>at least 99</div>", actual); 711 } 712 TestPluralSamples()713 public void TestPluralSamples() { 714 ExampleGenerator exampleGenerator = getExampleGenerator("sv"); 715 String[][] tests = { 716 { 717 "//ldml/units/unitLength[@type=\"short\"]/unit[@type=\"length-centimeter\"]/unitPattern[@count=\"one\"]", 718 "Number should be one", 719 "〖❬1❭ cm〗〖❬Jag tror att 1❭ cm❬ är tillräckligt.❭〗" 720 }, 721 { 722 "//ldml/numbers/minimalPairs/ordinalMinimalPairs[@ordinal=\"one\"]", 723 "Ordinal one", 724 "〖Ta ❬1❭:a svängen till höger〗〖❌ Ta ❬3❭:a svängen till höger〗" 725 }, 726 { 727 "//ldml/numbers/minimalPairs/ordinalMinimalPairs[@ordinal=\"other\"]", 728 "Ordinal other", 729 "〖Ta ❬3❭:e svängen till höger〗〖❌ Ta ❬1❭:e svängen till höger〗" 730 }, 731 }; 732 for (String[] row : tests) { 733 String path = row[0]; 734 String message = row[1]; 735 String expected = row[2]; 736 checkValue(message, expected, exampleGenerator, path); 737 } 738 } 739 TestLocaleDisplayPatterns()740 public void TestLocaleDisplayPatterns() { 741 ExampleGenerator exampleGenerator = getExampleGenerator("it"); 742 String actual = 743 exampleGenerator.getExampleHtml( 744 "//ldml/localeDisplayNames/localeDisplayPattern/localePattern", 745 "{0} [{1}]"); 746 assertEquals( 747 "localePattern example faulty", 748 "<div class='cldr_example'><span class='cldr_substituted'>uzbeco</span> [<span class='cldr_substituted'>Afghanistan</span>]</div>" 749 + "<div class='cldr_example'><span class='cldr_substituted'>uzbeco</span> [<span class='cldr_substituted'>arabo, Afghanistan</span>]</div>" 750 + "<div class='cldr_example'><span class='cldr_substituted'>uzbeco</span> [<span class='cldr_substituted'>arabo, Afghanistan, Cifre indo-arabe, Fuso orario: Ora Etiopia</span>]</div>", 751 actual); 752 actual = 753 exampleGenerator.getExampleHtml( 754 "//ldml/localeDisplayNames/localeDisplayPattern/localeSeparator", 755 "{0}. {1}"); 756 assertEquals( 757 "localeSeparator example faulty", 758 "<div class='cldr_example'><span class='cldr_substituted'>uzbeco (arabo</span>. <span class='cldr_substituted'>Afghanistan)</span></div>" 759 + "<div class='cldr_example'><span class='cldr_substituted'>uzbeco (arabo</span>. <span class='cldr_substituted'>Afghanistan</span>. <span class='cldr_substituted'>Cifre indo-arabe</span>. <span class='cldr_substituted'>Fuso orario: Ora Etiopia)</span></div>", 760 actual); 761 } 762 TestCurrencyFormats()763 public void TestCurrencyFormats() { 764 ExampleGenerator exampleGenerator = getExampleGenerator("it"); 765 String actual = 766 simplify( 767 exampleGenerator.getExampleHtml( 768 "//ldml/numbers/currencyFormats[@numberSystem=\"latn\"]/currencyFormatLength/currencyFormat[@type=\"standard\"]/pattern[@type=\"standard\"]", 769 "¤ #0.00")); 770 assertEquals("Currency format example faulty", "〖€ ❬1295,00❭〗〖-€ ❬1295,00❭〗", actual); 771 } 772 TestCurrencyFormatsWithContext()773 public void TestCurrencyFormatsWithContext() { 774 ExampleGenerator exampleGenerator = getExampleGenerator("he"); 775 String actual = 776 simplify( 777 exampleGenerator.getExampleHtml( 778 "//ldml/numbers/currencyFormats[@numberSystem=\"latn\"]/currencyFormatLength/currencyFormat[@type=\"standard\"]/pattern[@type=\"standard\"]", 779 "#,##0.00 ¤;-#,##0.00 ¤")); 780 assertEquals( 781 "Currency format example faulty", 782 "【❬1,295❭.❬00❭ ₪〗【⃪❬1,295❭.❬00❭ ₪〗【-❬1,295❭.❬00❭ ₪〗【⃪-❬1,295❭.❬00❭ ₪〗【❬1,295❭.❬00❭ ILS〗【⃪❬1,295❭.❬00❭ ILS〗【-❬1,295❭.❬00❭ ILS〗【⃪-❬1,295❭.❬00❭ ILS〗", 783 actual); 784 } 785 TestDateFormatsWithContext()786 public void TestDateFormatsWithContext() { 787 ExampleGenerator exampleGenerator = getExampleGenerator("ar"); 788 String actual = 789 simplify( 790 exampleGenerator.getExampleHtml( 791 "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dateFormats/dateFormatLength[@type=\"short\"]/dateFormat[@type=\"standard\"]/pattern[@type=\"standard\"]", 792 "d/M/y")); 793 assertEquals("Currency format example faulty", "【٥/٩/١٩٩٩〗【⃪٥/٩/١٩٩٩〗", actual); 794 } 795 TestDateTimeComboFormats()796 public void TestDateTimeComboFormats() { 797 ExampleGenerator exampleGenerator = getExampleGenerator("en"); 798 checkValue( 799 "DateTimeCombo long std", 800 "〖❬September 5, 1999❭, ❬1:25:59 PM Eastern Standard Time❭〗〖❬September 5, 1999❭, ❬1:25 PM❭〗〖❬September 5, 1999❭, ❬7:00 AM – 1:25 PM❭〗〖❬today❭, ❬7:00 AM – 1:25 PM❭〗", 801 exampleGenerator, 802 "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dateTimeFormats/dateTimeFormatLength[@type=\"long\"]/dateTimeFormat[@type=\"standard\"]/pattern[@type=\"standard\"]"); 803 checkValue( 804 "DateTimeCombo short std", 805 "〖❬9/5/99❭, ❬1:25:59 PM Eastern Standard Time❭〗〖❬9/5/99❭, ❬1:25 PM❭〗〖❬9/5/99❭, ❬7:00 AM – 1:25 PM❭〗〖❬today❭, ❬7:00 AM – 1:25 PM❭〗", 806 exampleGenerator, 807 "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dateTimeFormats/dateTimeFormatLength[@type=\"short\"]/dateTimeFormat[@type=\"standard\"]/pattern[@type=\"standard\"]"); 808 checkValue( 809 "DateTimeCombo long std", 810 "〖❬September 5, 1999❭ at ❬1:25:59 PM Eastern Standard Time❭〗〖❬September 5, 1999❭ at ❬1:25 PM❭〗〖❬today❭ at ❬1:25 PM❭〗", 811 exampleGenerator, 812 "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dateTimeFormats/dateTimeFormatLength[@type=\"long\"]/dateTimeFormat[@type=\"atTime\"]/pattern[@type=\"standard\"]"); 813 } 814 TestSymbols()815 public void TestSymbols() { 816 CLDRFile english = info.getEnglish(); 817 ExampleGenerator exampleGenerator = new ExampleGenerator(english, english); 818 String actual = 819 exampleGenerator.getExampleHtml( 820 "//ldml/numbers/symbols[@numberSystem=\"latn\"]/superscriptingExponent", 821 "x"); 822 823 assertEquals( 824 "superscriptingExponent faulty", 825 "<div class='cldr_example'><span class='cldr_substituted'>1.23456789</span>x10<span class='cldr_substituted'><sup>5</sup></span></div>", 826 actual); 827 } 828 TestFallbackFormat()829 public void TestFallbackFormat() { 830 ExampleGenerator exampleGenerator = 831 new ExampleGenerator(info.getEnglish(), info.getEnglish()); 832 String actual = 833 exampleGenerator.getExampleHtml( 834 "//ldml/dates/timeZoneNames/fallbackFormat", "{1} [{0}]"); 835 assertEquals( 836 "fallbackFormat faulty", 837 "〖❬Central Time❭ [❬Cancún❭]〗", 838 ExampleGenerator.simplify(actual, false)); 839 } 840 Test4897()841 public void Test4897() { 842 ExampleGenerator exampleGenerator = getExampleGenerator("it"); 843 final CLDRFile cldrFile = exampleGenerator.getCldrFile(); 844 for (String xpath : 845 With.in( 846 cldrFile.iterator( 847 "//ldml/dates/timeZoneNames", cldrFile.getComparator()))) { 848 String value = cldrFile.getStringValue(xpath); 849 String actual = exampleGenerator.getExampleHtml(xpath, value); 850 if (actual == null) { 851 if (!xpath.contains("singleCountries") && !xpath.contains("gmtZeroFormat")) { 852 errln("Null value for " + value + "\t" + xpath); 853 // for debugging 854 exampleGenerator.getExampleHtml(xpath, value); 855 } 856 } else { 857 logln(actual + "\t" + value + "\t" + xpath); 858 } 859 } 860 } 861 Test4528()862 public void Test4528() { 863 String[][] testPairs = { 864 { 865 "//ldml/numbers/currencies/currency[@type=\"BMD\"]/displayName[@count=\"other\"]", 866 "〖❬1,23 ❭dollari delle Bermuda〗〖❬0,00 ❭dollari delle Bermuda〗" 867 }, 868 { 869 "//ldml/numbers/currencyFormats[@numberSystem=\"latn\"]/unitPattern[@count=\"other\"]", 870 "〖❬1,23❭ ❬dollari statunitensi❭〗〖❬1,23❭ ❬euro❭〗〖❬0,00❭ ❬dollari statunitensi❭〗〖❬0,00❭ ❬euro❭〗" 871 }, 872 {"//ldml/numbers/currencies/currency[@type=\"BMD\"]/symbol", "〖❬123.456,79 ❭BMD〗"}, 873 }; 874 875 ExampleGenerator exampleGenerator = getExampleGenerator("it"); 876 for (String[] testPair : testPairs) { 877 String xpath = testPair[0]; 878 String expected = testPair[1]; 879 String value = exampleGenerator.getCldrFile().getStringValue(xpath); 880 String actual = simplify(exampleGenerator.getExampleHtml(xpath, value)); 881 assertEquals("specifics", expected, actual); 882 } 883 } 884 Test4607()885 public void Test4607() { 886 String[][] testPairs = { 887 { 888 "//ldml/numbers/decimalFormats[@numberSystem=\"latn\"]/decimalFormatLength[@type=\"long\"]/decimalFormat[@type=\"standard\"]/pattern[@type=\"1000\"][@count=\"one\"]", 889 "<div class='cldr_example'><span class='cldr_substituted'>1</span> thousand</div>" 890 }, 891 { 892 "//ldml/numbers/percentFormats[@numberSystem=\"latn\"]/percentFormatLength/percentFormat[@type=\"standard\"]/pattern[@type=\"standard\"]", 893 "<div class='cldr_example'><span class='cldr_substituted'>5</span>%</div>" 894 + "<div class='cldr_example'><span class='cldr_substituted'>12,345</span>,<span class='cldr_substituted'>679</span>%</div>" 895 + "<div class='cldr_example'>-<span class='cldr_substituted'>12,345</span>,<span class='cldr_substituted'>679</span>%</div>" 896 } 897 }; 898 final CLDRFile nativeCldrFile = info.getEnglish(); 899 ExampleGenerator exampleGenerator = 900 new ExampleGenerator(info.getEnglish(), info.getEnglish()); 901 for (String[] testPair : testPairs) { 902 String xpath = testPair[0]; 903 String expected = testPair[1]; 904 String value = nativeCldrFile.getStringValue(xpath); 905 String actual = exampleGenerator.getExampleHtml(xpath, value); 906 assertEquals("specifics", expected, actual); 907 } 908 } 909 showCldrFile(final CLDRFile cldrFile)910 private void showCldrFile(final CLDRFile cldrFile) { 911 ExampleGenerator exampleGenerator = new ExampleGenerator(cldrFile, info.getEnglish()); 912 checkPathValue( 913 exampleGenerator, 914 "//ldml/dates/calendars/calendar[@type=\"chinese\"]/dateFormats/dateFormatLength[@type=\"full\"]/dateFormat[@type=\"standard\"]/pattern[@type=\"standard\"][@draft=\"unconfirmed\"]", 915 "EEEE d MMMMl y'x'G", 916 null); 917 918 for (String xpath : cldrFile.fullIterable()) { 919 if (xpath.endsWith("/alias")) { 920 continue; 921 } 922 String value = cldrFile.getStringValue(xpath); 923 checkPathValue(exampleGenerator, xpath, value, null); 924 } 925 } 926 checkPathValue( ExampleGenerator exampleGenerator, String xpath, String value, String expected)927 private void checkPathValue( 928 ExampleGenerator exampleGenerator, String xpath, String value, String expected) { 929 Set<String> alreadySeen = new HashSet<>(); 930 try { 931 String text = exampleGenerator.getExampleHtml(xpath, value); 932 if (text == null) { 933 // skip 934 } else if (text.contains("Exception")) { 935 errln("getExampleHtml\t" + text); 936 } else if (!alreadySeen.contains(text)) { 937 if (text.contains("n/a")) { 938 if (text.contains("<")) { 939 errln("Text not quoted correctly:" + "\t" + text + "\t" + xpath); 940 } 941 } 942 boolean skipLog = false; 943 if (expected != null) { 944 String simplified = ExampleGenerator.simplify(text, false); 945 // redo for debugging 946 text = exampleGenerator.getExampleHtml(xpath, value); 947 skipLog = 948 !assertEquals("Example text for «" + value + "»", expected, simplified); 949 } 950 if (!skipLog) { 951 logln("getExampleHtml\t" + text + "\t" + xpath); 952 } 953 alreadySeen.add(text); 954 } 955 } catch (Exception e) { 956 errln("getExampleHtml\t" + e.getMessage()); 957 } 958 959 try { 960 String text = exampleGenerator.getHelpHtml(xpath, value); 961 if (text == null) { 962 // skip 963 } else if (text.contains("Exception")) { 964 errln("getHelpHtml\t" + text); 965 } else { 966 logln("getExampleHtml(help)\t" + "\t" + text + "\t" + xpath); 967 } 968 } catch (Exception e) { 969 if (false) { 970 e.printStackTrace(); 971 } 972 errln("getHelpHtml\t" + e.getMessage()); 973 } 974 } 975 TestCompactPlurals()976 public void TestCompactPlurals() { 977 checkCompactExampleFor("de", Count.one, "〖❬1❭ Mio. €〗", "short", "currency", "000000"); 978 checkCompactExampleFor("de", Count.other, "〖❬2❭ Mio. €〗", "short", "currency", "000000"); 979 checkCompactExampleFor("de", Count.one, "〖❬12❭ Mio. €〗", "short", "currency", "0000000"); 980 checkCompactExampleFor("de", Count.other, "〖❬10❭ Mio. €〗", "short", "currency", "0000000"); 981 982 checkCompactExampleFor("cs", Count.many, "〖❬1,1❭ milionu〗", "long", "decimal", "000000"); 983 checkCompactExampleFor("pl", Count.other, "〖❬1,1❭ miliona〗", "long", "decimal", "000000"); 984 } 985 checkCompactExampleFor( String localeID, Count many, String expected, String longVsShort, String decimalVsCurrency, String zeros)986 private void checkCompactExampleFor( 987 String localeID, 988 Count many, 989 String expected, 990 String longVsShort, 991 String decimalVsCurrency, 992 String zeros) { 993 CLDRFile cldrFile = info.getCLDRFile(localeID, true); 994 ExampleGenerator exampleGenerator = new ExampleGenerator(cldrFile, info.getEnglish()); 995 String path = 996 "//ldml/numbers/" 997 + decimalVsCurrency 998 + "Formats[@numberSystem=\"latn\"]" 999 + "/" 1000 + decimalVsCurrency 1001 + "FormatLength[@type=\"" 1002 + longVsShort 1003 + "\"]" 1004 + "/" 1005 + decimalVsCurrency 1006 + "Format[@type=\"standard\"]" 1007 + "/pattern[@type=\"1" 1008 + zeros 1009 + "\"][@count=\"" 1010 + many 1011 + "\"]"; 1012 checkPathValue(exampleGenerator, path, cldrFile.getStringValue(path), expected); 1013 } 1014 1015 // ldml/numbers/currencyFormats[@numberSystem="latn"]/currencyFormatLength[@type="short"]/currencyFormat[@type="standard"]/pattern[@type="1000"][@count="one"] 1016 TestDayPeriods()1017 public void TestDayPeriods() { 1018 // checkDayPeriod("da", "format", "morning1", "〖05:00 – 10:00〗〖❬7:30❭ morgens〗"); 1019 checkDayPeriod("zh", "format", "morning1", "〖05:00 – 08:00⁻〗〖清晨❬6:30❭〗"); 1020 1021 checkDayPeriod("de", "format", "morning1", "〖05:00 – 10:00⁻〗〖❬7:30 ❭morgens〗"); 1022 checkDayPeriod("de", "stand-alone", "morning1", "〖05:00 – 10:00⁻〗"); 1023 checkDayPeriod("de", "format", "morning2", "〖10:00 – 12:00⁻〗〖❬11:00 ❭vormittags〗"); 1024 checkDayPeriod("de", "stand-alone", "morning2", "〖10:00 – 12:00⁻〗"); 1025 checkDayPeriod("pl", "format", "morning1", "〖06:00 – 10:00⁻〗〖❬8:00 ❭rano〗"); 1026 checkDayPeriod("pl", "stand-alone", "morning1", "〖06:00 – 10:00⁻〗"); 1027 1028 checkDayPeriod( 1029 "en", "format", "night1", "〖00:00 – 06:00⁻; 21:00 – 24:00⁻〗〖❬3:00 ❭at night〗"); 1030 checkDayPeriod("en", "stand-alone", "night1", "〖00:00 – 06:00⁻; 21:00 – 24:00⁻〗"); 1031 1032 checkDayPeriod("en", "format", "noon", "〖12:00〗〖❬12:00 ❭noon〗"); 1033 checkDayPeriod("en", "format", "midnight", "〖00:00〗〖❬12:00 ❭midnight〗"); 1034 checkDayPeriod("en", "format", "am", "〖00:00 – 12:00⁻〗〖❬6:00 ❭AM〗"); 1035 checkDayPeriod("en", "format", "pm", "〖12:00 – 24:00⁻〗〖❬6:00 ❭PM〗"); 1036 } 1037 checkDayPeriod( String localeId, String type, String dayPeriodCode, String expected)1038 private void checkDayPeriod( 1039 String localeId, String type, String dayPeriodCode, String expected) { 1040 CLDRFile cldrFile = info.getCLDRFile(localeId, true); 1041 ExampleGenerator exampleGenerator = new ExampleGenerator(cldrFile, info.getEnglish()); 1042 String prefix = 1043 "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dayPeriods/dayPeriodContext[@type=\""; 1044 String suffix = 1045 "\"]/dayPeriodWidth[@type=\"wide\"]/dayPeriod[@type=\"" + dayPeriodCode + "\"]"; 1046 String path = prefix + type + suffix; 1047 checkPathValue(exampleGenerator, path, cldrFile.getStringValue(path), expected); 1048 } 1049 1050 /** 1051 * Test that getExampleHtml returns same output for same input regardless of order in which it 1052 * is called with different inputs. 1053 * 1054 * <p>Calling getExampleHtml with a particular path and value presumably should NOT depend on 1055 * the history of paths and/or values it has been called with previously. 1056 * 1057 * <p>We formerly got different examples for SPECIAL_PATH depending on whether an example was 1058 * first gotten for USE_EVIL_PATH. 1059 * 1060 * <p>Without EVIL_PATH, got right value for SPECIAL_PATH: <div class='cldr_example'><span 1061 * class='cldr_substituted'>123 456,79 </span>€</div> 1062 * 1063 * <p>With EVIL_PATH, got wrong value for SPECIAL_PATH: <div class='cldr_example'><span 1064 * class='cldr_substituted'>123457 k </span>€</div> 1065 * 1066 * <p>This was fixed by doing clone() before returning a DecimalFormat in ICUServiceBuilder. 1067 * Reference: https://unicode-org.atlassian.net/browse/CLDR-13375. 1068 * 1069 * <p>Subsequently, DAIP changed to normalize NNBSP to NBSP for some paths, so this test was 1070 * revised not to depend on that distinction, only to expect an example containing "456,79" as a 1071 * substring (DAIP has its own unit tests). Ideally this test might be improved so as not to 1072 * depend on actual values at all, but would call getExampleHtml repeatedly for the same set of 1073 * paths but in different orders and confirm the example is the same regardless of the order; 1074 * that would require disabling the cache. 1075 * 1076 * @throws IOException 1077 */ TestExampleGeneratorConsistency()1078 public void TestExampleGeneratorConsistency() throws IOException { 1079 final String EVIL_PATH = 1080 "//ldml/numbers/currencyFormats/currencyFormatLength[@type=\"short\"]/currencyFormat[@type=\"standard\"]/pattern[@type=\"10000\"][@count=\"one\"]"; 1081 final String SPECIAL_PATH = "//ldml/numbers/currencies/currency[@type=\"EUR\"]/symbol"; 1082 final String EXPECTED_TO_CONTAIN = "456,79"; 1083 1084 final CLDRFile cldrFile = info.getCLDRFile("fr", true); 1085 final ExampleGenerator eg = new ExampleGenerator(cldrFile, info.getEnglish()); 1086 1087 final String evilValue = cldrFile.getStringValue(EVIL_PATH); 1088 final String specialValue = cldrFile.getStringValue(SPECIAL_PATH); 1089 1090 eg.getExampleHtml(EVIL_PATH, evilValue); 1091 final String specialExample = eg.getExampleHtml(SPECIAL_PATH, specialValue); 1092 if (!specialExample.contains(EXPECTED_TO_CONTAIN)) { 1093 errln("Expected example to contain " + EXPECTED_TO_CONTAIN + "; got " + specialExample); 1094 } 1095 } 1096 TestInflectedUnitExamples()1097 public void TestInflectedUnitExamples() { 1098 String[][] deTests = { 1099 { 1100 "one", 1101 "accusative", 1102 "〖❬1❭ Tag〗〖❬… für 1❭ Tag❬ …❭〗〖❌ ❬Anstatt 1❭ Tag❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗" 1103 }, 1104 { 1105 "one", 1106 "dative", 1107 "〖❬1❭ Tag〗〖❬… mit 1❭ Tag❬ …❭〗〖❌ ❬Anstatt 1❭ Tag❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗" 1108 }, 1109 { 1110 "one", 1111 "genitive", 1112 "〖❬1❭ Tages〗〖❬Anstatt 1❭ Tages❬ …❭〗〖❌ ❬… für 1❭ Tages❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗" 1113 }, 1114 { 1115 "one", 1116 "nominative", 1117 "〖❬1❭ Tag〗〖❬1❭ Tag❬ kostet (kosten) € 3,50.❭〗〖❌ ❬Anstatt 1❭ Tag❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗" 1118 }, 1119 { 1120 "other", 1121 "accusative", 1122 "〖❬1,5❭ Tage〗〖❬… für 1,5❭ Tage❬ …❭〗〖❌ ❬… mit 1,5❭ Tage❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗" 1123 }, 1124 { 1125 "other", 1126 "dative", 1127 "〖❬1,5❭ Tagen〗〖❬… mit 1,5❭ Tagen❬ …❭〗〖❌ ❬… für 1,5❭ Tagen❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗" 1128 }, 1129 { 1130 "other", 1131 "genitive", 1132 "〖❬1,5❭ Tage〗〖❬Anstatt 1,5❭ Tage❬ …❭〗〖❌ ❬… mit 1,5❭ Tage❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗" 1133 }, 1134 { 1135 "other", 1136 "nominative", 1137 "〖❬1,5❭ Tage〗〖❬1,5❭ Tage❬ kostet (kosten) € 3,50.❭〗〖❌ ❬… mit 1,5❭ Tage❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗" 1138 }, 1139 }; 1140 checkInflectedUnitExamples("de", deTests); 1141 String[][] elTests = { 1142 { 1143 "one", 1144 "accusative", 1145 "〖❬1❭ ημέρα〗〖❬… ανά 1❭ ημέρα❬ …❭〗〖❌ ❬… αξίας 1❭ ημέρα❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗" 1146 }, 1147 { 1148 "one", 1149 "genitive", 1150 "〖❬1❭ ημέρας〗〖❬… αξίας 1❭ ημέρας❬ …❭〗〖❌ ❬… ανά 1❭ ημέρας❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗" 1151 }, 1152 { 1153 "one", 1154 "nominative", 1155 "〖❬1❭ ημέρα〗〖❬Η απόσταση είναι 1❭ ημέρα❬ …❭〗〖❌ ❬… αξίας 1❭ ημέρα❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗" 1156 }, 1157 { 1158 "other", 1159 "accusative", 1160 "〖❬0,9❭ ημέρες〗〖❬… ανά 0,9❭ ημέρες❬ …❭〗〖❌ ❬… αξίας 0,9❭ ημέρες❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗" 1161 }, 1162 { 1163 "other", 1164 "genitive", 1165 "〖❬0,9❭ ημερών〗〖❬… αξίας 0,9❭ ημερών❬ …❭〗〖❌ ❬… ανά 0,9❭ ημερών❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗" 1166 }, 1167 { 1168 "other", 1169 "nominative", 1170 "〖❬0,9❭ ημέρες〗〖❬Η απόσταση είναι 0,9❭ ημέρες❬ …❭〗〖❌ ❬… αξίας 0,9❭ ημέρες❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗" 1171 }, 1172 }; 1173 checkInflectedUnitExamples("el", elTests); 1174 } 1175 checkInflectedUnitExamples(final String locale, String[][] tests)1176 private void checkInflectedUnitExamples(final String locale, String[][] tests) { 1177 final CLDRFile cldrFile = info.getCLDRFile(locale, true); 1178 ExampleGenerator exampleGenerator = getExampleGenerator(locale); 1179 String pattern = 1180 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"duration-day\"]/unitPattern[@count=\"COUNT\"][@case=\"CASE\"]"; 1181 boolean showWorkingExamples = false; 1182 for (String[] row : tests) { 1183 String path = pattern.replace("COUNT", row[0]).replace("CASE", row[1]); 1184 String expected = row[2]; 1185 String value = cldrFile.getStringValue(path); 1186 String actualRaw = exampleGenerator.getExampleHtml(path, value); 1187 String actual = ExampleGenerator.simplify(actualRaw, false); 1188 showWorkingExamples |= !assertEquals(row[0] + ", " + row[1], expected, actual); 1189 } 1190 1191 // If a test fails, verbose will regenerate what the code thinks they should be. 1192 // Review for correctness, and then replace the test cases 1193 1194 if (showWorkingExamples) { 1195 System.out.println( 1196 "## The following would satisfy the test, but check to make sure the expected values are all correct!"); 1197 PluralInfo pluralInfo = SDI.getPlurals(PluralType.cardinal, locale); 1198 GrammarInfo grammarInfo = SDI.getGrammarInfo(locale); 1199 final Collection<String> grammaticalValues2 = 1200 grammarInfo.get( 1201 GrammaticalTarget.nominal, 1202 GrammaticalFeature.grammaticalCase, 1203 GrammaticalScope.units); 1204 1205 for (Count plural : pluralInfo.getCounts()) { 1206 for (String grammaticalCase : grammaticalValues2) { 1207 String path = 1208 pattern.replace("COUNT", plural.toString()) 1209 .replace("CASE", grammaticalCase); 1210 String value = cldrFile.getStringValue(path); 1211 String actualRaw = exampleGenerator.getExampleHtml(path, value); 1212 String actual = ExampleGenerator.simplify(actualRaw, false); 1213 System.out.println( 1214 "{\"" 1215 + plural 1216 + "\", " 1217 + "\"" 1218 + grammaticalCase 1219 + "\", " 1220 + "\"" 1221 + actual 1222 + "\"},"); 1223 } 1224 } 1225 } 1226 } 1227 TestMinimalPairExamples()1228 public void TestMinimalPairExamples() { 1229 String[][] tests = { 1230 { 1231 "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\"one\"]", 1232 "〖❬1❭ Tag〗〖❌ ❬2❭ Tag〗" 1233 }, 1234 { 1235 "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\"other\"]", 1236 "〖❬2❭ Tage〗〖❌ ❬1❭ Tage〗" 1237 }, 1238 { 1239 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"accusative\"]", 1240 "〖… für ❬1 metrische Pint❭ …〗〖❌ … für ❬1 metrischen Pint❭ …〗" 1241 }, 1242 { 1243 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"dative\"]", 1244 "〖… mit ❬1 metrischen Pint❭ …〗〖❌ … mit ❬1 metrische Pint❭ …〗" 1245 }, 1246 { 1247 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"genitive\"]", 1248 "〖Anstatt ❬1 metrischen Pints❭ …〗〖❌ Anstatt ❬1 metrische Pint❭ …〗" 1249 }, 1250 { 1251 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"nominative\"]", 1252 "〖❬2 metrische Pints❭ kostet (kosten) € 3,50.〗〖❌ ❬1 metrische Pint❭ kostet (kosten) € 3,50.〗" 1253 }, 1254 { 1255 "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"feminine\"]", 1256 "〖Die ❬Stunde❭ ist …〗〖❌ Die ❬Tag❭ ist …〗" 1257 }, 1258 { 1259 "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"masculine\"]", 1260 "〖Der ❬Tag❭ ist …〗〖❌ Der ❬Stunde❭ ist …〗" 1261 }, 1262 { 1263 "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"neuter\"]", 1264 "〖Das ❬Jahr❭ ist …〗〖❌ Das ❬Stunde❭ ist …〗" 1265 }, 1266 }; 1267 checkMinimalPairExamples("de", tests); 1268 1269 String[][] elTests = { 1270 { 1271 "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\"one\"]", 1272 "〖❬1❭ ημέρα〗〖❌ ❬2❭ ημέρα〗" 1273 }, 1274 { 1275 "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\"other\"]", 1276 "〖❬2❭ ημέρες〗〖❌ ❬1❭ ημέρες〗" 1277 }, 1278 { 1279 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"accusative\"]", 1280 "〖… ανά ❬1 τόνο❭ …〗〖❌ … ανά ❬1 τόνου❭ …〗" 1281 }, 1282 { 1283 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"genitive\"]", 1284 "〖… αξίας ❬1 τόνου❭ …〗〖❌ … αξίας ❬1 τόνο❭ …〗" 1285 }, 1286 { 1287 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"nominative\"]", 1288 "〖Η απόσταση είναι ❬2 τόνοι❭ …〗〖❌ Η απόσταση είναι ❬1 τόνο❭ …〗" 1289 }, 1290 { 1291 "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"feminine\"]", 1292 "〖Η ❬ημέρα❭ είναι〗〖❌ Η ❬μήνας❭ είναι〗" 1293 }, 1294 { 1295 "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"masculine\"]", 1296 "〖Ο ❬μήνας❭ θα είναι〗〖❌ Ο ❬ημέρα❭ θα είναι〗" 1297 }, 1298 { 1299 "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"neuter\"]", 1300 "〖Το ❬λεπτό❭ ήταν〗〖❌ Το ❬ημέρα❭ ήταν〗" 1301 }, 1302 }; 1303 checkMinimalPairExamples("el", elTests); 1304 } 1305 checkMinimalPairExamples(final String locale, String[][] tests)1306 private void checkMinimalPairExamples(final String locale, String[][] tests) { 1307 final CLDRFile cldrFile = info.getCLDRFile(locale, true); 1308 ExampleGenerator exampleGenerator = getExampleGenerator(locale); 1309 boolean showWorkingExamples = false; 1310 for (String[] row : tests) { 1311 String path = row[0]; 1312 String expected = row[1]; 1313 String value = cldrFile.getStringValue(path); 1314 String actualRaw = exampleGenerator.getExampleHtml(path, value); 1315 String actual = ExampleGenerator.simplify(actualRaw, false); 1316 showWorkingExamples |= !assertEquals(row[0] + ", " + row[1], expected, actual); 1317 } 1318 1319 // If a test fails, verbose will regenerate what the code thinks they should be. 1320 // Review for correctness, and then replace the test cases 1321 1322 if (showWorkingExamples) { 1323 System.out.println( 1324 "## The following would satisfy the test, but check to make sure the expected values are all correct!"); 1325 PluralInfo pluralInfo = SDI.getPlurals(PluralType.cardinal, locale); 1326 ArrayList<String> paths = new ArrayList<>(); 1327 1328 for (Count plural : pluralInfo.getCounts()) { 1329 paths.add( 1330 "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\"" 1331 + plural 1332 + "\"]"); 1333 } 1334 GrammarInfo grammarInfo = SDI.getGrammarInfo(locale); 1335 for (String grammaticalValues : 1336 grammarInfo.get( 1337 GrammaticalTarget.nominal, 1338 GrammaticalFeature.grammaticalCase, 1339 GrammaticalScope.units)) { 1340 paths.add( 1341 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"" 1342 + grammaticalValues 1343 + "\"]"); 1344 } 1345 for (String grammaticalValues : 1346 grammarInfo.get( 1347 GrammaticalTarget.nominal, 1348 GrammaticalFeature.grammaticalGender, 1349 GrammaticalScope.units)) { 1350 paths.add( 1351 "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"" 1352 + grammaticalValues 1353 + "\"]"); 1354 } 1355 for (String path : paths) { 1356 String value = cldrFile.getStringValue(path); 1357 String actualRaw = exampleGenerator.getExampleHtml(path, value); 1358 String actual = ExampleGenerator.simplify(actualRaw, false); 1359 System.out.println("{\"" + path.replace("\"", "\\\"") + "\", \"" + actual + "\"},"); 1360 } 1361 } 1362 } 1363 1364 /** 1365 * Test the production of minimal pair examples, to make sure we get no exceptions. If -v, then 1366 * generates lines for spreadsheet survey 1367 */ TestListMinimalPairExamples()1368 public void TestListMinimalPairExamples() { 1369 Set<String> localesWithGrammar = SDI.hasGrammarInfo(); 1370 if (isVerbose()) { 1371 System.out.println( 1372 "\nLC\tLocale\tType\tCode\tCurrent Pattern\tVerify this is correct!\tVerify this is wrong!"); 1373 } 1374 final String unused = "∅"; 1375 List<String> pluralSheet = new ArrayList(); 1376 for (String locale : localesWithGrammar) { 1377 final CLDRFile cldrFile = info.getCLDRFile(locale, true); 1378 ExampleGenerator exampleGenerator = getExampleGenerator(locale); 1379 1380 PluralInfo pluralInfo = SDI.getPlurals(PluralType.cardinal, cldrFile.getLocaleID()); 1381 Map<String, Pair<String, String>> paths = new LinkedHashMap<>(); 1382 1383 Set<Count> counts = pluralInfo.getCounts(); 1384 if (counts.size() > 1) { 1385 for (Count plural : counts) { 1386 paths.put( 1387 "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\"" 1388 + plural 1389 + "\"]", 1390 Pair.of("plural", plural.toString())); 1391 } 1392 } 1393 GrammarInfo grammarInfo = SDI.getGrammarInfo(locale); 1394 Collection<String> unitCases = 1395 grammarInfo.get( 1396 GrammaticalTarget.nominal, 1397 GrammaticalFeature.grammaticalCase, 1398 GrammaticalScope.units); 1399 Collection<String> generalCasesRaw = 1400 grammarInfo.get( 1401 GrammaticalTarget.nominal, 1402 GrammaticalFeature.grammaticalCase, 1403 GrammaticalScope.general); 1404 Collection<CaseValues> generalCases = 1405 generalCasesRaw.stream() 1406 .map(x -> CaseValues.valueOf(x)) 1407 .collect(Collectors.toCollection(TreeSet::new)); 1408 for (CaseValues unitCase0 : generalCases) { 1409 String unitCase = unitCase0.toString(); 1410 paths.put( 1411 (unitCases.contains(unitCase) ? "" : unused) 1412 + "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"" 1413 + unitCase 1414 + "\"]", 1415 Pair.of("case", unitCase)); 1416 } 1417 Collection<String> unitGenders = 1418 grammarInfo.get( 1419 GrammaticalTarget.nominal, 1420 GrammaticalFeature.grammaticalGender, 1421 GrammaticalScope.units); 1422 Collection<String> generalGenders = 1423 grammarInfo.get( 1424 GrammaticalTarget.nominal, 1425 GrammaticalFeature.grammaticalGender, 1426 GrammaticalScope.general); 1427 for (String unitGender : generalGenders) { 1428 paths.put( 1429 (unitGenders.contains(unitGender) ? "" : unused) 1430 + "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"" 1431 + unitGender 1432 + "\"]", 1433 Pair.of("gender", unitGender)); 1434 } 1435 String localeName = CLDRConfig.getInstance().getEnglish().getName(locale); 1436 boolean pluralOnly = true; 1437 if (paths.isEmpty()) { 1438 pluralSheet.add( 1439 locale + "\t" + localeName + "\t" + "N/A" + "\t" + "N/A" + "\t" + "N/A"); 1440 } else { 1441 for (Entry<String, Pair<String, String>> pathAndLabel : paths.entrySet()) { 1442 String path = pathAndLabel.getKey(); 1443 String label = pathAndLabel.getValue().getFirst(); 1444 String code = pathAndLabel.getValue().getSecond(); 1445 if (!label.equals("plural")) { 1446 pluralOnly = false; 1447 } 1448 } 1449 String lastLabel = ""; 1450 for (Entry<String, Pair<String, String>> pathAndLabel : paths.entrySet()) { 1451 String path = pathAndLabel.getKey(); 1452 String label = pathAndLabel.getValue().getFirst(); 1453 String code = pathAndLabel.getValue().getSecond(); 1454 String pattern = ""; 1455 String examples = ""; 1456 if (!label.equals(lastLabel)) { 1457 lastLabel = label; 1458 if (!pluralOnly) { 1459 if (isVerbose()) { 1460 System.out.println(); 1461 } 1462 } 1463 } 1464 if (path.startsWith(unused)) { 1465 pattern = " Not used with formatted units"; 1466 } else { 1467 pattern = cldrFile.getStringValue(path); 1468 if (pattern == null) { 1469 warnln( 1470 "Missing ExampleGenerator html example for " 1471 + locale 1472 + "(" 1473 + localeName 1474 + "): " 1475 + path); 1476 continue; 1477 } 1478 String actualRaw = exampleGenerator.getExampleHtml(path, pattern); 1479 String actualSimplified = ExampleGenerator.simplify(actualRaw, false); 1480 examples = 1481 actualSimplified 1482 .replace("〗〖", "\t") 1483 .replace("〗", "") 1484 .replace("〖", ""); 1485 List<String> exampleList = 1486 com.google.common.base.Splitter.on('\t') 1487 .trimResults() 1488 .splitToList(examples); 1489 final int exampleListSize = exampleList.size(); 1490 switch (exampleListSize) { 1491 case 2: // ok 1492 break; 1493 case 1: 1494 warnln( 1495 "Expecting exactly 2 examples: " 1496 + exampleList 1497 + ", but got " 1498 + exampleListSize); 1499 break; 1500 default: 1501 errln( 1502 "Expecting exactly 2 examples: " 1503 + exampleList 1504 + ", but got " 1505 + exampleListSize); 1506 break; 1507 } 1508 StringBuilder exampleBuffer = new StringBuilder(); 1509 for (String exampleItem : exampleList) { 1510 if (exampleItem.contains("❬null❭") || exampleItem.contains("❬n/a❭")) { 1511 boolean bad = (exampleItem.contains("❌")); 1512 exampleItem = " No unit available"; 1513 if (bad) { 1514 exampleItem = "❌ " + exampleItem; 1515 } 1516 } 1517 if (exampleBuffer.length() != 0) { 1518 exampleBuffer.append('\t'); 1519 } 1520 exampleBuffer.append(exampleItem); 1521 } 1522 examples = exampleBuffer.toString(); 1523 } 1524 String line = 1525 (locale 1526 + "\t" 1527 + localeName 1528 + "\t" 1529 + label 1530 + "\t" 1531 + code 1532 + "\t" 1533 + pattern 1534 + "\t" 1535 + examples); 1536 if (pluralOnly) { 1537 pluralSheet.add(line); 1538 } else { 1539 if (isVerbose()) { 1540 System.out.println(line); 1541 } 1542 } 1543 } 1544 } 1545 if (pluralOnly) { 1546 pluralSheet.add(""); 1547 } else if (isVerbose()) { 1548 System.out.println(); 1549 } 1550 } 1551 if (isVerbose()) { 1552 System.out.println("#################### Plural Only ###################"); 1553 for (String line : pluralSheet) { 1554 System.out.println(line); 1555 } 1556 } 1557 } 1558 TestUnicodeSetExamples()1559 public void TestUnicodeSetExamples() { 1560 String[][] tests = { 1561 { 1562 "hi", 1563 "//ldml/characters/exemplarCharacters[@type=\"auxiliary\"]", 1564 "[ॄ]", 1565 "〖️ ॑ ॒ ॠ ॡ ॻ ॼ ॾ ॿ ऱ ॢ ॣ〗〖❰ZWNJ❱ ≡ cursive non-joiner〗〖❰ZWJ❱ ≡ cursive joiner〗〖❬internal: ❭[ॄ]〗" 1566 }, 1567 { 1568 "hu", 1569 "//ldml/characters/exemplarCharacters[@type=\"auxiliary\"]", 1570 "[qw-yàâ-èê-ìîïñòôøùûÿāăēĕīĭōŏœūŭ]", 1571 "〖️ · ắ ằ ẵ ẳ ấ ầ ẫ ẩ ǎ a̧ ą ą́ a᷆ a᷇ ả ạ ặ ậ a̱ aː áː àː ɓ ć ĉ č ċ ď ḑ đ ḍ ḓ ð ɖ ɗ ế ề ễ ể ě ẽ ė ę ę́ e᷆ e᷇ ẻ ẹ ẹ́ ẹ̀ ệ e̱ eː éː èː ǝ ǝ́ ǝ̀ ǝ̂ ǝ̌ ǝ̄ ə ə́ ə̀ ə̂ ə̌ ə̄ ɛ ɛ́ ɛ̀ ɛ̂ ɛ̌ ɛ̈ ɛ̃ ɛ̧ ɛ̄ ɛ᷆ ɛ᷇ ɛ̱ ɛ̱̈ ƒ ğ ĝ ǧ g̃ ġ ģ g̱ gʷ ǥ ɣ ĥ ȟ ħ ḥ ʻ ǐ ĩ İ i̧ į į́ i᷆ i᷇ ỉ ị i̱ iː íː ìː íj́ ı ɨ ɨ́ ɨ̀ ɨ̂ ɨ̌ ɨ̄ ɩ ɩ́ ɩ̀ ɩ̂ ĵ ǩ ķ ḵ kʷ ƙ ĺ ľ ļ ł ḷ ḽ ḻ ḿ m̀ m̄ ń ǹ ň ṅ ņ n̄ ṇ ṋ ṉ ɲ ŋ ŋ́ ŋ̀ ŋ̄ ố ồ ỗ ổ ǒ õ ǫ ǫ́ o᷆ o᷇ ỏ ơ ớ ờ ỡ ở ợ ọ ọ́ ọ̀ ộ o̱ oː óː òː ɔ ɔ́ ɔ̀ ɔ̂ ɔ̌ ɔ̈ ɔ̃ ɔ̧ ɔ̄ ɔ᷆ ɔ᷇ ɔ̱ ŕ ř ŗ ṛ ś ŝ š ş ṣ ș ß ť ṭ ț ṱ ṯ ŧ ǔ ů ũ u̧ ų u᷆ u᷇ ủ ư ứ ừ ữ ử ự ụ uː úː ùː ʉ ʉ́ ʉ̀ ʉ̂ ʉ̌ ʉ̈ ʉ̄ ʊ ʊ́ ʊ̀ ʊ̂ ṽ ʋ ẃ ẁ ŵ ẅ ý ỳ ŷ ỹ ỷ ỵ y̱ ƴ ź ž ż ẓ ʒ ǯ þ ʔ ˀ ʼ ꞌ ǀ ǁ ǂ ǃ〗〖❬internal: ❭[qw-yàâ-èê-ìîïñòôøùûÿāăēĕīĭōŏœūŭ]〗" 1572 }, 1573 { 1574 "de", 1575 "//ldml/characters/parseLenients[@scope=\"date\"][@level=\"lenient\"]/parseLenient[@sample=\"-\"]", 1576 "[\\u200B \\- . ๎ ็]", 1577 "〖➕ ❰WNJ❱ ๎ ็〗〖➖ ‑ /〗〖❰WNJ❱ ≡ allow line wrap after, aka ZWSP〗〖❬internal: ❭[\\-.็๎]〗" 1578 }, 1579 { 1580 "de", 1581 "//ldml/characters/exemplarCharacters", 1582 "[\\u200B a-z ๎ ็]", 1583 "〖➕ ❰WNJ❱ ๎ ็〗〖➖ ä ö ß ü〗〖❰WNJ❱ ≡ allow line wrap after, aka ZWSP〗〖❬internal: ❭[a-z็๎]〗" 1584 }, 1585 {"de", "//ldml/characters/exemplarCharacters", "a-z ❰ZWSP❱", null}, 1586 }; 1587 1588 for (String[] test : tests) { 1589 final String locale = test[0]; 1590 final String path = test[1]; 1591 final String value = test[2]; 1592 final String expected = test[3]; 1593 ExampleGenerator exampleGenerator = getExampleGenerator(locale); 1594 String actual = 1595 ExampleGenerator.simplify( // 1596 exampleGenerator.getExampleHtml(path, value)); 1597 assertEquals(locale + path + "=" + value, expected, actual); 1598 } 1599 } 1600 } 1601