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