001package net.filebot; 002 003import static java.util.Arrays.*; 004import static java.util.Collections.*; 005import static java.util.Comparator.*; 006import static java.util.stream.Collectors.*; 007import static net.filebot.Logging.*; 008import static net.filebot.util.RegularExpressions.*; 009 010import java.io.Serializable; 011import java.util.Comparator; 012import java.util.List; 013import java.util.Locale; 014import java.util.ResourceBundle; 015import java.util.stream.Stream; 016 017public class Language implements Serializable { 018 019 // ISO 639-1 code 020 private final String iso_639_1; 021 022 // ISO 639-3 code (mostly identical to ISO 639-2/T) 023 private final String iso_639_3; 024 025 // ISO 639-2/B code 026 private final String iso_639_2B; 027 028 // BCP 47 language tag 029 private final String tag; 030 031 // Language name 032 private final String[] names; 033 034 public Language(String iso_639_1, String iso_639_3, String iso_639_2B, String tag, String[] names) { 035 this.iso_639_1 = iso_639_1; 036 this.iso_639_3 = iso_639_3; 037 this.iso_639_2B = iso_639_2B; 038 this.tag = tag; 039 this.names = names.clone(); 040 } 041 042 public String getCode() { 043 return iso_639_1; 044 } 045 046 public String getISO2() { 047 return iso_639_1; 048 } 049 050 public String getISO3() { 051 return iso_639_3; // 3-letter code 052 } 053 054 public String getISO3B() { 055 return iso_639_2B; // alternative 3-letter code 056 } 057 058 public String getTag() { 059 return tag; 060 } 061 062 public String getName() { 063 return names[0]; 064 } 065 066 public List<String> getNames() { 067 return unmodifiableList(asList(names)); 068 } 069 070 @Override 071 public String toString() { 072 return iso_639_3; 073 } 074 075 public Locale getLocale() { 076 return Locale.forLanguageTag(tag); 077 } 078 079 public boolean matches(Language language) { 080 if (language == null) { 081 return false; 082 } 083 return getCode().equals(language.getCode()); 084 } 085 086 public boolean matches(String code) { 087 if (code == null || code.isEmpty()) { 088 return false; 089 } 090 return Stream.of(iso_639_1, iso_639_2B, iso_639_3, tag).anyMatch(code::equalsIgnoreCase) || stream(names).anyMatch(code::equalsIgnoreCase); 091 } 092 093 @Override 094 public Language clone() { 095 return new Language(iso_639_1, iso_639_3, iso_639_2B, tag, names); 096 } 097 098 public static final Comparator<Language> ALPHABETIC_ORDER = comparing(Language::getName, String::compareToIgnoreCase); 099 100 public static Language getLanguage(String code) { 101 if (code == null || code.isEmpty()) { 102 return null; 103 } 104 105 try { 106 String[] values = TAB.split(getProperty(code), 4); 107 return new Language(code, values[0], values[1], values[2], TAB.split(values[3])); 108 } catch (Exception e) { 109 debug.warning(message("Unexpected language code", code)); 110 } 111 112 return new Language(code, code, code, code, new String[] { code }); 113 } 114 115 public static List<Language> getLanguages(String... codes) { 116 return stream(codes).map(Language::getLanguage).collect(toList()); 117 } 118 119 public static Language getLanguage(Locale locale) { 120 return locale == null ? null : findLanguage(locale.getLanguage()); 121 } 122 123 public static Language findLanguage(String name) { 124 return availableLanguages().stream().filter(l -> l.matches(name)).findFirst().orElse(null); 125 } 126 127 public static Language forName(String name) { 128 Language language = Language.findLanguage(name); 129 if (language != null) { 130 return language; 131 } 132 throw new IllegalArgumentException(name + " not in " + availableLanguages()); 133 } 134 135 public static List<Language> availableLanguages() { 136 String languages = getProperty("languages.ui"); 137 return getLanguages(SPACE.split(languages)); 138 } 139 140 public static List<Language> commonLanguages() { 141 String languages = getProperty("languages.common"); 142 return getLanguages(SPACE.split(languages)); 143 } 144 145 public static List<Language> preferredLanguages() { 146 // English | system language | common languages 147 Stream<String> codes = Stream.of(Locale.ENGLISH, Locale.getDefault()).map(Locale::getLanguage); 148 149 // append common languages 150 codes = Stream.concat(codes, SPACE.splitAsStream(getProperty("languages.common"))).distinct(); 151 152 return codes.map(Language::getLanguage).collect(toList()); 153 } 154 155 public static Language defaultLanguage() { 156 return Language.getLanguage("en"); 157 } 158 159 private static String getProperty(String key) { 160 return ResourceBundle.getBundle(Language.class.getName()).getString(key); 161 } 162 163}