12fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa/* 22fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * Copyright (C) 2013 The Android Open Source Project 32fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * 42fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * Licensed under the Apache License, Version 2.0 (the "License"); 52fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * you may not use this file except in compliance with the License. 62fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * You may obtain a copy of the License at 72fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * 82fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * http://www.apache.org/licenses/LICENSE-2.0 92fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * 102fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * Unless required by applicable law or agreed to in writing, software 112fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * distributed under the License is distributed on an "AS IS" BASIS, 122fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * See the License for the specific language governing permissions and 142fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * limitations under the License. 152fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa */ 162fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa 172fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasapackage com.android.inputmethod.keyboard.tools; 182fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa 192fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport java.util.HashMap; 202fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport java.util.Locale; 212fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa 222fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa/** 232fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * A class to help with handling Locales in string form. 242fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * 252fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * This is a subset of com/android/inputmethod/latin/utils/LocaleUtils.java in order to use 262fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * for the make-keyboard-text tool. 272fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa */ 282fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasapublic final class LocaleUtils { 29d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka public static final Locale DEFAULT_LOCALE = Locale.ROOT; 30d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static final String DEFAULT_LOCALE_CODE = "DEFAULT"; 316bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka public static final String NO_LANGUAGE_LOCALE_CODE = "zz"; 326bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka public static final String NO_LANGUAGE_LOCALE_DISPLAY_NAME = "Alphabet"; 33465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka 342fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa private LocaleUtils() { 352fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa // Intentional empty constructor for utility class. 362fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa } 372fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa 38a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka private static final HashMap<String, Locale> sLocaleCache = new HashMap<>(); 392fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa 40d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static final int INDEX_LANGUAGE = 0; 41d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static final int INDEX_SCRIPT = 1; 42d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static final int INDEX_REGION = 2; 43d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static final int ELEMENT_LIMIT = INDEX_REGION + 1; 44d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka 452fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa /** 462fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * Creates a locale from a string specification. 47d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka * 48d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka * Locale string is: language(_script)?(_region)? 49d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka * where: language := [a-zA-Z]{2,3} 50d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka * script := [a-zA-Z]{4} 51d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka * region := [a-zA-Z]{2,3}|[0-9]{3} 522fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa */ 532fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa public static Locale constructLocaleFromString(final String localeStr) { 542fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa if (localeStr == null) { 552fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa return null; 562fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa } 572fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa synchronized (sLocaleCache) { 58d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka if (sLocaleCache.containsKey(localeStr)) { 59d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return sLocaleCache.get(localeStr); 60d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 61d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka boolean hasRegion = false; 62d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka final Locale.Builder builder = new Locale.Builder(); 63d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka final String[] localeElements = localeStr.split("_", ELEMENT_LIMIT); 64d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka if (localeElements.length > INDEX_LANGUAGE) { 65d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka final String text = localeElements[INDEX_LANGUAGE]; 66d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka if (isValidLanguage(text)) { 67d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka builder.setLanguage(text); 68d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } else { 69d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka throw new RuntimeException("Unknown locale format: " + localeStr); 70d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 712fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa } 72d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka if (localeElements.length > INDEX_SCRIPT) { 73d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka final String text = localeElements[INDEX_SCRIPT]; 74d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka if (isValidScript(text)) { 75d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka builder.setScript(text); 76d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } else if (isValidRegion(text)) { 77d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka builder.setRegion(text); 78d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka hasRegion = true; 79d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } else { 80d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka throw new RuntimeException("Unknown locale format: " + localeStr); 81d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa } 83d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka if (localeElements.length > INDEX_REGION) { 84d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka final String text = localeElements[INDEX_REGION]; 85d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka if (!hasRegion && isValidRegion(text)) { 86d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka builder.setRegion(text); 87d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } else { 88d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka throw new RuntimeException("Unknown locale format: " + localeStr); 89d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 90d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 91d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka final Locale locale = builder.build(); 92d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka sLocaleCache.put(localeStr, locale); 93d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return locale; 94d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 95d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 96d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka 97d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static final int MIN_LENGTH_OF_LANGUAGE = 2; 98d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static final int MAX_LENGTH_OF_LANGUAGE = 2; 99d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static final int LENGTH_OF_SCRIPT = 4; 100d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static final int MIN_LENGTH_OF_REGION = 2; 101d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static final int MAX_LENGTH_OF_REGION = 2; 102d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static final int LENGTH_OF_AREA_CODE = 3; 103d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka 104d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static boolean isValidLanguage(final String text) { 105d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return isAlphabetSequence(text, MIN_LENGTH_OF_LANGUAGE, MAX_LENGTH_OF_LANGUAGE); 106d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 107d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka 108d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static boolean isValidScript(final String text) { 109d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return isAlphabetSequence(text, LENGTH_OF_SCRIPT, LENGTH_OF_SCRIPT); 110d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 111d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka 112d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static boolean isValidRegion(final String text) { 113d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return isAlphabetSequence(text, MIN_LENGTH_OF_REGION, MAX_LENGTH_OF_REGION) 114d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka || isDigitSequence(text, LENGTH_OF_AREA_CODE, LENGTH_OF_AREA_CODE); 115d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 116d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka 117d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static boolean isAlphabetSequence(final String text, final int lower, final int upper) { 118d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka final int length = text.length(); 119d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka if (length < lower || length > upper) { 120d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return false; 121d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 122d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka for (int index = 0; index < length; index++) { 123d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka if (!isAsciiAlphabet(text.charAt(index))) { 124d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return false; 1252fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa } 1262fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa } 127d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return true; 1282fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa } 129465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka 130d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static boolean isDigitSequence(final String text, final int lower, final int upper) { 131d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka final int length = text.length(); 132d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka if (length < lower || length > upper) { 133d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return false; 134d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 135d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka for (int index = 0; index < length; ++index) { 136d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka if (!isAsciiDigit(text.charAt(index))) { 137d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return false; 138d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 139d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 140d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return true; 141d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 142d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka 143d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static boolean isAsciiAlphabet(char c) { 144d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); 145d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 146d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka 147d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka private static boolean isAsciiDigit(char c) { 148d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return c >= '0' && c <= '9'; 149d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 150d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka 151d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka public static String getLocaleCode(final Locale locale) { 152d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka if (locale == DEFAULT_LOCALE) { 153d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return DEFAULT_LOCALE_CODE; 154d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 155d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return locale.toString(); 156d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 157d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka 158d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka public static String getLocaleDisplayName(final Locale locale) { 159d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka if (locale == DEFAULT_LOCALE) { 160d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return DEFAULT_LOCALE_CODE; 161d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka } 162d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka if (locale.getLanguage().equals(NO_LANGUAGE_LOCALE_CODE)) { 1636bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka return NO_LANGUAGE_LOCALE_DISPLAY_NAME; 164465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka } 165465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka return locale.getDisplayName(Locale.ENGLISH); 166465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka } 1672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa} 168