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}