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