• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.unicode.cldr.tool;
2 
3 import java.io.IOException;
4 import java.io.PrintWriter;
5 import java.io.StringWriter;
6 import java.io.UncheckedIOException;
7 import java.util.LinkedHashSet;
8 import java.util.List;
9 import java.util.Set;
10 
11 import org.unicode.cldr.test.BuildIcuCompactDecimalFormat;
12 import org.unicode.cldr.test.BuildIcuCompactDecimalFormat.CurrencyStyle;
13 import org.unicode.cldr.tool.GeneratePluralRanges.RangeSample;
14 import org.unicode.cldr.util.CLDRConfig;
15 import org.unicode.cldr.util.CLDRFile;
16 import org.unicode.cldr.util.CLDRURLS;
17 import org.unicode.cldr.util.CldrUtility;
18 import org.unicode.cldr.util.Factory;
19 import org.unicode.cldr.util.ICUServiceBuilder;
20 import org.unicode.cldr.util.LanguageTagCanonicalizer;
21 import org.unicode.cldr.util.PluralSnapshot;
22 import org.unicode.cldr.util.SupplementalDataInfo;
23 import org.unicode.cldr.util.SupplementalDataInfo.PluralInfo;
24 import org.unicode.cldr.util.SupplementalDataInfo.PluralInfo.Count;
25 import org.unicode.cldr.util.SupplementalDataInfo.PluralType;
26 
27 import com.google.common.base.Joiner;
28 import com.ibm.icu.impl.Utility;
29 import com.ibm.icu.impl.number.DecimalQuantity;
30 import com.ibm.icu.text.CompactDecimalFormat;
31 import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
32 import com.ibm.icu.text.NumberFormat;
33 import com.ibm.icu.text.PluralRules;
34 import com.ibm.icu.text.PluralRules.DecimalQuantitySamples;
35 import com.ibm.icu.text.PluralRules.Operand;
36 import com.ibm.icu.util.ICUUncheckedIOException;
37 import com.ibm.icu.util.ULocale;
38 
39 public class ShowPlurals {
40 
41     private static final String NO_PLURAL_DIFFERENCES = "<i>no plural differences</i>";
42     private static final String NOT_AVAILABLE = "<i>Not available.<br>Please <a target='_blank' href='" + CLDRURLS.CLDR_NEWTICKET_URL
43         + "'>file a ticket</a> to supply.</i>";
44     final SupplementalDataInfo supplementalDataInfo;
45 
main(String[] args)46     public static void main(String[] args) {
47         Factory cldrFactory = CLDRConfig.getInstance().getCldrFactory();//.make(CLDRPaths.MAIN_DIRECTORY, ".*");
48         CLDRFile english = CLDRConfig.getInstance().getEnglish();
49         StringWriter sw = new StringWriter();
50         PrintWriter pw = new PrintWriter(sw);
51 
52         try {
53             new ShowPlurals().printPlurals(english, null, pw, cldrFactory);
54         } catch (IOException e) {
55             throw new UncheckedIOException(e);
56         }
57     }
58 
ShowPlurals()59     public ShowPlurals() {
60         supplementalDataInfo = CLDRConfig.getInstance().getSupplementalDataInfo();
61     }
62 
ShowPlurals(SupplementalDataInfo supplementalDataInfo)63     public ShowPlurals(SupplementalDataInfo supplementalDataInfo) {
64         this.supplementalDataInfo = supplementalDataInfo;
65     }
66 
printPlurals(CLDRFile english, String localeFilter, PrintWriter index, Factory factory)67     public void printPlurals(CLDRFile english, String localeFilter, PrintWriter index, Factory factory) throws IOException {
68         String section1 = "Rules";
69         String section2 = "Comparison";
70 
71         final String title = "Language Plural Rules";
72         final PrintWriter pw = new PrintWriter(new FormattedFileWriter(null, title, null, ShowLanguages.SUPPLEMENTAL_INDEX_ANCHORS));
73 
74         pw.append("<div style='margin-right:2em; margin-left:2em'>\n");
75         ShowLanguages.showContents(pw, "rules", "Rules", "comparison", "Comparison");
76 
77         pw.append("<h2>" + CldrUtility.getDoubleLinkedText("rules", "1. " + section1) + "</h2>" + System.lineSeparator());
78         pw.append("<div style='margin-right:2em; margin-left:2em'>\n");
79         printPluralTable(english, localeFilter, pw, factory);
80         pw.append("</div>\n");
81 
82         pw.append("<h2>" + CldrUtility.getDoubleLinkedText("comparison", "2. " + section2) + "</h2>" + System.lineSeparator());
83         pw.append("<p style='text-align:left'>The plural forms are abbreviated by first letter, with 'x' for 'other'. "
84             +
85             "If values are made redundant by explicit 0 and 1, they are underlined. " +
86             "The fractional and integral results are separated for clarity.</p>" + System.lineSeparator());
87         pw.append("<div style='margin-right:2em; margin-left:2em'>\n");
88         PluralSnapshot.writeTables(english, pw);
89         pw.append("</div>\n");
90         pw.append("</div>\n");
91         appendBlanksForScrolling(pw);
92         pw.close();
93     }
94 
appendBlanksForScrolling(final Appendable pw)95     public static void appendBlanksForScrolling(final Appendable pw) {
96         try {
97             pw.append(Utility.repeat("<br>", 100)).append(System.lineSeparator());
98         } catch (IOException e) {
99             throw new ICUUncheckedIOException(e);
100         }
101     }
102 
printPluralTable(CLDRFile english, String localeFilter, Appendable appendable, Factory factory)103     public void printPluralTable(CLDRFile english, String localeFilter,
104         Appendable appendable, Factory factory) throws IOException {
105 
106         final TablePrinter tablePrinter = new TablePrinter()
107             .setTableAttributes("class='dtf-table'")
108             .addColumn("Name", "class='source'", null, "class='source'", true).setSortPriority(0).setHeaderAttributes("class='dtf-th'")
109             .setCellAttributes("class='dtf-s'")
110             .setBreakSpans(true).setRepeatHeader(true)
111             .addColumn("Code", "class='source'", CldrUtility.getDoubleLinkMsg(), "class='source'", true).setHeaderAttributes("class='dtf-th'")
112             .setCellAttributes("class='dtf-s'")
113             .addColumn("Type", "class='source'", null, "class='source'", true).setHeaderAttributes("class='dtf-th'").setCellAttributes("class='dtf-s'")
114             .setBreakSpans(true)
115             .addColumn("Category", "class='target'", null, "class='target'", true).setHeaderAttributes("class='dtf-th'").setCellAttributes("class='dtf-s'")
116             .setSpanRows(false)
117             .addColumn("Examples", "class='target'", null, "class='target'", true).setHeaderAttributes("class='dtf-th'").setCellAttributes("class='dtf-s'")
118             .addColumn("Minimal Pairs", "class='target'", null, "class='target'", true).setHeaderAttributes("class='dtf-th'").setCellAttributes("class='dtf-s'")
119             .addColumn("Rules", "class='target'", null, "class='target' nowrap", true).setHeaderAttributes("class='dtf-th'").setCellAttributes("class='dtf-s'")
120             .setSpanRows(false);
121         PluralRulesFactory prf = PluralRulesFactory.getInstance(supplementalDataInfo);
122         //Map<ULocale, PluralRulesFactory.SamplePatterns> samples = PluralRulesFactory.getLocaleToSamplePatterns();
123         Set<String> cardinalLocales = supplementalDataInfo.getPluralLocales(PluralType.cardinal);
124         Set<String> ordinalLocales = supplementalDataInfo.getPluralLocales(PluralType.ordinal);
125         Set<String> all = new LinkedHashSet<>(cardinalLocales);
126         all.addAll(ordinalLocales);
127 
128         LanguageTagCanonicalizer canonicalizer = new LanguageTagCanonicalizer();
129         SampleMaker sampleMaker = new SampleMaker();
130 
131         for (String locale : supplementalDataInfo.getPluralLocales()) {
132             if (localeFilter != null && !localeFilter.equals(locale) || locale.equals("root")) {
133                 continue;
134             }
135             final String name = english.getName(locale);
136             String canonicalLocale = canonicalizer.transform(locale);
137             if (!locale.equals(canonicalLocale)) {
138                 String redirect = "<i>=<a href='#" + canonicalLocale + "'>" + canonicalLocale + "</a></i>";
139                 tablePrinter.addRow()
140                 .addCell(name)
141                 .addCell(locale)
142                 .addCell(redirect)
143                 .addCell(redirect)
144                 .addCell(redirect)
145                 .addCell(redirect)
146                 .addCell(redirect)
147                 .finishRow();
148                 continue;
149             }
150 
151             CLDRFile cldrFile2;
152             try {
153                 cldrFile2 = factory.make(locale, true);
154             } catch (Exception e1) {
155                 continue;
156             }
157             sampleMaker.setCldrFile(cldrFile2);
158 
159             for (PluralType pluralType : PluralType.values()) {
160                 if (pluralType == PluralType.ordinal && !ordinalLocales.contains(locale)
161                     || pluralType == PluralType.cardinal && !cardinalLocales.contains(locale)) {
162                     continue;
163                 }
164                 final PluralInfo plurals = supplementalDataInfo.getPlurals(pluralType, locale);
165                 ULocale locale2 = new ULocale(locale);
166                 final PluralMinimalPairs samplePatterns = PluralMinimalPairs.getInstance(locale2.toString());
167                 //                    pluralType == PluralType.ordinal ? null
168                 //                    : CldrUtility.get(samples, locale2);
169 
170                 String rules = plurals.getRules();
171                 rules += rules.length() == 0 ? "other:<i>everything</i>" : ";other:<i>everything else</i>";
172                 rules = rules.replace(":", " → ").replace(";", ";<br>");
173                 PluralRules pluralRules = plurals.getPluralRules();
174                 //final Map<PluralInfo.Count, String> typeToExamples = plurals.getCountToStringExamplesMap();
175                 //final String examples = typeToExamples.get(type).toString().replace(";", ";<br>");
176                 Set<Count> counts = plurals.getCounts();
177                 for (PluralInfo.Count count : counts) {
178                     String keyword = count.toString();
179                     DecimalQuantitySamples exampleList = pluralRules.getDecimalSamples(keyword, PluralRules.SampleType.INTEGER); // plurals.getSamples9999(count);
180                     DecimalQuantitySamples exampleList2 = pluralRules.getDecimalSamples(keyword, PluralRules.SampleType.DECIMAL);
181                     if (exampleList == null) {
182                         exampleList = exampleList2;
183                         exampleList2 = null;
184                     }
185                     String examples = getExamples(exampleList);
186                     if (exampleList2 != null) {
187                         examples += "<br>" + getExamples(exampleList2);
188                     }
189                     String rule = pluralRules.getRules(keyword);
190                     rule = rule != null ? rule.replace(":", " → ")
191                         .replace(" and ", " and<br>&nbsp;&nbsp;")
192                         .replace(" or ", " or<br>")
193                         : counts.size() == 1 ? "<i>everything</i>"
194                             : "<i>everything else</i>";
195 
196                     String sample = counts.size() == 1 ? NO_PLURAL_DIFFERENCES : NOT_AVAILABLE;
197                     if (samplePatterns != null) {
198                         String samplePattern = samplePatterns.get(pluralType.standardType, Count.valueOf(keyword)); // CldrUtility.get(samplePatterns.keywordToPattern, Count.valueOf(keyword));
199                         if (samplePattern != null) {
200                             DecimalQuantity sampleDecimal = PluralInfo.getNonZeroSampleIfPossible(exampleList);
201                             sample = sampleMaker.getSample(sampleDecimal, samplePattern);
202                             if (exampleList2 != null) {
203                                 sampleDecimal = PluralInfo.getNonZeroSampleIfPossible(exampleList2);
204                                 sample += "<br>" + sampleMaker.getSample(sampleDecimal, samplePattern);
205                             }
206                         }
207                     }
208                     tablePrinter.addRow()
209                     .addCell(name)
210                     .addCell(locale)
211                     .addCell(pluralType.toString())
212                     .addCell(count.toString())
213                     .addCell(examples.toString())
214                     .addCell(sample)
215                     .addCell(rule)
216                     .finishRow();
217                 }
218             }
219             List<RangeSample> rangeInfoList = null;
220             try {
221                 rangeInfoList = new GeneratePluralRanges(supplementalDataInfo).getRangeInfo(cldrFile2);
222             } catch (Exception e) {
223             }
224             if (rangeInfoList != null) {
225                 for (RangeSample item : rangeInfoList) {
226                     tablePrinter.addRow()
227                     .addCell(name)
228                     .addCell(locale)
229                     .addCell("range")
230                     .addCell(item.start + "+" + item.end)
231                     .addCell(item.min + "–" + item.max)
232                     .addCell(item.resultExample.replace(". ", ".<br>"))
233                     .addCell(item.start + " + " + item.end + " → " + item.result)
234                     .finishRow();
235                 }
236             } else {
237                 String message = supplementalDataInfo.getPlurals(PluralType.cardinal, locale).getCounts().size() == 1 ? NO_PLURAL_DIFFERENCES : NOT_AVAILABLE;
238                 tablePrinter.addRow()
239                 .addCell(name)
240                 .addCell(locale)
241                 .addCell("range")
242                 .addCell("<i>n/a</i>")
243                 .addCell("<i>n/a</i>")
244                 .addCell(message)
245                 .addCell("<i>n/a</i>")
246                 .finishRow();
247             }
248         }
249         appendable.append(tablePrinter.toTable()).append(System.lineSeparator());
250     }
251 
getExamples(DecimalQuantitySamples exampleList)252     private String getExamples(DecimalQuantitySamples exampleList) {
253         return Joiner.on(", ").join(exampleList.getSamples()) + (exampleList.bounded ? "" : ", …");
254     }
255 
256     static final class SampleMaker {
257         ICUServiceBuilder icusb = new ICUServiceBuilder();
258         CLDRFile cldrFile;
259 
setCldrFile(CLDRFile cldrFile)260         void setCldrFile(CLDRFile cldrFile) {
261            this.cldrFile = cldrFile;
262            icusb.setCldrFile(cldrFile);
263         }
264 
getSample(DecimalQuantity numb, String samplePattern)265     private String getSample(DecimalQuantity numb, String samplePattern) {
266         String sample;
267         String value;
268         if (numb.getExponent() > 0) {
269             Set<String> debugCreationErrors = new LinkedHashSet<>();
270             String[] debugOriginals = null;
271             CompactDecimalFormat cdfCurr = BuildIcuCompactDecimalFormat.build(
272                 cldrFile, debugCreationErrors,
273                 debugOriginals, CompactStyle.SHORT,
274                 ULocale.forLanguageTag(cldrFile.getLocaleID()),
275                 CurrencyStyle.PLAIN, null);
276             value = cdfCurr.format(numb.toDouble());
277         } else {
278             NumberFormat nf = icusb.getGenericNumberFormat("latn");
279             nf.setMaximumFractionDigits((int) numb.getPluralOperand(Operand.v));
280             nf.setMinimumFractionDigits((int) numb.getPluralOperand(Operand.v));
281             value = nf.format(numb.toDouble());
282         }
283         sample = samplePattern
284             .replace('\u00A0', '\u0020')
285             .replace("{0}", value)
286             .replace(". ", ".<br>");
287         return sample;
288     }
289     }
290 }
291