Skip to content

Commit 9d57005

Browse files
authored
CLDR-16425 v43 BRS: JSON (#2749)
* CLDR-16425 v43 BRS: JSON fixes for CoverageLevel reader - use CalculatedCoverageLevels instead of homemade reader * CLDR-16425 v43 BRS: JSON - core package has everything, full has basic, modern has modern+ - add a -M option to include the modern package (on by default for now) - add an effectiveCoverageLevels calculation for sublocales. Also include this in the coverageLevels.json * CLDR-16425 v43 BRS: fixes for parentLocales
1 parent 3181036 commit 9d57005

File tree

7 files changed

+160
-48
lines changed

7 files changed

+160
-48
lines changed

tools/cldr-code/src/main/java/org/unicode/cldr/json/Ldml2JsonConverter.java

Lines changed: 62 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.unicode.cldr.json;
22

3-
import java.io.BufferedReader;
43
import java.io.File;
54
import java.io.IOException;
65
import java.io.PrintWriter;
@@ -44,6 +43,7 @@
4443
import com.ibm.icu.number.NumberFormatter;
4544
import com.ibm.icu.number.Precision;
4645
import com.ibm.icu.util.NoUnit;
46+
import com.ibm.icu.util.ULocale;
4747

4848
/**
4949
* Utility methods to extract data from CLDR repository and export it in JSON
@@ -61,7 +61,7 @@ public class Ldml2JsonConverter {
6161
enum RunType {
6262
all,
6363
main,
64-
supplemental(false, false), // aka 'core'
64+
supplemental(false, false), // aka 'cldr-core'
6565
segments, rbnf(false, true), annotations, annotationsDerived, bcp47(false, false);
6666

6767
private final boolean isTiered;
@@ -146,7 +146,8 @@ private class AvailableLocales {
146146
.add("identity", 'i', "(true|false)", "true",
147147
"Whether to copy the identity info into all sections containing data")
148148
.add("konfig", 'k', ".*", null, "LDML to JSON configuration file")
149-
.add("pkgversion", 'V', ".*", getDefaultVersion(), "Version to be used in writing package files");
149+
.add("pkgversion", 'V', ".*", getDefaultVersion(), "Version to be used in writing package files")
150+
.add("Modern", 'M', "(true|false)", "true", "Whether to include the -modern tier");
150151

151152
public static void main(String[] args) throws Exception {
152153
options.parse(args, true);
@@ -186,7 +187,8 @@ static void processType(final String runType) throws Exception {
186187
options.get("konfig").getValue(),
187188
options.get("pkgversion").getValue(),
188189
Boolean.parseBoolean(options.get("bcp47").getValue()),
189-
Boolean.parseBoolean(options.get("bcp47-no-subtags").getValue())
190+
Boolean.parseBoolean(options.get("bcp47-no-subtags").getValue()),
191+
Boolean.parseBoolean(options.get("Modern").getValue())
190192
);
191193

192194
DraftStatus status = DraftStatus.valueOf(options.get("draftstatus").getValue());
@@ -226,12 +228,14 @@ public int compareTo(JSONSection other) {
226228
private Set<String> packages;
227229
final private String pkgVersion;
228230
final private boolean strictBcp47;
231+
final private boolean writeModernPackage;
229232
final private boolean skipBcp47LocalesWithSubtags;
230233
private LdmlConfigFileReader configFileReader;
231234

232235
public Ldml2JsonConverter(String cldrDir, String outputDir, String runType, boolean fullNumbers, boolean resolve, String coverage, String match,
233236
boolean writePackages, String configFile, String pkgVersion,
234-
boolean strictBcp47, boolean skipBcp47LocalesWithSubtags) {
237+
boolean strictBcp47, boolean skipBcp47LocalesWithSubtags, boolean writeModernPackage) {
238+
this.writeModernPackage = writeModernPackage;
235239
this.strictBcp47 = strictBcp47;
236240
this.skipBcp47LocalesWithSubtags = strictBcp47 && skipBcp47LocalesWithSubtags;
237241
this.cldrCommonDir = cldrDir;
@@ -432,7 +436,7 @@ private Map<JSONSection, List<CldrItem>> mapPathsToSections(AtomicInteger readCo
432436

433437
// discard draft before transforming
434438
final String pathNoDraft = CLDRFile.DRAFT_PATTERN.matcher(path).replaceAll("");
435-
final String fullPathNoDraft = CLDRFile.DRAFT_PATTERN.matcher(fullPath).replaceAll("");
439+
final String fullPathNoDraft = CLDRFile.DRAFT_PATTERN.matcher(fullPath).replaceAll("");
436440

437441
final String pathNoXmlSpace = CLDRFile.XML_SPACE_PATTERN.matcher(pathNoDraft).replaceAll("");
438442
final String fullPathNoXmlSpace = CLDRFile.XML_SPACE_PATTERN.matcher(fullPathNoDraft).replaceAll("");
@@ -559,7 +563,7 @@ private int convertCldrItems(AtomicInteger readCount, int totalCount,
559563
continue;
560564
}
561565
final boolean isModernTier = localeIsModernTier(filename);
562-
if (isModernTier) {
566+
if (isModernTier && writeModernPackage) {
563567
tier = MODERN_TIER_SUFFIX;
564568
if (type == RunType.main) {
565569
avl.modern.add(filenameAsLangTag);
@@ -598,9 +602,9 @@ private int convertCldrItems(AtomicInteger readCount, int totalCount,
598602
List<String> outputDirs = new ArrayList<>();
599603
outputDirs.add(outputDirname.toString());
600604
if (writePackages && tier.equals(MODERN_TIER_SUFFIX) && js.packageName != null) {
601-
// if it is in 'modern', add it to 'full' also.
605+
// if it is in 'modern', add it to 'full' and core also.
602606
outputDirs.add(outputDirname.toString().replaceFirst(MODERN_TIER_SUFFIX, FULL_TIER_SUFFIX));
603-
// Also need to make sure that the full package is added
607+
// Also need to make sure that the full and core package is added
604608
packages.add(CLDR_PKG_PREFIX + js.packageName + FULL_TIER_SUFFIX);
605609
}
606610

@@ -746,12 +750,15 @@ private int convertCldrItems(AtomicInteger readCount, int totalCount,
746750
}
747751

748752
private boolean localeIsModernTier(String filename) {
749-
boolean isModernTier;
750-
{
751-
final Level localeCoverageLevel = sc.getHighestLocaleCoverageLevel("Cldr", filename);
752-
isModernTier = (localeCoverageLevel.getLevel() >= Level.MODERN.getLevel()) || filename.equals(LocaleNames.ROOT) || filename.equals(LocaleNames.UND);
753-
}
754-
return isModernTier;
753+
Level lev = CalculatedCoverageLevels.getInstance().getEffectiveCoverageLevel(filename);
754+
if (lev == null) return false;
755+
return lev.isAtLeast(Level.MODERN);
756+
}
757+
758+
private boolean localeIsBasicTier(String filename) {
759+
Level lev = CalculatedCoverageLevels.getInstance().getEffectiveCoverageLevel(filename);
760+
if (lev == null) return false;
761+
return lev.isAtLeast(Level.BASIC);
755762
}
756763

757764
/**
@@ -965,11 +972,10 @@ public void writeReadme(String outputDir, String packageName) throws IOException
965972
outf.println(configFileReader.getPackageDescriptions().get(basePackageName));
966973
outf.println();
967974
if (packageName.endsWith(FULL_TIER_SUFFIX)) {
968-
outf.println("This package contains the complete set of locales, including what is in the `" +
969-
CLDR_PKG_PREFIX + basePackageName + MODERN_TIER_SUFFIX + "` package.");
975+
outf.println("This package contains all locales.");
970976
outf.println();
971977
} else if (packageName.endsWith(MODERN_TIER_SUFFIX)) {
972-
outf.println("This package contains the set of locales listed as modern coverage. See also the `" +
978+
outf.println("This package contains only the set of locales listed as modern coverage. See also the `" +
973979
CLDR_PKG_PREFIX + basePackageName + FULL_TIER_SUFFIX + "` package.");
974980
outf.println();
975981
}
@@ -1047,7 +1053,7 @@ public void writePackageJson(String outputDir, String packageName) throws IOExce
10471053
final String basePackageName = getBasePackageName(packageName);
10481054
String description = configFileReader.getPackageDescriptions().get(basePackageName);
10491055
if (packageName.endsWith(FULL_TIER_SUFFIX)) {
1050-
description = description + " (complete)";
1056+
description = description + " (all locales)";
10511057
} else if (packageName.endsWith(MODERN_TIER_SUFFIX)) {
10521058
description = description + " (modern coverage locales)";
10531059
}
@@ -1119,32 +1125,33 @@ public void writeDefaultContent(String outputDir) throws IOException {
11191125

11201126
public void writeCoverageLevels(String outputDir) throws IOException {
11211127
final Splitter SEMICOLON = Splitter.on(';').trimResults();
1122-
try (BufferedReader r = FileUtilities.openUTF8Reader(CLDRPaths.COMMON_DIRECTORY + "properties/", "coverageLevels.txt");
1123-
PrintWriter outf = FileUtilities.openUTF8Writer(outputDir + "/cldr-core", "coverageLevels.json");) {
1124-
final Map<String, String> covlocs = new TreeMap<>();
1125-
System.out.println("Creating packaging file => " + outputDir + "/cldr-core" + File.separator + "coverageLevels.json from coverageLevels.txt");
1126-
String line;
1127-
int no = 0;
1128-
while ((line = r.readLine()) != null) {
1129-
no++;
1130-
line = line.trim();
1131-
if(line.startsWith("#") || line.isBlank()) {
1132-
continue;
1133-
}
1134-
final List<String> l = SEMICOLON.splitToList(line);
1135-
if (l.size() != 2) {
1136-
throw new IllegalArgumentException("coverageLevels.txt:"+no+": expected 2 fields, got " + l.size());
1137-
}
1138-
final String uloc = l.get(0);
1139-
final String level = l.get(1);
1140-
final String bcp47loc = unicodeLocaleToString(uloc);
1141-
if (covlocs.put(bcp47loc, level) != null) {
1142-
throw new IllegalArgumentException("coverageLevels.txt:"+no+": duplicate locale " + bcp47loc);
1143-
}
1128+
try (PrintWriter outf = FileUtilities.openUTF8Writer(outputDir + "/cldr-core", "coverageLevels.json");) {
1129+
final Map<String, String> covlocs = new TreeMap<>();
1130+
System.out.println("Creating packaging file => " + outputDir + "/cldr-core" + File.separator + "coverageLevels.json from coverageLevels.txt");
1131+
CalculatedCoverageLevels ccl = CalculatedCoverageLevels.getInstance();
1132+
for (final Map.Entry<String, org.unicode.cldr.util.Level> e : ccl.getLevels().entrySet()) {
1133+
final String uloc = e.getKey();
1134+
final String level = e.getValue().name().toLowerCase();
1135+
final String bcp47loc = unicodeLocaleToString(uloc);
1136+
if (covlocs.put(bcp47loc, level) != null) {
1137+
throw new IllegalArgumentException("coverageLevels.txt: duplicate locale " + bcp47loc);
1138+
}
1139+
}
1140+
final Map<String, String> effectiveCovlocs = new TreeMap<>();
1141+
avl.full.forEach(loc -> {
1142+
final String uloc = ULocale.forLanguageTag(loc).toString();
1143+
final Level lev = ccl.getEffectiveCoverageLevel(uloc);
1144+
if (lev != null) {
1145+
effectiveCovlocs.put(loc, lev.name().toLowerCase());
11441146
}
1145-
JsonObject obj = new JsonObject();
1146-
obj.add("coverageLevels", gson.toJsonTree(covlocs));
1147-
outf.println(gson.toJson(obj));
1147+
});
1148+
JsonObject obj = new JsonObject();
1149+
// exactly what is in CLDR .txt file
1150+
obj.add("coverageLevels", gson.toJsonTree(covlocs));
1151+
1152+
// resolved, including all available locales
1153+
obj.add("effectiveCoverageLevels", gson.toJsonTree(effectiveCovlocs));
1154+
outf.println(gson.toJson(obj));
11481155
}
11491156
}
11501157

@@ -1213,15 +1220,23 @@ public void writePackageList(String outputDir) throws IOException {
12131220
} else {
12141221
{
12151222
JsonObject packageEntry = new JsonObject();
1216-
packageEntry.addProperty("description", e.getValue() + " (full)");
1223+
packageEntry.addProperty("description", e.getValue() + " (basic)");
1224+
packageEntry.addProperty("tier", "full");
1225+
packageEntry.addProperty("name", CLDR_PKG_PREFIX + baseName + FULL_TIER_SUFFIX);
1226+
packages.add(packageEntry);
1227+
pkgsToDesc.put(packageEntry.get("name").getAsString(), packageEntry.get("description").getAsString());
1228+
}
1229+
{
1230+
JsonObject packageEntry = new JsonObject();
1231+
packageEntry.addProperty("description", e.getValue() + " (basic)");
12171232
packageEntry.addProperty("tier", "full");
12181233
packageEntry.addProperty("name", CLDR_PKG_PREFIX + baseName + FULL_TIER_SUFFIX);
12191234
packages.add(packageEntry);
12201235
pkgsToDesc.put(packageEntry.get("name").getAsString(), packageEntry.get("description").getAsString());
12211236
}
12221237
{
12231238
JsonObject packageEntry = new JsonObject();
1224-
packageEntry.addProperty("description", e.getValue() + " (modern only)");
1239+
packageEntry.addProperty("description", e.getValue() + " (modern)");
12251240
packageEntry.addProperty("tier", "modern");
12261241
packageEntry.addProperty("name", CLDR_PKG_PREFIX + baseName + MODERN_TIER_SUFFIX);
12271242
packages.add(packageEntry);
@@ -1716,7 +1731,7 @@ public void processDirectory(String dirName, DraftStatus minimalDraftStatus)
17161731
}
17171732
return new Pair<>(dirName + "/" + filename, totalForThisFile);
17181733
})
1719-
.filter(p -> p.getSecond() == 0)
1734+
.filter(p -> p.getSecond() == 0) // filter out only files which produced no output
17201735
.map(p -> p.getFirst())
17211736
.toArray();
17221737
System.out.println(progressPrefix(total, total) + " Completed parallel process of " + total + " file(s)");

tools/cldr-code/src/main/java/org/unicode/cldr/json/LdmlConvertRules.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,8 @@ public static class SplittableAttributeSpec {
313313
new SplittableAttributeSpec("measurementSystem-category-temperature", "territories", "type"),
314314
new SplittableAttributeSpec("paperSize", "territories", "type"),
315315
new SplittableAttributeSpec("parentLocale", "locales", "parent"),
316+
new SplittableAttributeSpec("collations", "locales", "parent"), // parentLocale component=collations
317+
new SplittableAttributeSpec("segmentations", "locales", "parent"), // parentLocale component=segmentations
316318
new SplittableAttributeSpec("hours", "regions", null),
317319
new SplittableAttributeSpec("dayPeriodRules", "locales", null),
318320
// new SplittableAttributeSpec("group", "contains", "group"),

tools/cldr-code/src/main/java/org/unicode/cldr/util/CalculatedCoverageLevels.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313

1414

1515
public class CalculatedCoverageLevels {
16+
/**
17+
* Assumed level for root. CLDR-16420
18+
*/
19+
final private static Level DEFAULT_ROOT_LEVEL = Level.MODERN;
20+
1621
final Map<String, Level> levels;
1722

1823
protected CalculatedCoverageLevels(Map<String, Level> levels) {
@@ -26,6 +31,30 @@ public Map<String, Level> getLevels() {
2631
return levels;
2732
}
2833

34+
public Level getEffectiveCoverageLevel(String locale) {
35+
return getEffectiveCoverageLevel(CLDRLocale.getInstance(locale));
36+
}
37+
38+
public Level getEffectiveCoverageLevel(CLDRLocale locale) {
39+
// per spec, assumed level for the explicit root locale
40+
if (locale == CLDRLocale.ROOT) {
41+
return DEFAULT_ROOT_LEVEL;
42+
}
43+
// See if there is an explicit entry
44+
final Level level = levels.get(locale.getBaseName());
45+
if (level != null) {
46+
return level;
47+
}
48+
// Otherwise, tail-recurse on parent, unless the parent is root
49+
final CLDRLocale parent = locale.getParent();
50+
if (parent == CLDRLocale.ROOT) {
51+
// not found: no level.
52+
// TODO: should this really be 'core'? if at least core? CLDR-16420
53+
return null;
54+
}
55+
return getEffectiveCoverageLevel(parent);
56+
}
57+
2958
/**
3059
* Is the locale present in the list? If so, it is at least basic.
3160
* Requires an exact match on the locale

tools/cldr-code/src/main/java/org/unicode/cldr/util/Level.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,14 @@ public static Level max(Level... levels) {
160160
}
161161
return result;
162162
}
163+
164+
/**
165+
* Returns true if this is at least the other level.
166+
* Example: lev.isAtLeast(Level.MODERN)
167+
* @param other
168+
* @return
169+
*/
170+
public boolean isAtLeast(Level other) {
171+
return getLevel() >= other.getLevel();
172+
}
163173
}

tools/cldr-code/src/main/resources/org/unicode/cldr/json/JSON_config_supplemental.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ section=weekData ; path=//cldr/supplemental/weekData/.* ; package=core
2626
section=timeData ; path=//cldr/supplemental/timeData/.* ; package=core
2727
section=measurementData ; path=//cldr/supplemental/measurementData/.* ; package=core
2828
section=codeMappings ; path=//cldr/supplemental/codeMappings/.* ; package=core
29-
section=parentLocales ; path=//cldr/supplemental/parentLocales/.* ; package=core
29+
section=parentLocales ; path=//cldr/supplemental/parentLocales.* ; package=core
3030
section=references ; path=//cldr/supplemental/references/.* ; package=core
3131
section=telephoneCodeData ; path=//cldr/supplemental/telephoneCodeData/.* ; package=core
3232
section=windowsZones ; path=//cldr/supplemental/windowsZones/.* ; package=core

tools/cldr-code/src/main/resources/org/unicode/cldr/json/pathTransforms.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,6 @@
170170
< (.*/personNames)/(sampleName)\[@item="([^"]*)"\]/nameField\[@type="([^"]*)"\]$
171171
> $1/$2/$3/$4
172172

173+
# ParentLocales
174+
< (.*/parentLocales)\[@component="([^"]*)"\]/(parentLocale)(.*)$
175+
> $1/$2$4

