Author: tchemit Date: 2009-05-14 05:41:01 +0000 (Thu, 14 May 2009) New Revision: 1550 Added: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/ nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/CountryEnum.java nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nDefaultTooltipFilter.java nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nFileReader.java nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nFilter.java nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nUtil.java nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/LanguageEnum.java nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/ nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundle.java nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleEntry.java nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleFactory.java nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleScope.java nuiton-i18n-api/trunk/src/main/java/org/nuiton/util/ nuiton-i18n-api/trunk/src/main/java/org/nuiton/util/LocaleConverter.java Log: migrate to nuiton Copied: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/CountryEnum.java (from rev 1542, nuiton-i18n-api/trunk/src/main/java/org/codelutin/i18n/CountryEnum.java) =================================================================== --- nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/CountryEnum.java (rev 0) +++ nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/CountryEnum.java 2009-05-14 05:41:01 UTC (rev 1550) @@ -0,0 +1,284 @@ +/* + * *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n; + +/** + * Une énumération pour représenter le pays d'une locale + * <p/> + * <a href="http://www.iso.org/iso/country_codes"><code>ISO 3166-1:1998 (ICS n° 01.140.20)</code></a>. + * <p/> + * <a href="http://www.iso.org/iso/french_country_names_and_code_elements">la liste des codes</a> + * <p/> + * Chaque pays est repésenté ainsi : + * <pre>A2, // A3 Number Country name</pre> + * + * @author chemit + */ +public enum CountryEnum { + + AF, // AFG 004 AFGHANISTAN + AL, // ALB 008 ALBANIA + DZ, // DZA 012 ALGERIA + AS, // ASM 016 AMERICAN SAMOA + AD, // AND 020 ANDORRA + AO, // AGO 024 ANGOLA + AI, // AIA 660 ANGUILLA + AQ, // ATA 010 ANTARCTICA + AG, // ATG 028 ANTIGUA AND BARBUDA + AR, // ARG 032 ARGENTINA + AM, // ARM 051 ARMENIA + AW, // ABW 533 ARUBA + AU, // AUS 036 AUSTRALIA + AT, // AUT 040 AUSTRIA + AZ, // AZE 031 AZERBAIJAN + BS, // BHS 044 BAHAMAS + BH, // BHR 048 BAHRAIN + BD, // BGD 050 BANGLADESH + BB, // BRB 052 BARBADOS + BY, // BLR 112 BELARUS + BE, // BEL 056 BELGIUM + BZ, // BLZ 084 BELIZE + BJ, // BEN 204 BENIN + BM, // BMU 060 BERMUDA + BT, // BTN 064 BHUTAN + BO, // BOL 068 BOLIVIA + BA, // BIH 070 BOSNIA AND HERZEGOWINA + BW, // BWA 072 BOTSWANA + BV, // BVT 074 BOUVET ISLAND + BR, // BRA 076 BRAZIL + IO, // IOT 086 BRITISH INDIAN OCEAN TERRITORY + BN, // BRN 096 BRUNEI DARUSSALAM + BG, // BGR 100 BULGARIA + BF, // BFA 854 BURKINA FASO + BI, // BDI 108 BURUNDI + KH, // KHM 116 CAMBODIA + CM, // CMR 120 CAMEROON + CA, // CAN 124 CANADA + CV, // CPV 132 CAPE VERDE + KY, // CYM 136 CAYMAN ISLANDS + CF, // CAF 140 CENTRAL AFRICAN REPUBLIC + TD, // TCD 148 CHAD + CL, // CHL 152 CHILE + CN, // CHN 156 CHINA + CX, // CXR 162 CHRISTMAS ISLAND + CC, // CCK 166 COCOS (KEELING) ISLANDS + CO, // COL 170 COLOMBIA + KM, // COM 174 COMOROS + CG, // COG 178 CONGO + CK, // COK 184 COOK ISLANDS + CR, // CRI 188 COSTA RICA + CI, // CIV 384 COTE D'IVOIRE + HR, // HRV 191 CROATIA (local name: Hrvatska) + CU, // CUB 192 CUBA + CY, // CYP 196 CYPRUS + CZ, // CZE 203 CZECH REPUBLIC + DK, // DNK 208 DENMARK + DJ, // DJI 262 DJIBOUTI + DM, // DMA 212 DOMINICA + DO, // DOM 214 DOMINICAN REPUBLIC + TP, // TMP 626 EAST TIMOR + EC, // ECU 218 ECUADOR + EG, // EGY 818 EGYPT + SV, // SLV 222 EL SALVADOR + GQ, // GNQ 226 EQUATORIAL GUINEA + ER, // ERI 232 ERITREA + EE, // EST 233 ESTONIA + ET, // ETH 210 ETHIOPIA + FK, // FLK 238 FALKLAND ISLANDS (MALVINAS) + FO, // FRO 234 FAROE ISLANDS + FJ, // FJI 242 FIJI + FI, // FIN 246 FINLAND + FR, // FRA 250 FRANCE + FX, // FXX 249 FRANCE, METROPOLITAN + GF, // GUF 254 FRENCH GUIANA + PF, // PYF 258 FRENCH POLYNESIA + TF, // ATF 260 FRENCH SOUTHERN TERRITORIES + GA, // GAB 266 GABON + GM, // GMB 270 GAMBIA + GE, // GEO 268 GEORGIA + DE, // DEU 276 GERMANY + GH, // GHA 288 GHANA + GI, // GIB 292 GIBRALTAR + GR, // GRC 300 GREECE + GL, // GRL 304 GREENLAND + GD, // GRD 308 GRENADA + GP, // GLP 312 GUADELOUPE + GU, // GUM 316 GUAM + GT, // GTM 320 GUATEMALA + GN, // GIN 324 GUINEA + GW, // GNB 624 GUINEA-BISSAU + GY, // GUY 328 GUYANA + HT, // HTI 332 HAITI + HM, // HMD 334 HEARD AND MC DONALD ISLANDS + HN, // HND 340 HONDURAS + HK, // HKG 344 HONG KONG + HU, // HUN 348 HUNGARY + IS, // ISL 352 ICELAND + IN, // IND 356 INDIA + ID, // IDN 360 INDONESIA + IR, // IRN 364 IRAN (ISLAMIC REPUBLIC OF) + IQ, // IRQ 368 IRAQ + IE, // IRL 372 IRELAND + IL, // ISR 376 ISRAEL + IT, // ITA 380 ITALY + JM, // JAM 388 JAMAICA + JP, // JPN 392 JAPAN + JO, // JOR 400 JORDAN + KZ, // KAZ 398 KAZAKHSTAN + KE, // KEN 404 KENYA + KI, // KIR 296 KIRIBATI + KP, // PRK 408 KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF + KR, // KOR 410 KOREA, REPUBLIC OF + KW, // KWT 414 KUWAIT + KG, // KGZ 417 KYRGYZSTAN + LA, // LAO 418 LAO PEOPLE'S DEMOCRATIC REPUBLIC + LV, // LVA 428 LATVIA + LB, // LBN 422 LEBANON + LS, // LSO 426 LESOTHO + LR, // LBR 430 LIBERIA + LY, // LBY 434 LIBYAN ARAB JAMAHIRIYA + LI, // LIE 438 LIECHTENSTEIN + LT, // LTU 440 LITHUANIA + LU, // LUX 442 LUXEMBOURG + MO, // MAC 446 MACAU + MK, // MKD 807 (provis) MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF + MG, // MDG 450 MADAGASCAR + MW, // MWI 454 MALAWI + MY, // MYS 458 MALAYSIA + MV, // MDV 462 MALDIVES + ML, // MLI 466 MALI + MT, // MLT 470 MALTA + MH, // MHL 584 MARSHALL ISLANDS + MQ, // MTQ 474 MARTINIQUE + MR, // MRT 478 MAURITANIA + MU, // MUS 480 MAURITIUS + YT, // MYT 175 MAYOTTE + MX, // MEX 484 MEXICO + FM, // FSM 583 MICRONESIA, FEDERATED STATES OF + MD, // MDA 498 MOLDOVA, REPUBLIC OF + MC, // MCO 492 MONACO + MN, // MNG 496 MONGOLIA + MS, // MSR 500 MONTSERRAT + MA, // MAR 504 MOROCCO + MZ, // MOZ 508 MOZAMBIQUE + MM, // MMR 104 MYANMAR + NA, // NAM 516 NAMIBIA + NR, // NRU 520 NAURU + NP, // NPL 524 NEPAL + NL, // NLD 528 NETHERLANDS + AN, // ANT 530 NETHERLANDS ANTILLES + NC, // NCL 540 NEW CALEDONIA + NZ, // NZL 554 NEW ZEALAND + NI, // NIC 558 NICARAGUA + NE, // NER 562 NIGER + NG, // NGA 566 NIGERIA + NU, // NIU 570 NIUE + NF, // NFK 574 NORFOLK ISLAND + MP, // MNP 580 NORTHERN MARIANA ISLANDS + NO, // NOR 578 NORWAY + OM, // OMN 512 OMAN + PK, // PAK 586 PAKISTAN + PW, // PLW 585 PALAU + PA, // PAN 591 PANAMA + PG, // PNG 598 PAPUA NEW GUINEA + PY, // PRY 600 PARAGUAY + PE, // PER 604 PERU + PH, // PHL 608 PHILIPPINES + PN, // PCN 612 PITCAIRN + PL, // POL 616 POLAND + PT, // PRT 620 PORTUGAL + PR, // PRI 630 PUERTO RICO + QA, // QAT 634 QATAR + RE, // REU 638 REUNION + RO, // ROM 642 ROMANIA + RU, // RUS 643 RUSSIAN FEDERATION + RW, // RWA 646 RWANDA + KN, // KNA 659 SAINT KITTS AND NEVIS + LC, // LCA 662 SAINT LUCIA + VC, // VCT 670 SAINT VINCENT AND THE GRENADINES + WS, // WSM 882 SAMOA + SM, // SMR 674 SAN MARINO + ST, // STP 678 SAO TOME AND PRINCIPE + SA, // SAU 682 SAUDI ARABIA + SN, // SEN 686 SENEGAL + SC, // SYC 690 SEYCHELLES + SL, // SLE 694 SIERRA LEONE + SG, // SGP 702 SINGAPORE + SK, // SVK 703 SLOVAKIA (Slovak Republic) + SI, // SVN 705 SLOVENIA + SB, // SLB 090 SOLOMON ISLANDS + SO, // SOM 706 SOMALIA + ZA, // ZAF 710 SOUTH AFRICA + ES, // ESP 724 SPAIN + LK, // LKA 144 SRI LANKA + SH, // SHN 654 ST. HELENA + PM, // SPM 666 ST. PIERRE AND MIQUELON + SD, // SDN 736 SUDAN + SR, // SUR 740 SURINAME + SJ, // SJM 744 SVALBARD AND JAN MAYEN ISLANDS + SZ, // SWZ 748 SWAZILAND + SE, // SWE 752 SWEDEN + CH, // CHE 756 SWITZERLAND + SY, // SYR 760 SYRIAN ARAB REPUBLIC + TW, // TWN 158 TAIWAN, PROVINCE OF CHINA + TJ, // TJK 762 TAJIKISTAN + TZ, // TZA 834 TANZANIA, UNITED REPUBLIC OF + TH, // THA 764 THAILAND + TG, // TGO 768 TOGO + TK, // TKL 772 TOKELAU + TO, // TON 776 TONGA + TT, // TTO 780 TRINIDAD AND TOBAGO + TN, // TUN 788 TUNISIA + TR, // TUR 792 TURKEY + TM, // TKM 795 TURKMENISTAN + TC, // TCA 796 TURKS AND CAICOS ISLANDS + TV, // TUV 798 TUVALU + UG, // UGA 800 UGANDA + UA, // UKR 804 UKRAINE + AE, // ARE 784 UNITED ARAB EMIRATES + GB, // GBR 826 UNITED KINGDOM + US, // USA 840 UNITED STATES + UM, // UMI 581 UNITED STATES MINOR OUTLYING ISLANDS + UY, // URY 858 URUGUAY + UZ, // UZB 860 UZBEKISTAN + VU, // VUT 548 VANUATU + VA, // VAT 336 VATICAN CITY STATE (HOLY SEE) + VE, // VEN 862 VENEZUELA + VN, // VNM 704 VIET NAM + VG, // VGB 092 VIRGIN ISLANDS (BRITISH) + VI, // VIR 850 VIRGIN ISLANDS (U.S.) + WF, // WLF 876 WALLIS AND FUTUNA ISLANDS + EH, // ESH 732 WESTERN SAHARA + YE, // YEM 887 YEMEN + YU, // YUG 891 YUGOSLAVIA + ZR, // ZAR 180 ZAIRE + ZM, // ZMB 894 ZAMBIA + ZW; // ZWE 716 ZIMBABWE + + public static CountryEnum valueOf(String country, CountryEnum defaultValue) { + CountryEnum countryValue = null; + try { + countryValue = CountryEnum.valueOf(country.toUpperCase()); + } catch (IllegalArgumentException e) { + System.err.println("unfound country " + country + ", will use default one : " + defaultValue); + } catch (NullPointerException e) { + System.err.println("unfound country " + country + ", will use default one : " + defaultValue); + } + return countryValue == null ? defaultValue : countryValue; + } +} Property changes on: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/CountryEnum.java ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:mergeinfo + Name: svn:eol-style + native Copied: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nDefaultTooltipFilter.java (from rev 1543, nuiton-i18n-api/trunk/src/main/java/org/codelutin/i18n/I18nDefaultTooltipFilter.java) =================================================================== --- nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nDefaultTooltipFilter.java (rev 0) +++ nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nDefaultTooltipFilter.java 2009-05-14 05:41:01 UTC (rev 1550) @@ -0,0 +1,43 @@ +/* *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +/* * + * i18nDefaultTooltipFilter.java + * + * Created: 2 déc. 2003 + * + * @author Benjamin Poussin <poussin@codelutin.com> + * Copyright Code Lutin + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : $Author$ + */ + +package org.nuiton.i18n; + +public class I18nDefaultTooltipFilter implements I18nFilter { // I18nDefaultTooltipFilter + + @Override + public String applyFilter(String message) { + if (message != null && message.startsWith("defaultToolTip-")) { + return null; + } + return message; + } +} // I18nDefaultTooltipFilter + Copied: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nFileReader.java (from rev 1542, nuiton-i18n-api/trunk/src/main/java/org/codelutin/i18n/I18nFileReader.java) =================================================================== --- nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nFileReader.java (rev 0) +++ nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nFileReader.java 2009-05-14 05:41:01 UTC (rev 1550) @@ -0,0 +1,131 @@ +/* *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +/* * + * I18nFileReader.java + * + * Created: Nov 22, 2004 + * + * @author Cédric Pineau <pineau@codelutin.com> + * @version $Revision$ + * + * Last update : $Date$ + * by : $Author$ + */ + +package org.nuiton.i18n; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.util.Properties; +import java.util.regex.Pattern; + +/** Classe assurant la lecture et les possibles traitement nécessaires à I18n. */ +public class I18nFileReader extends Properties { + + protected static final Pattern commentPattern = Pattern.compile("[^\\\\]#"); + + protected static final Pattern splitPattern = Pattern.compile("[^\\\\]="); + private static final long serialVersionUID = 3611718334066783394L; + + public void load(InputStream inStream, String encodingTo) throws IOException { + Charset charsetTo = Charset.forName(encodingTo); + BufferedReader readerFile; + readerFile = new BufferedReader(new InputStreamReader(inStream, charsetTo)); + String lineFile; + StringBuilder builderFile; + builderFile = new StringBuilder(); + while ((lineFile = readerFile.readLine()) != null) { + builderFile.append(lineFile).append('\n'); + } + readerFile.close(); + super.load(new ByteArrayInputStream(builderFile.toString().getBytes())); + } + + protected String interpretBackslashes(String message) { + int backslashIndex = -1; + while ((backslashIndex = message.indexOf("\\", backslashIndex + 1)) != -1) { + if (message.length() >= backslashIndex + 1) { + char charNextToBackslash = message.charAt(backslashIndex + 1); + char replacementChar; + switch (charNextToBackslash) { + case '\\': + replacementChar = '\\'; + break; + case 't': + replacementChar = '\t'; + break; + case 'n': + replacementChar = '\n'; + break; + case ' ': + replacementChar = ' '; + break; + case '=': + replacementChar = '='; + break; + case ':': + replacementChar = ':'; + break; + default: + replacementChar = '\\'; + break; + } + message = message.substring(0, backslashIndex) + replacementChar + message.substring(backslashIndex + 2); + } + } + return message; + } + + private static char[] chars = {'\\', '\n', '\t', ' ', '=', ':'}; + + protected String serializeBackslashes(String message) { + for (char c : chars) { + int charIndex = -1; + while ((charIndex = message.indexOf(c, charIndex + 2)) != -1) { + String replacementString = "" + c; + switch (c) { + case '\\': + replacementString = "\\\\"; + break; + case '\t': + replacementString = "\\t"; + break; + case '\n': + replacementString = "\\n"; + break; + case ' ': + replacementString = "\\ "; + break; + case '=': + replacementString = "\\="; + break; + case ':': + replacementString = "\\:"; + break; + } + message = message.substring(0, charIndex) + replacementString + message.substring(charIndex + 1); + } + } + return message; + } + +} //I18nFileReader Property changes on: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nFileReader.java ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:mergeinfo + Name: svn:eol-style + native Copied: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nFilter.java (from rev 1543, nuiton-i18n-api/trunk/src/main/java/org/codelutin/i18n/I18nFilter.java) =================================================================== --- nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nFilter.java (rev 0) +++ nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nFilter.java 2009-05-14 05:41:01 UTC (rev 1550) @@ -0,0 +1,37 @@ +/* *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +/* * + * i18nFilter.java + * + * Created: 2 déc. 2003 + * + * @author Benjamin Poussin <poussin@codelutin.com> + * Copyright Code Lutin + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : $Author$ + */ + +package org.nuiton.i18n; + +public interface I18nFilter { // I18nFilter + + public String applyFilter(String message); +} // I18nFilter + Copied: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nUtil.java (from rev 1542, nuiton-i18n-api/trunk/src/main/java/org/codelutin/i18n/I18nUtil.java) =================================================================== --- nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nUtil.java (rev 0) +++ nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nUtil.java 2009-05-14 05:41:01 UTC (rev 1550) @@ -0,0 +1,194 @@ +package org.nuiton.i18n; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.logging.Logger; +import java.util.zip.ZipFile; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.util.LocaleConverter; + +/** + * + * @author chemit + */ +public class I18nUtil { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + private static final Log log = LogFactory.getLog(I18nUtil.class); + public static final String ISO_8859_1_ENCONDING = "ISO-8859-1"; + public static final String UTF_8_ENCONDING = "UTF-8"; + public static final String DEFAULT_ENCODING = ISO_8859_1_ENCONDING; + public static final Locale DEFAULT_LOCALE = Locale.UK; + + /** + * Parse a list of {@link Locale} seperated by comma. + * + * Example : fr_FR,en_GB + * + * @param str the string representation of locale separated by comma + * @return list of available locales + * @throws IllegalArgumentException ia a locale is not valid + */ + public static Locale[] parseLocales(String str) throws IllegalArgumentException { + List<Locale> result = new java.util.ArrayList<Locale>(); + String[] bundlesToUse = str.split(","); + for (int i = 0, j = bundlesToUse.length; i < j; i++) { + String s = bundlesToUse[i].trim(); + // on devrait verifier que le bundle existe + try { + Locale l = (Locale) new LocaleConverter().convert(Locale.class, s); + result.add(l); + } catch (Exception e) { + throw new IllegalArgumentException("bundle " + s + " is not a valid locale,e"); + } + } + return result.toArray(new Locale[result.size()]); + } + + public static Locale newLocale(String str) { + if (str == null) { + // get use locale + return newLocale(null, null); + } + try { + return (Locale) new LocaleConverter().convert(Locale.class, str); + } catch (Exception e) { + Logger.getLogger("org.codelutin.i18n.I18n").warning("could not load locale '" + str + " for reason : " + e.getMessage()); + // use default locale + return DEFAULT_LOCALE; + } + } + + public static Locale newLocale(String language, String country) { + if (language == null) { + // get user locale + language = System.getProperty("user.language", DEFAULT_LOCALE.getLanguage()); + country = System.getProperty("user.country", DEFAULT_LOCALE.getCountry()); + } + return newLocale(language + (country == null ? "" : '_' + country)); + } + + /** + * Test if an url contains the given directory with no recurse seeking. + * + * @param url the url to seek + * @param directory the directory to find + * @return <code>true</code> if directory was found, <code>false</code> otherwise. + * @throws java.io.IOException if any io pb + */ + public static boolean containsDirectDirectory(URL url, String directory) throws IOException { + String fileName = url.getFile(); + // TODO deal with encoding in windows, this is very durty, but it works... + File file = new File(fileName.replaceAll("%20", " ")); + if (!file.exists()) { + return false; + } + if (isJar(fileName) || isZip(fileName)) { + // cas ou le fichier du classLoader est un fichier jar ou zip + if (log.isTraceEnabled()) { + log.trace("zip to search " + file); + } + return new ZipFile(file).getEntry(directory + "/") != null; + } + if (file.isDirectory()) { + // cas ou le ichier du classLoader est un repertoire + if (log.isTraceEnabled()) { + log.trace("directory to search " + file); + } + return new File(file, directory).exists(); + } + + if (log.isWarnEnabled()) { + log.warn("unknown resource type " + url); + } + return false; + } + + /** + * Verifie si le fichier est un fichier jar. + * + * @param name nom du fichier a tester + * @return vrai si le fichier se termine par .jar faux sinon + */ + static public boolean isJar(String name) { + if (name != null && name.length() > 4) { + String ext = name.substring(name.length() - 4, name.length()); + return ".jar".equalsIgnoreCase(ext); + } + return false; + } + + /** + * Verifie si le fichier est un fichier zip + * + * @param name nom du fichier a tester + * @return vrai si le fichier se termine par .zip faux sinon + */ + static public boolean isZip(String name) { + if (name != null && name.length() > 4) { + String ext = name.substring(name.length() - 4, name.length()); + return ".zip".equalsIgnoreCase(ext); + } + return false; + } + + /** + * Retourne la liste des fichiers correspondant au pattern donne, aucun + * ordre ne doit être supposé sur les fichiers. + * + * @param repository repertoire dans lequel on recherche les fichiers + * @param pattern le nom du fichier a extraire du fichier du repertoire doit + * correspondre au pattern (repertoire + nom compris). si le + * pattern est null, tous les fichiers trouvé sont retourné. + * @return la liste des urls correspondant au pattern + */ + static public List<URL> getURLsFromDirectory(File repository, String pattern) { + try { + if (log.isTraceEnabled()) { + log.trace("search '" + pattern + "' in " + repository); + } + + List<URL> urlList = new ArrayList<URL>(); + File[] filesList = repository.listFiles(); + + if (filesList != null) { + + for (File file : filesList) { + + String name = file.getAbsolutePath(); + + if (log.isTraceEnabled()) { + log.trace("directory: " + repository + " name: " + name); + } + + // cas de recursivite : repertoire dans un repertoire + if (file.exists() && file.isDirectory()) { + urlList.addAll(getURLsFromDirectory(file, + pattern)); + // si le fichier du repertoire n'est pas un repertoire + // on verifie s'il correspond au pattern + } else if (pattern == null || name.matches(pattern)) { + URL url = file.toURI().toURL(); + if (log.isTraceEnabled()) { + log.trace("directory: " + repository + " url: " + url); + } + urlList.add(url); + } + } + } + if (log.isTraceEnabled()) { + log.trace("found with pattern '" + pattern + "' : " + urlList); + } + return urlList; + } catch (MalformedURLException eee) { + throw new IllegalArgumentException("Erreur lors de la conversion de l'url " + repository + " (pattern " + pattern + ") " + eee.getMessage(), eee); + //throw new ResourceException("Le fichier n'a pu être converti en URL", eee); + } + } +} Property changes on: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/I18nUtil.java ___________________________________________________________________ Name: svn:mergeinfo + Copied: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/LanguageEnum.java (from rev 1542, nuiton-i18n-api/trunk/src/main/java/org/codelutin/i18n/LanguageEnum.java) =================================================================== --- nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/LanguageEnum.java (rev 0) +++ nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/LanguageEnum.java 2009-05-14 05:41:01 UTC (rev 1550) @@ -0,0 +1,181 @@ +/* + * *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n; + +/** + * Une énumération pour représenter le langue d'une locale définie dans la norme + * <a href="http://www.iso.org/iso/iso_catalogue/catalogue_ics/catalogue_detail_ics.htm?csnumber=22109&ICS1=1&ICS2=140&ICS3=20"><code>ISO 639-1:1998 (ICS n° 01.140.20)</code></a>. + * <p/> + * <a href="http://www.loc.gov/standards/iso639-2/php/French_list.php">la liste des codes</a> + * + * @author chemit + */ +public enum LanguageEnum { + + aa, // Afar + ab, // Abkhazian + af, // Afrikaans + am, // Amharic + ar, // Arabic + as, // Assamese + ay, // Aymara + az, // Azerbaijani + ba, // Bashkir + be, // Byelorussian + bg, // Bulgarian + bh, // Bihari + bi, // Bislama + bn, // Bengali; Bangla + bo, // Tibetan + br, // Breton + ca, // Catalan + co, // Corsican + cs, // Czech + cy, // Welsh + da, // Danish + de, // German + dz, // Bhutani + el, // Greek + en, // English + eo, // Esperanto + es, // Spanish + et, // Estonian + eu, // Basque + fa, // Persian + fi, // Finnish + fj, // Fiji + fo, // Faroese + fr, // French + fy, // Frisian + ga, // Irish + gd, // Scots Gaelic + gl, // Galician + gn, // Guarani + gu, // Gujarati + ha, // Hausa + he, // Hebrew (formerly iw) + hi, // Hindi + hr, // Croatian + hu, // Hungarian + hy, // Armenian + ia, // Interlingua + id, // Indonesian (formerly in) + ie, // Interlingue + ik, // Inupiak + is, // Icelandic + it, // Italian + iu, // Inuktitut + ja, // Japanese + jw, // Javanese + ka, // Georgian + kk, // Kazakh + kl, // Greenlandic + km, // Cambodian + kn, // Kannada + ko, // Korean + ks, // Kashmiri + ku, // Kurdish + ky, // Kirghiz + la, // Latin + ln, // Lingala + lo, // Laothian + lt, // Lithuanian + lv, // Latvian, Lettish + mg, // Malagasy + mi, // Maori + mk, // Macedonian + ml, // Malayalam + mn, // Mongolian + mo, // Moldavian + mr, // Marathi + ms, // Malay + mt, // Maltese + my, // Burmese + na, // Nauru + ne, // Nepali + nl, // Dutch + no, // Norwegian + oc, // Occitan + om, // (Afan) Oromo + or, // Oriya + pa, // Punjabi + pl, // Polish + ps, // Pashto, Pushto + pt, // Portuguese + qu, // Quechua + rm, // Rhaeto-Romance + rn, // Kirundi + ro, // Romanian + ru, // Russian + rw, // Kinyarwanda + sa, // Sanskrit + sd, // Sindhi + sg, // Sangho + sh, // Serbo-Croatian + si, // Sinhalese + sk, // Slovak + sl, // Slovenian + sm, // Samoan + sn, // Shona + so, // Somali + sq, // Albanian + sr, // Serbian + ss, // Siswati + st, // Sesotho + su, // Sundanese + sv, // Swedish + sw, // Swahili + ta, // Tamil + te, // Telugu + tg, // Tajik + th, // Thai + ti, // Tigrinya + tk, // Turkmen + tl, // Tagalog + tn, // Setswana + to, // Tonga + tr, // Turkish + ts, // Tsonga + tt, // Tatar + tw, // Twi + ug, // Uighur + uk, // Ukrainian + ur, // Urdu + uz, // Uzbek + vi, // Vietnamese + vo, // Volapuk + wo, // Wolof + xh, // Xhosa + yi, // Yiddish (formerly ji) + yo, // Yoruba + za, // Zhuang + zh, // Chinese + zu; // Zulu + + public static LanguageEnum valueOf(String language, LanguageEnum defaultValue) { + LanguageEnum languageValue = null; + try { + languageValue = LanguageEnum.valueOf(language.toLowerCase()); + } catch (IllegalArgumentException e) { + System.err.println("Unfound language " + language + ", will use default one " + defaultValue); + } catch (NullPointerException e) { + System.err.println("Unfound language " + language + ", will use default one " + defaultValue); + } + return languageValue == null ? defaultValue : languageValue; + } +} Property changes on: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/LanguageEnum.java ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:mergeinfo + Name: svn:eol-style + native Copied: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundle.java (from rev 1542, nuiton-i18n-api/trunk/src/main/java/org/codelutin/i18n/bundle/I18nBundle.java) =================================================================== --- nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundle.java (rev 0) +++ nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundle.java 2009-05-14 05:41:01 UTC (rev 1550) @@ -0,0 +1,154 @@ +/* +* *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n.bundle; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * Class to represent a i18n Bundle. + * <p/> + * A bundle is defined by a resource prefix (eg /tmp/bundle.properties), and a list of locale implemented entries. + * <p/> + * The property {@link #bundlePrefix} is the equals order property. + * <p/> + * The property {@link #entries} contains all entries defined for this bundle. + * <p/> + * The method {@link #getEntries(java.util.Locale)} filter entries for a given locale, including scope inclusive property. + * <p/> + * The method {@link #getEntries(I18nBundleScope)} filter entries for a givne scope, with no inclusive logi. + * <p/> + * Thoses filter methods return result in the order defines in {@link I18nBundleEntry}, e.g + * <pre> + * XXX.properties + * XXX-fr.properties + * XXX-fr_FR.properties + * </pre> + * In that way, we can load resource in the good order : load before more general scope to more specialized. + * + * @author chemit + * @see I18nBundleScope + * @see I18nBundleEntry + */ +public class I18nBundle { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static final Log log = LogFactory.getLog(I18nBundle.class); + + /** les entrés du bundle */ + protected List<I18nBundleEntry> entries; + + /** le nom du bundle encapsulé (correspond au prefix de l'url de chargement) */ + final String bundlePrefix; + + public I18nBundle(String bundlePrefix) { + this.bundlePrefix = bundlePrefix; + } + + public String getBundlePrefix() { + return bundlePrefix; + } + + /** + * Obtain the entries for a given locale, with a inclusive scope search. + * <p/> + * The order of result respect {@link I18nBundleEntry} order. + * + * @param locale the required locale + * @return the array of entries matching extacly the locale or one of the lesser scope one. + */ + public I18nBundleEntry[] getEntries(Locale locale) { + I18nBundleScope scope = I18nBundleScope.valueOf(locale); + + List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>(); + for (I18nBundleEntry entry : entries) { + I18nBundleScope i18nBundleScope = entry.getScope(); + // load from general to the max scope and always if there is only one bundle entry found + if ((i18nBundleScope == scope || i18nBundleScope.ordinal() < scope.ordinal()) && entry.matchLocale(locale, scope)) { + result.add(entry); + } + } + return result.toArray(new I18nBundleEntry[result.size()]); + } + + /** + * Obtain the entries for a given <code>scope</code> <ith no incluvie logic. + * <p/> + * The order of result respect {@link I18nBundleEntry} order. + * + * @param scope the required scope + * @return the list of entries matching exactly the given scope + */ + public I18nBundleEntry[] getEntries(I18nBundleScope scope) { + List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>(); + for (I18nBundleEntry entry : entries) { + I18nBundleScope i18nBundleScope = entry.getScope(); + // load from general to the max scope and always if there is only one bundle entry found + if (i18nBundleScope == scope) { + result.add(entry); + } + } + return result.toArray(new I18nBundleEntry[result.size()]); + } + + /** @return number of entries in bundle */ + public int size() { + return entries == null ? 0 : entries.size(); + } + + @Override + public String toString() { + String s = super.toString(); + return "<" + s.substring(s.lastIndexOf(".") + 1) + ", bundlePrefix:" + bundlePrefix + ", size:" + size() + ">"; + } + + protected List<I18nBundleEntry> getEntries() { + return entries; + } + + protected boolean matchLocale(Locale locale) { + I18nBundleScope scope = I18nBundleScope.valueOf(locale); + boolean result = false; + if (size() != 0) { + for (I18nBundleEntry entry : entries) { + if (entry.matchLocale(locale, scope)) { + result = true; + break; + } + } + } + return result; + } + + protected boolean addEntry(I18nBundleEntry entry) { + if (entries == null) { + entries = new ArrayList<I18nBundleEntry>(); + } + boolean b = entries.add(entry); + if (log.isDebugEnabled()) { + log.info(this + "\n\t" + entry); + } + return b; + } + + +} Property changes on: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundle.java ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:mergeinfo + Name: svn:eol-style + native Copied: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleEntry.java (from rev 1542, nuiton-i18n-api/trunk/src/main/java/org/codelutin/i18n/bundle/I18nBundleEntry.java) =================================================================== --- nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleEntry.java (rev 0) +++ nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleEntry.java 2009-05-14 05:41:01 UTC (rev 1550) @@ -0,0 +1,192 @@ +/* + * *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n.bundle; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Locale; +import java.util.Map.Entry; +import java.util.Properties; +import org.nuiton.i18n.I18nUtil; +import org.nuiton.i18n.I18nFileReader; + +/** + * A class to represent an entry in a bundle. + * <p/> + * The object matches exactly one resource file in a given scope. + * <p/> + * The object has three properties : + * <ul> + * <li> {@link #path} : the path to resource file where to find transaltion for the entry. + * <li> {@link #locale} : the locale of the entry + * <li> {link #scope} ; the scope of the entry + * </ul> + * This object defines a equals order base on property {@link #path}. + * <p/> + * This object is {@link Comparable}, the order relation is defined like this : + * <ul> + * <li> sort first on {@link #scope}, in the scope order (see {@link I18nBundleScope}), + * <li> if scopes are equals, sort on {@link #locale} string representation. + * </ul> + * + * @author chemit + * @see I18nBundleScope + */ +public class I18nBundleEntry implements Comparable<I18nBundleEntry> { + + /** path to resource file */ + protected URL path; + /** local of the entry, can be null if general scope */ + protected Locale locale; + /** scope of the entry */ + protected I18nBundleScope scope; + + /** + * Constructor if an bundle entry. + * <p/> + * It is defined by a <code>path</code> of the resource file, a scope and a locale. + * + * @param path the path of the resource file fo the bundle entry + * @param locale the given locale of the bundle entry + * @param scope the scope of the given entry + */ + public I18nBundleEntry(URL path, Locale locale, I18nBundleScope scope) { + this.path = path; + this.locale = locale; + this.scope = scope; + } + + public URL getPath() { + return path; + } + + public Locale getLocale() { + return locale; + } + + public I18nBundleScope getScope() { + return scope; + } + + /** + * Method to match or not a bundle entry for a given scope and locale. + * <p/> + * We use the inclusive property of scope, means that we accept all entries on the path + * to the generalest entry for a givne locale. + * + * @param locale the locale to match + * @param scope the highest scope to match + * @return <code>true</code> if the entry match the scope and locale + * * + */ + public boolean matchLocale(Locale locale, I18nBundleScope scope) { + if (this.locale == null) { + // a general bundle entry is always matched! + return true; + } + if (locale == null) { + // can not match a specialized entry with a general scope + return false; + } + // match full locale, or at least a language + return this.locale.equals(locale) || + (this.scope.ordinal() < scope.ordinal() && locale.getLanguage().equals(this.locale.getLanguage())); + } + + /** + * For a given language, load the resource file of this entry into the <code>resource</code> + * properties object. + * + * @param resource the save of resources already loaded + * @throws IOException if any pb while reading resource file + */ + public void load(Properties resource) throws IOException { + InputStream inputStream = null; + StringBuilder sb = new StringBuilder(); + try { + I18nFileReader fileReader = new I18nFileReader(); + inputStream = getPath().openStream(); + //String encoding = language.getEncoding(); + if (I18nBundle.log.isDebugEnabled()) { + sb.append(getPath()).append("\n"); + } + // TC 20081117 always use ISO_8859_1_ENCONDING, since java does it like this. + fileReader.load(inputStream, I18nUtil.ISO_8859_1_ENCONDING); + + if (I18nBundle.log.isDebugEnabled()) { + for (Entry<Object, Object> entry : fileReader.entrySet()) { + sb.append(I18nUtil.ISO_8859_1_ENCONDING).append(" : ").append(entry).append("\n"); + } + } + for (Entry<Object, Object> entry : fileReader.entrySet()) { + String key = (String) entry.getKey(); + String value = (String) entry.getValue(); + if (value.trim().isEmpty()) { + // if there is a previous sentence loaded but not empty + // do not override it + String oldValue = (String) resource.get(key); + if (oldValue != null) { + continue; + } + } + resource.put(key, value); + } + //resource.putAll(fileReader); + if (I18nBundle.log.isDebugEnabled()) { + sb.append("nbSentences : ").append(fileReader.size()).append("\n"); + sb.append("====================================="); + } + fileReader.clear(); + + } finally { + if (I18nBundle.log.isDebugEnabled()) { + I18nBundle.log.debug(sb.toString()); + } + if (inputStream != null) { + inputStream.close(); + } + } + } + + @Override + public int compareTo(I18nBundleEntry o) { + int i = getScope().compareTo(o.getScope()); + if (i == 0) { + // same scope, sort on locale + i = getLocale().toString().compareTo(o.getLocale().toString()); + } + return i; + } + + @Override + public boolean equals(Object o) { + return this == o || o instanceof I18nBundleEntry && path.equals(((I18nBundleEntry) o).path); + } + + @Override + public int hashCode() { + return path.hashCode(); + } + + @Override + public String toString() { + String s = super.toString(); + return "<" + s.substring(s.lastIndexOf(".") + 1) + ", locale:" + locale + ", scope " + scope + ", path:" + path + ">"; + } +} Property changes on: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleEntry.java ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:mergeinfo + Name: svn:eol-style + native Copied: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleFactory.java (from rev 1542, nuiton-i18n-api/trunk/src/main/java/org/codelutin/i18n/bundle/I18nBundleFactory.java) =================================================================== --- nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleFactory.java (rev 0) +++ nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleFactory.java 2009-05-14 05:41:01 UTC (rev 1550) @@ -0,0 +1,548 @@ +package org.nuiton.i18n.bundle; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Properties; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.i18n.I18nUtil; + +/** + * Classe qui est responsable de la detection et construction + * de {@link I18nBundle}. + * + * On retrouve aussi ici des méthodes utiles de parcours de bundles. + * + * @author chemit + * + * @since 1.0.6 + */ +public class I18nBundleFactory { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + private static final Log log = LogFactory.getLog(I18nBundleFactory.class); + /** pattern to find all i18n bundles in classloader class path */ + public static final String SEARCH_BUNDLE_PATTERN = ".*i18n/.+\\.properties"; + public static final String DIRECTORY_SEARCH_BUNDLE_PATTERN = "i18n"; + protected static String UNIQUE_BUNDLE_PATH = "/META-INF/"; + public static String UNIQUE_BUNDLE_DEF = "%1$s-definition.properties"; + public static String UNIQUE_BUNDLE_ENTRY = "%1$s-%2$s.properties"; + public static String BUNDLE_DEF_LOCALES = "locales"; + + /** + * Récuperation de toutes les locales connus par un ensemble de bundles. + * + * @param bundles les bundles a parcourir + * @return la liste des locales rencontrées + */ + public static Locale[] getLocales(I18nBundle... bundles) { + Set<Locale> result = new java.util.HashSet<Locale>(); + for (I18nBundle i18nBundle : bundles) { + for (I18nBundleEntry entry : i18nBundle.getEntries()) { + Locale o = entry.getLocale(); + if (o != null) { + result.add(o); + } + } + } + return result.toArray(new Locale[result.size()]); + } + + /** + * Filtrage des bundles qui correspondante à la locale donnée. + * + * @param l la locale à filtrer + * @param bundles les bundles a parcourir + * @return les bundles qui correspondent à la locale donnée. + */ + public static I18nBundle[] getBundles(Locale l, I18nBundle... bundles) { + List<I18nBundle> result = new ArrayList<I18nBundle>(); + for (I18nBundle i18nBundle : bundles) { + if (i18nBundle.matchLocale(l)) { + result.add(i18nBundle); + } + } + return result.toArray(new I18nBundle[result.size()]); + } + + /** + * Récupération de toutes les entrées de bundles pour les bundles données. + * + * @param bundles les bundles a parcourir + * @return toutes les entrées de bundles. + */ + public static I18nBundleEntry[] getBundleEntries(I18nBundle... bundles) { + List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>(); + for (I18nBundle i18nBundle : bundles) { + List<I18nBundleEntry> list = i18nBundle.getEntries(); + if (!list.isEmpty()) { + result.addAll(list); + } + } + return result.toArray(new I18nBundleEntry[result.size()]); + } + + /** + * Filtrage des entrées de bundles pour une locale donnée. + * + * On essaye de trouver les meilleurs entrées possibles (possibilité de + * promotion). + * + * Note: Cette méthode doit être utilisé pour trouver toutes les entrées à + * charger par le système i18n pour une locale donnée. + * + * @param l la locale à filtrer + * @param defaultLocale la locale à utiliser pour les promotions + * @param bundles les bundles a parcourir + * @return les entrées de bundles filtrés. + */ + public static I18nBundleEntry[] getBundleEntries(Locale l, Locale defaultLocale, I18nBundle... bundles) { + + List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>(); + for (I18nBundle i18nBundle : bundles) { + I18nBundleEntry[] entries = i18nBundle.getEntries(l); + if (entries.length == 0) { + //no entry found for the bundle, try pomotion + entries = promuteBundle(i18nBundle, l, defaultLocale); + } + result.addAll(Arrays.asList(entries)); + } + return result.toArray(new I18nBundleEntry[result.size()]); + } + + /** + * Teste si un ensemble de bundles contient au moins une entrée. + * + * @param bundles les bundles a parcourir + * @return <code>true</code> si aucune entree trouvee, <code>false</code> + * autrement. + */ + public static boolean isEmpty(I18nBundle... bundles) { + for (I18nBundle i18nBundle : bundles) { + if (!i18nBundle.getEntries().isEmpty()) { + // on a trouve au moins une entree + return false; + } + } + return true; + } + + /** + * Recherche la liste des url de toutes les resources i18n, i.e les urls + * des fichiers de traduction en mode uniqueBundleName. + * + * On va d'abord rechercher un fichier /META-INF/unqiueBundleName-definition.properties + * + * Dans ce fichier il y a une entree locales qui contient les locales du bundle + * + * Ensuite pour chaque locale on recupere l'url du fichier : + * + * /META-INF/uniqueBundleName-locale.properties + * + * @param uniqueBundleName le nom de lu'inque bundle a charger + * @return la liste des urls de bundle i18n + */ + public static URL[] getURLs(String uniqueBundleName) { + + String definitionFileName = String.format(UNIQUE_BUNDLE_DEF, uniqueBundleName); + URL[] urls = null; + + try { + URL defURL = I18nBundleFactory.class.getResource(UNIQUE_BUNDLE_PATH + definitionFileName); + log.info("definition i18n file : " + defURL); + InputStream stream = defURL.openStream(); + Properties p = new Properties(); + p.load(stream); + stream.close(); + + String localesAsStr = p.getProperty(BUNDLE_DEF_LOCALES); + Locale[] locales = I18nUtil.parseLocales(localesAsStr); + List<URL> lUrls = new java.util.ArrayList<URL>(1); + String prefixURL = defURL.toString(); + prefixURL = prefixURL.substring(0, prefixURL.length() - definitionFileName.length()); + //FIXME on devrait tester que la resource est disponible ? + + for (Locale l : locales) { + String url = prefixURL + String.format(UNIQUE_BUNDLE_ENTRY, uniqueBundleName, l.toString()); + log.info("detected bundle properties file : " + url); + URL u = new URL(url); +// //FIXME on devrait tester que la resource est disponible ? + + lUrls.add(u); + } + if (!lUrls.isEmpty()) { + urls = lUrls.toArray(new URL[lUrls.size()]); + } else { + // l'unique bundle n'a pas ete trouve! + // on utilise la methode classique de chargement avec recherche + // de tous les bundles i18n + log.warn("not bundle files detected in " + prefixURL); + urls = null; + } + + } catch (Exception ex) { + log.warn("could not load unique bundle " + uniqueBundleName + " for reason " + ex.getMessage(), ex); + urls = null; + + } + return urls; + } + + /** + * Recherche la liste des url de toutes les resources i18n, i.e les urls + * des fichiers de traduction. + * + * @param urls des urls de resources i18n deja calcule, à ajouter au resultat sans traitement particulier + * @return la liste des urls de bundle i18n + */ + public static URL[] getURLs(URL... urls) { + + try { + // on calcule toutes les urls utilisable dans le classloader donnee + List<URL> urlToSeek = new ArrayList<URL>(); + urlToSeek.addAll(Arrays.asList(urls)); + + // on va maintenant supprimer toutes les urls qui ne respectent pas + // le pattern i18n : il faut que la resource contienne un repertoire i18n + // ce simple test permet de restreindre la recherche des resources + // i18n qui est tres couteuse + int size = urlToSeek.size(); + for (Iterator<URL> it = urlToSeek.iterator(); it.hasNext();) { + URL url = it.next(); + if (!I18nUtil.containsDirectDirectory(url, DIRECTORY_SEARCH_BUNDLE_PATTERN)) { + if (log.isDebugEnabled()) { + log.debug("skip url with no " + DIRECTORY_SEARCH_BUNDLE_PATTERN + " directory : " + url); + } + it.remove(); + } + } + + if (log.isDebugEnabled()) { + log.debug("detect " + urlToSeek.size() + " i18n capable url (out of " + size + ")"); + } + + List<URL> listURLs = new java.util.ArrayList<URL>(); + + for (URL url : urlToSeek) { + // on recherche tous les fichiers de traduction pour cet url + + List<URL> result = null; + + if (log.isDebugEnabled()) { + log.debug("seek in : " + url); + } + + String fileName = url.getFile(); + // TODO deal with encoding in windows, this is very durty, but it + // works... + File file = new File(fileName.replaceAll("%20", " ")); + + if (I18nUtil.isJar(fileName)) { + // cas ou le ichier du classLoader est un fichier jar + if (log.isDebugEnabled()) { + log.debug("jar to search " + file); + } + result = getURLsFromJar(url, file); + + } else if (file.isDirectory()) { + // cas ou le ichier du classLoader est un repertoire + if (log.isDebugEnabled()) { + log.debug("directory to search " + file); + } + // on traite le cas ou il peut y avoir des repertoire dans ce + // repertoire + result = getURLsFromDirectory(url, file); + } + if (result != null && !result.isEmpty()) { + listURLs.addAll(result); + } + + } + return listURLs.toArray(new URL[listURLs.size()]); + } catch (Exception eee) { + log.warn("Unable to find urls for urls : " + urls + " for reason " + eee.getMessage(), eee); + return new URL[0]; + } + } + + /** + * Detecte les bundles i18n a partir des urls des fichiers de traduction + * donnes. + * + * Tous les entrées de bundles sont triees dans l'ordre des scopes i18n. + * + * @param urls les urls des fichiers de traductions + * @return la liste des bundle i18n construits à partir des fichiers de + * traduction donnes. + */ + public static List<I18nBundle> detectBundles(URL... urls) { + + List<String> bundleNames = new ArrayList<String>(); + List<I18nBundle> bundles = new ArrayList<I18nBundle>(); + + for (URL url : urls) { + + if (addBundleEntry(url, I18nBundleScope.FULL, bundleNames, bundles)) { + // found a full bundle + continue; + } + if (addBundleEntry(url, I18nBundleScope.LANGUAGE, bundleNames, bundles)) { + // found a language bundle + continue; + } + // must be a general bundle with no locale defined + addBundleEntry(url, I18nBundleScope.GENERAL, bundleNames, bundles); + } + bundleNames.clear(); + + // once for all, sort entries from general to full + for (I18nBundle bundle : bundles) { + java.util.Collections.sort(bundle.getEntries()); + } + + return bundles; + } + + protected static boolean addBundleEntry(URL url, I18nBundleScope scope, List<String> bundleNames, List<I18nBundle> bundles) { + String path = url.toString(); + Matcher matcher = scope.getMatcher(path); + if (!matcher.matches()) { + // no match at this scope + return false; + } + // create a new bundle entry + I18nBundleEntry entry = new I18nBundleEntry(url, scope.getLocale(matcher), scope); + if (log.isDebugEnabled()) { + log.debug("bundle (" + bundles.size() + ") : " + entry); + } + // get the associated bundle + I18nBundle bundle = addBundle(scope.getBundlePrefix(matcher), bundleNames, bundles); + // add entry to bundle + bundle.addEntry(entry); + return true; + } + + protected static I18nBundle addBundle(String bundleName, List<String> bundleNames, List<I18nBundle> bundles) { + I18nBundle bundle; + int index = bundleNames.indexOf(bundleName); + if (index > -1) { + bundle = bundles.get(index); + } else { + bundle = new I18nBundle(bundleName); + if (log.isDebugEnabled()) { + log.debug("bundle (" + bundles.size() + ") : " + bundle); + } + bundles.add(bundle); + bundleNames.add(bundleName); + } + return bundle; + } + + /** + * Obtain some rescue entries for a given locale. + * <p/> + * Note: <b>Calling this method implies there is no entry matched by the common method + * {@link #getBundleEntries(Locale, Locale, I18nBundle[])} return a empty array. + * + * @param bundle the bundle to promute + * @param l the locale required + * @param defaultLocale the default locale to used for promotion + * @return the table of entries promuted for the given locale + */ + protected static I18nBundleEntry[] promuteBundle(I18nBundle bundle, Locale l, Locale defaultLocale) { + + I18nBundleScope scope = I18nBundleScope.valueOf(l); + + if (log.isDebugEnabled()) { + log.debug('[' + bundle.getBundlePrefix() + "] did not find matching entries for locale " + l + ". Try to detect best entries..."); + } + + if (bundle.size() == 0) { + // there is no entry to take... + log.warn("PROMUTE NO ENTRY FOUND"); + return new I18nBundleEntry[0]; + } + + if (bundle.size() == 1) { + // there is one entry take it,what ever... + I18nBundleEntry entry = bundle.getEntries().get(0); + log.warn("PROMUTE" + l + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']'); + return new I18nBundleEntry[]{entry}; + } + + List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>(); + + switch (scope) { + case FULL: + promuteFull(bundle, l, defaultLocale, result); + break; + case LANGUAGE: + promuteLanguage(bundle, l, defaultLocale, result); + break; + case GENERAL: + promuteGeneral(bundle, l, defaultLocale, result); + break; + } + return result.toArray(new I18nBundleEntry[result.size()]); + } + + protected static void promuteFull(I18nBundle bundle, Locale locale, Locale defaultLocale, List<I18nBundleEntry> result) { + if (bundle.size() == 0) { + return; + } + // try with a another FULL matching locale ? + for (I18nBundleEntry entry : bundle.getEntries()) { + I18nBundleScope i18nBundleScope = entry.getScope(); + // load from general to the max scope and always if there is only one bundle entry found + if (i18nBundleScope == I18nBundleScope.FULL && + !entry.getLocale().getCountry().equals(locale.getCountry()) && + entry.getLocale().getLanguage().equals(locale.getLanguage())) { + log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']'); + result.add(entry); + // we take the first one, this is a resuce!!! + break; + } + } + if (result.isEmpty()) { + // full promotion failed,trylanguage promotion + promuteLanguage(bundle, locale, defaultLocale, result); + } + + } + + protected static void promuteLanguage(I18nBundle bundle, Locale locale, Locale defaultLocale, List<I18nBundleEntry> result) { + if (bundle.size() == 0) { + return; + } + for (I18nBundleEntry entry : bundle.getEntries()) { + I18nBundleScope i18nBundleScope = entry.getScope(); + // load from general to the max scope and always if there is only one bundle entry found + if (i18nBundleScope == I18nBundleScope.FULL && entry.getLocale().getLanguage().equals(locale.getLanguage())) { + result.add(entry); + log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']'); + // we take the first one, this is a resuce!!! + break; + } + } + if (result.isEmpty()) { + // language promotion failed,try general promotion + promuteGeneral(bundle, locale, defaultLocale, result); + } + } + + protected static void promuteGeneral(I18nBundle bundle, Locale locale, Locale defaultLocale, List<I18nBundleEntry> result) { + if (bundle.size() == 0) { + return; + } + if (bundle.size() == 1) { + // there is one entry take it,what ever... + I18nBundleEntry entry = bundle.getEntries().get(0); + result.add(entry); + log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']'); + return; + } + I18nBundleScope scope = I18nBundleScope.valueOf(defaultLocale); + for (I18nBundleEntry entry : bundle.getEntries(scope)) { + if (entry.getLocale().equals(defaultLocale)) { + // default locale found + log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']'); + result.add(entry); + return; + } + } + + // default locale not found, take the first one ? + I18nBundleEntry entry = bundle.getEntries().get(0); + result.add(entry); + log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']'); + //TODO Should try to load default en_GB from I18nLoader ? + //I18n.DEFAULT_LOCALE.getCountry() + } + + protected static List<URL> getURLsFromJar(URL incomingURL, File jarfile) { + + String pattern = SEARCH_BUNDLE_PATTERN; + try { + + List<URL> result = new ArrayList<URL>(); + InputStream in = new FileInputStream(jarfile); + ZipInputStream zis = new ZipInputStream(in); + ClassLoader cl = new URLClassLoader(new URL[]{incomingURL}, I18nBundleFactory.class.getClassLoader()); + while (zis.available() != 0) { + ZipEntry entry = zis.getNextEntry(); + + if (entry == null) { + break; + } + + String name = entry.getName(); + + if (pattern == null || name.matches(pattern)) { + // on recupere le fichier correspondant au pattern dans le + // classloader + if (log.isDebugEnabled()) { + log.debug(name + " accepted for pattern " + pattern); + } + URL url = cl.getResource(name); + // on ajoute le fichier correspondant au pattern dans la + // liste + result.add(url); + } + } + + return result; + } catch (Exception eee) { + throw new RuntimeException("n'a pas pu trouve la resource dans le jar " + jarfile.getAbsolutePath(), eee); + } + } + + protected static List<URL> getURLsFromDirectory(URL incomingURL, File repository) { + String pattern = SEARCH_BUNDLE_PATTERN; + try { + if (log.isDebugEnabled()) { + log.debug("search '" + pattern + "' in " + repository); + } + + List<URL> urlList = new ArrayList<URL>(); + File[] filesList = repository.listFiles(); + + if (filesList != null) { + + for (File file : filesList) { + + String name = file.getAbsolutePath(); + + // cas de recursivite : repertoire dans un repertoire + if (file.exists() && file.isDirectory()) { + urlList.addAll(I18nUtil.getURLsFromDirectory(file, + pattern)); + // si le fichier du repertoire n'est pas un repertoire + // on verifie s'il correspond au pattern + } else if (pattern == null || name.matches(pattern)) { + URL url = file.toURI().toURL(); + if (log.isDebugEnabled()) { + log.debug("directory: " + repository + " url: " + url); + } + urlList.add(url); + } + } + } + return urlList; + } catch (MalformedURLException eee) { + throw new RuntimeException("n'a pas pu trouve la resource dans le repertoire " + repository.getAbsolutePath(), eee); + } + } +} Property changes on: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleFactory.java ___________________________________________________________________ Name: svn:mergeinfo + Copied: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleScope.java (from rev 1542, nuiton-i18n-api/trunk/src/main/java/org/codelutin/i18n/bundle/I18nBundleScope.java) =================================================================== --- nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleScope.java (rev 0) +++ nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleScope.java 2009-05-14 05:41:01 UTC (rev 1550) @@ -0,0 +1,144 @@ +/* +* *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n.bundle; + +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.nuiton.i18n.I18nUtil; + +/** + * The enumaration defines the scope of a bundle entry. + * <p/> + * There is three scope possible: + * <ul> + * <li>{@link #GENERAL} : for a bundle entry with no locale specialized information, eg : <code>bundle.properties</code></li> + * <li>{@link #LANGUAGE} : for a bundle entry with language locale specialized information, eg : <code>bundle-en.properties</code></li> + * <li>{@link #FULL} : for a bundle entry with full locale specialized information, eg : <code>bundle-en_GB.properties</code></li> + * </ul> + * <p/> + * We define a order relation, from general to full scope : + * <p/> + * {@link #GENERAL} < {@link #LANGUAGE} < {@link #FULL} + * <p/> + * Scopes are inclusives, in a search of entries, eg the search of <code>en_GB</code> will include <code>en</code> scope... + * <p/> + * The {@link #patternAll} is the searching pattern of bundle of the scope. + * <p/> + * The method {@link #getMatcher(String)} obtain from the {@link #patternAll} the matcher for a bundle path. + * <p/> + * The method {@link #getLocale(Matcher)} obtain from the {@link #patternAll} matched in a bundle path, the + * corresponding locale. + * <p/> + * The class offer also a static method {@link #valueOf(java.util.Locale)} to obtain the scope of a locale. + * + * @author chemit + */ +public enum I18nBundleScope { + + /** default scope (with no language, nor country information) */ +// GENERAL("(.*18n/.+)\\.properties") { + GENERAL("(.*/.+)\\.properties") { + @Override + public Locale getLocale(Matcher matcher) { + // no locale for general bundle + return null; + } + }, + + /** language scope (no country information) */ +// LANGUAGE("(.*18n/.+)-(\\w\\w)\\.properties") { + LANGUAGE("(.*/.+)-(\\w\\w)\\.properties") { + @Override + public Locale getLocale(Matcher matcher) { + Locale result = null; + if (matcher.matches()) { + result = I18nUtil.newLocale(matcher.group(2)); + } + return result; + } + }, + + /** full scope : language + country */ +// FULL("(.*18n/.+)-(\\w\\w_\\w\\w)\\.properties") { + FULL("(.*/.+)-(\\w\\w_\\w\\w)\\.properties") { + @Override + public Locale getLocale(Matcher matcher) { + Locale result = null; + if (matcher.matches()) { + result = I18nUtil.newLocale(matcher.group(2)); + } + return result; + } + }; + + /** pattern used to detect bundle entry */ + private final Pattern patternAll; + + /** + * Obtain the scope of a given <code>locale</code>. + * <p/> + * The given locale can be null, which means {@link I18nBundleScope#GENERAL} scope. + * + * @param locale given locale to convert + * @return the scope of given locale + */ + public static I18nBundleScope valueOf(Locale locale) { + if (locale == null || locale.getLanguage() == null || locale.getLanguage().length() == 0) { + return GENERAL; + } + if (locale.getCountry() == null || locale.getCountry().length() == 0) { + return LANGUAGE; + } + return FULL; + } + + /** + * get a matcher fro the given path for this scope + * + * @param path the path to treate + * @return the bunle detect matcher + */ + public Matcher getMatcher(String path) { + return patternAll.matcher(path); + } + + /** + * get the locale for a given matcher. + * + * @param matcher the scope matcher to use + * @return the locale + */ + public abstract Locale getLocale(Matcher matcher); + + /** + * @param matcher the scope matcher to use + * @return the prefix of the bundle + */ + public String getBundlePrefix(Matcher matcher) { + String result = null; + if (matcher.matches()) { + result = matcher.group(1); + } + return result; + } + + private I18nBundleScope(String patternAll) { + this.patternAll = Pattern.compile(patternAll); + } +} Property changes on: nuiton-i18n-api/trunk/src/main/java/org/nuiton/i18n/bundle/I18nBundleScope.java ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:mergeinfo + Name: svn:eol-style + native Copied: nuiton-i18n-api/trunk/src/main/java/org/nuiton/util/LocaleConverter.java (from rev 1542, nuiton-i18n-api/trunk/src/main/java/org/codelutin/util/LocaleConverter.java) =================================================================== --- nuiton-i18n-api/trunk/src/main/java/org/nuiton/util/LocaleConverter.java (rev 0) +++ nuiton-i18n-api/trunk/src/main/java/org/nuiton/util/LocaleConverter.java 2009-05-14 05:41:01 UTC (rev 1550) @@ -0,0 +1,122 @@ +/* + * *##% Lutin utilities library + * Copyright (C) 2004 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +package org.nuiton.util; + +import org.apache.commons.beanutils.ConversionException; +import org.apache.commons.beanutils.Converter; +import static org.apache.commons.logging.LogFactory.getLog; +import org.nuiton.i18n.CountryEnum; +import org.nuiton.i18n.LanguageEnum; + +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * classe pour convertir une chaine en un objet {@link java.util.Locale}. + * + * @author chemit + */ +public class LocaleConverter implements Converter { + + private static final Pattern FULL_SCOPE_PATTERN = Pattern.compile("([a-zA-Z]{2})_([a-zA-Z]{2})"); + + private static final Pattern MEDIUM_SCOPE_PATTERN = Pattern.compile("([a-zA-Z]{2})"); + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static org.apache.commons.logging.Log log = getLog(LocaleConverter.class); + + public Object convert(Class aClass, Object value) { + if (value == null) { + throw new ConversionException("can not convert null value in " + this + " convertor"); + } + if (isEnabled(aClass)) { + Object result; + if (isEnabled(value.getClass())) { + result = value; + return result; + } + if (value instanceof String) { + result = valueOf(((String) value).trim()); + return result; + } + } + throw new ConversionException("could not find a convertor for type " + aClass.getName() + " and value : " + value); + } + + protected Locale valueOf(String value) { + try { + Locale result = convertFullScope(value); + + if (result == null) { + result = convertMediumScope(value); + } + + if (result == null) { + throw new ConversionException("could not convert locale " + value); + } + + return result; + } catch (Exception e) { + throw new ConversionException("could not convert locale " + value + " for reason " + e.getMessage()); + } + } + + private Locale convertFullScope(String value) { + Matcher m = FULL_SCOPE_PATTERN.matcher(value); + if (m.matches()) { + // found a full scope pattern (language + country) + LanguageEnum language = LanguageEnum.valueOf(m.group(1).toLowerCase()); + CountryEnum country = CountryEnum.valueOf(m.group(2).toUpperCase()); + if (language == null || country == null) { + // not safe + throw new ConversionException("could not convert locale " + value); + } + return new Locale(language.name(), country.name()); + } + return null; + } + + private Locale convertMediumScope(String value) { + Matcher m = MEDIUM_SCOPE_PATTERN.matcher(value); + if (m.matches()) { + // found a medium scope pattern (only language) + LanguageEnum language = LanguageEnum.valueOf(m.group(1).toLowerCase()); + + if (language == null) { + // not safe + throw new ConversionException("could not convert locale " + value); + } + return new Locale(language.name()); + } + return null; + } + + + public LocaleConverter() { + if (log.isDebugEnabled()) { + log.debug(this); + } + } + + protected boolean isEnabled(Class aClass) { + return aClass == Locale.class; + } + +} Property changes on: nuiton-i18n-api/trunk/src/main/java/org/nuiton/util/LocaleConverter.java ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:mergeinfo + Name: svn:eol-style + native