tools/cldr-code/src/test/java/org/unicode/cldr/util/TestCalculatedCoverageLevels.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,22 @@
22

33
import static org.junit.jupiter.api.Assertions.assertAll;
44
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertEquals;
56
import static org.junit.jupiter.api.Assertions.assertNotNull;
67
import static org.junit.jupiter.api.Assertions.assertTrue;
8+
import static org.junit.jupiter.api.Assumptions.assumeTrue;
79

10+
import java.util.stream.Stream;
11+
12+
import org.junit.jupiter.api.Disabled;
813
import org.junit.jupiter.api.Test;
14+
import org.junit.jupiter.params.ParameterizedTest;
15+
import org.junit.jupiter.params.provider.Arguments;
16+
import org.junit.jupiter.params.provider.CsvSource;
17+
import org.junit.jupiter.params.provider.MethodSource;
18+
19+
import static org.junit.jupiter.params.provider.Arguments.arguments;
20+
921

1022
public class TestCalculatedCoverageLevels {
1123
@Test
@@ -21,4 +33,45 @@ void TestBasic() {
2133
() -> assertTrue(CalculatedCoverageLevels.getInstance().isLocaleAtLeastBasic("fr"), "fr=true"),
2234
() -> assertFalse(CalculatedCoverageLevels.getInstance().isLocaleAtLeastBasic("und"), "und=false"));
2335
}
36+
37+
public static Stream<Arguments> allLocales() {
38+
return CLDRConfig.getInstance().getFullCldrFactory().getAvailable().stream().map(str -> arguments(str));
39+
}
40+
41+
@ParameterizedTest(name = "{0}")
42+
@MethodSource("allLocales")
43+
@Test
44+
@Disabled
45+
/**
46+
* Test compares coverage level to the Locales.txt goals.
47+
* Skipped because it fails (for cv, as of v43), but there may be a useful form for it in the future
48+
* @param locale
49+
*/
50+
void TestCoverageLevelsVsCldrOrg(final String locale) {
51+
final Level coverageLevel = CalculatedCoverageLevels.getInstance().getLevels().get(locale);
52+
assumeTrue(coverageLevel != null, () -> "CalculatedCoverageLevel not available for " + locale);
53+
final Level localeCoverageLevel = StandardCodes.make().getHighestLocaleCoverageLevel("Cldr", locale);
54+
assumeTrue(localeCoverageLevel != null, () -> "Cldr membership not available for " + locale);
55+
assertTrue(localeCoverageLevel.compareTo(coverageLevel) >= 0,
56+
() -> String.format("Expected Locales.txt:Cldr=%s at least coverageLevels=%s", coverageLevel.name(), localeCoverageLevel.name()));
57+
}
58+
59+
@ParameterizedTest
60+
@CsvSource({
61+
"en_US, modern",
62+
"en, modern",
63+
"mul, null",
64+
"root, modern",
65+
"sr_Cyrl, modern",
66+
"sr_Cyrl_XK, modern",
67+
"sr, modern",
68+
})
69+
void TestEffective(final String loc, String l) {
70+
Level level = null;
71+
if (!l.equals("null")) {
72+
level = Level.fromString(l.toUpperCase());
73+
}
74+
CalculatedCoverageLevels ccl = CalculatedCoverageLevels.getInstance(); // TODO: data sensitivity. Ideally would use a private instance of CCL from a static file.
75+
assertEquals(level, ccl.getEffectiveCoverageLevel(loc));
76+
}
2477
}

0 commit comments

Comments
 (0)