mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-05-27 04:07:10 +00:00
Use language tags (#445)
WARNING: due to renames, your existing user history and blacklist files might not be used after this commit. If you build the app with this commit, backup and restore settings ot fix it. Use language tags for identifying a string locale, not Locale.toString. This allows to avoid issues with non-default scripts, e.g. we can now use `sr-Latn` instead of the `sr_ZZ` workaround. Existing files are not renamed, but rename will happen when restoring backups. Most of the occurrences of a locale string have been replaced with Locale where possible. One notable exception is in user dictionary settings, where the locale string must be used to retrieve contents from system personal dictionary. Internal script IDs are switched to string as used in language tags, e.g. Latn for latin. This allows for correct interpretation of a Locale with explicitly specified script.
This commit is contained in:
parent
93dfecfe9e
commit
ac7fb752df
150 changed files with 1446 additions and 1909 deletions
|
@ -0,0 +1,12 @@
|
|||
package org.dslul.openboard.inputmethod.compat
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
import java.util.Locale
|
||||
|
||||
fun Configuration.locale(): Locale =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
locales[0]
|
||||
} else {
|
||||
@Suppress("Deprecation") locale
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* modified
|
||||
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.dslul.openboard.inputmethod.compat
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Build.VERSION_CODES
|
||||
import android.view.inputmethod.InputMethodSubtype
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils
|
||||
import org.dslul.openboard.inputmethod.latin.utils.locale
|
||||
import java.util.*
|
||||
|
||||
object InputMethodSubtypeCompatUtils {
|
||||
@JvmStatic
|
||||
fun getLocaleObject(subtype: InputMethodSubtype): Locale { // Locale.forLanguageTag() is available only in Android L and later.
|
||||
if (Build.VERSION.SDK_INT >= VERSION_CODES.N) {
|
||||
val languageTag = subtype.languageTag
|
||||
if (languageTag.isNotEmpty())
|
||||
return Locale.forLanguageTag(languageTag)
|
||||
}
|
||||
return LocaleUtils.constructLocaleFromString(subtype.locale())
|
||||
}
|
||||
}
|
|
@ -98,7 +98,7 @@ public final class KeyboardLayoutSet {
|
|||
boolean mIsSpellChecker;
|
||||
int mKeyboardWidth;
|
||||
int mKeyboardHeight;
|
||||
int mScriptId = ScriptUtils.SCRIPT_LATIN;
|
||||
String mScript = ScriptUtils.SCRIPT_LATIN;
|
||||
// Indicates if the user has enabled the split-layout preference
|
||||
// and the required ProductionFlags are enabled.
|
||||
boolean mIsSplitLayoutEnabled;
|
||||
|
@ -202,8 +202,8 @@ public final class KeyboardLayoutSet {
|
|||
return keyboard;
|
||||
}
|
||||
|
||||
public int getScriptId() {
|
||||
return mParams.mScriptId;
|
||||
public String getScript() {
|
||||
return mParams.mScript;
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
|
@ -298,7 +298,7 @@ public final class KeyboardLayoutSet {
|
|||
public KeyboardLayoutSet build() {
|
||||
if (mParams.mSubtype == null)
|
||||
throw new RuntimeException("KeyboardLayoutSet subtype is not specified");
|
||||
mParams.mScriptId = ScriptUtils.getScriptFromSpellCheckerLocale(mParams.mSubtype.getLocale());
|
||||
mParams.mScript = ScriptUtils.script(mParams.mSubtype.getLocale());
|
||||
// todo: the whole parsing stuff below should be removed, but currently
|
||||
// it simply breaks when it's not available
|
||||
// for emojis, moreKeys and moreSuggestions there are relevant parameters included
|
||||
|
|
|
@ -594,11 +594,11 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
|
|||
}
|
||||
}
|
||||
|
||||
public int getCurrentKeyboardScriptId() {
|
||||
public String getCurrentKeyboardScript() {
|
||||
if (null == mKeyboardLayoutSet) {
|
||||
return ScriptUtils.SCRIPT_UNKNOWN;
|
||||
}
|
||||
return mKeyboardLayoutSet.getScriptId();
|
||||
return mKeyboardLayoutSet.getScript();
|
||||
}
|
||||
|
||||
public void switchToSubtype(InputMethodSubtype subtype) {
|
||||
|
|
|
@ -18,7 +18,6 @@ import android.graphics.Paint;
|
|||
import android.graphics.Paint.Align;
|
||||
import android.graphics.Typeface;
|
||||
import android.util.AttributeSet;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
@ -31,6 +30,7 @@ import androidx.appcompat.view.ContextThemeWrapper;
|
|||
import org.dslul.openboard.inputmethod.accessibility.AccessibilityUtils;
|
||||
import org.dslul.openboard.inputmethod.accessibility.MainKeyboardAccessibilityDelegate;
|
||||
import org.dslul.openboard.inputmethod.annotations.ExternallyReferenced;
|
||||
import org.dslul.openboard.inputmethod.compat.ConfigurationCompatKt;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.DrawingPreviewPlacerView;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.DrawingProxy;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.GestureFloatingTextDrawingPreview;
|
||||
|
@ -55,6 +55,7 @@ import org.dslul.openboard.inputmethod.latin.settings.DebugSettings;
|
|||
import org.dslul.openboard.inputmethod.latin.settings.Settings;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.DeviceProtectedUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.LanguageOnSpacebarUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.Log;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.TypefaceUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -800,7 +801,7 @@ public final class MainKeyboardView extends KeyboardView implements DrawingProxy
|
|||
final List<Locale> secondaryLocalesToUse = withoutDuplicateLanguages(secondaryLocales, subtype.getLocale().getLanguage());
|
||||
if (secondaryLocalesToUse.size() > 0) {
|
||||
StringBuilder sb = new StringBuilder(subtype.getMiddleDisplayName());
|
||||
final Locale displayLocale = getResources().getConfiguration().locale;
|
||||
final Locale displayLocale = ConfigurationCompatKt.locale(getResources().getConfiguration());
|
||||
for (Locale locale : secondaryLocales) {
|
||||
sb.append(" - ");
|
||||
sb.append(locale.getDisplayLanguage(displayLocale));
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.floris.
|
|||
import org.dslul.openboard.inputmethod.latin.common.splitOnFirstSpacesOnly
|
||||
import org.dslul.openboard.inputmethod.latin.common.splitOnWhitespace
|
||||
import org.dslul.openboard.inputmethod.latin.settings.Settings
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeLocaleUtils
|
||||
import java.io.InputStream
|
||||
import java.util.Locale
|
||||
import kotlin.math.round
|
||||
|
@ -244,11 +245,11 @@ private fun createLocaleKeyTexts(context: Context, params: KeyboardParams, moreK
|
|||
|
||||
private fun getStreamForLocale(locale: Locale, context: Context) =
|
||||
try {
|
||||
if (locale.toString() == "zz") context.assets.open("$LANGUAGE_TEXTS_FOLDER/more_more_keys.txt")
|
||||
else context.assets.open("$LANGUAGE_TEXTS_FOLDER/${locale.toString().lowercase()}.txt")
|
||||
if (locale.toLanguageTag() == SubtypeLocaleUtils.NO_LANGUAGE) context.assets.open("$LANGUAGE_TEXTS_FOLDER/more_more_keys.txt")
|
||||
else context.assets.open("$LANGUAGE_TEXTS_FOLDER/${locale.toLanguageTag()}.txt")
|
||||
} catch (_: Exception) {
|
||||
try {
|
||||
context.assets.open("$LANGUAGE_TEXTS_FOLDER/${locale.language.lowercase()}.txt")
|
||||
context.assets.open("$LANGUAGE_TEXTS_FOLDER/${locale.language}.txt")
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
|
|
|
@ -1,318 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
* modified
|
||||
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.dslul.openboard.inputmethod.latin;
|
||||
|
||||
import static org.dslul.openboard.inputmethod.latin.settings.LanguageSettingsFragmentKt.USER_DICTIONARY_SUFFIX;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.Log;
|
||||
|
||||
import org.dslul.openboard.inputmethod.latin.common.FileUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.define.DecoderSpecificConstants;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.DictionaryInfoUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Helper class to get the address of a mmap'able dictionary file.
|
||||
*/
|
||||
final public class BinaryDictionaryGetter {
|
||||
|
||||
/**
|
||||
* Used for Log actions from this class
|
||||
*/
|
||||
private static final String TAG = BinaryDictionaryGetter.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* Used to return empty lists
|
||||
*/
|
||||
private static final File[] EMPTY_FILE_ARRAY = new File[0];
|
||||
|
||||
/**
|
||||
* Name of the common preferences name to know which word list are on and which are off.
|
||||
*/
|
||||
private static final String COMMON_PREFERENCES_NAME = "LatinImeDictPrefs";
|
||||
|
||||
private static final boolean SHOULD_USE_DICT_VERSION =
|
||||
DecoderSpecificConstants.SHOULD_USE_DICT_VERSION;
|
||||
|
||||
// Name of the category for the main dictionary
|
||||
public static final String MAIN_DICTIONARY_CATEGORY = "main";
|
||||
public static final String ID_CATEGORY_SEPARATOR = ":";
|
||||
|
||||
public static final String ASSETS_DICTIONARY_FOLDER = "dicts";
|
||||
|
||||
// The key considered to read the version attribute in a dictionary file.
|
||||
private static final String VERSION_KEY = "version";
|
||||
|
||||
// Prevents this from being instantiated
|
||||
private BinaryDictionaryGetter() {}
|
||||
|
||||
/**
|
||||
* Generates a unique temporary file name in the app cache directory.
|
||||
*/
|
||||
public static String getTempFileName(final String id, final Context context)
|
||||
throws IOException {
|
||||
final String safeId = DictionaryInfoUtils.replaceFileNameDangerousCharacters(id);
|
||||
final File directory = new File(DictionaryInfoUtils.getWordListTempDirectory(context));
|
||||
if (!directory.exists()) {
|
||||
if (!directory.mkdirs()) {
|
||||
Log.e(TAG, "Could not create the temporary directory");
|
||||
}
|
||||
}
|
||||
// If the first argument is less than three chars, createTempFile throws a
|
||||
// RuntimeException. We don't really care about what name we get, so just
|
||||
// put a three-chars prefix makes us safe.
|
||||
return File.createTempFile("xxx" + safeId, null, directory).getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file address from a resource, or null if it cannot be opened.
|
||||
*/
|
||||
public static AssetFileAddress loadFallbackResource(final Context context,
|
||||
final int fallbackResId) {
|
||||
AssetFileDescriptor afd = null;
|
||||
try {
|
||||
afd = context.getResources().openRawResourceFd(fallbackResId);
|
||||
} catch (RuntimeException e) {
|
||||
Log.e(TAG, "Resource not found: " + fallbackResId);
|
||||
return null;
|
||||
}
|
||||
if (afd == null) {
|
||||
Log.e(TAG, "Resource cannot be opened: " + fallbackResId);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return AssetFileAddress.makeFromFileNameAndOffset(
|
||||
context.getApplicationInfo().sourceDir, afd.getStartOffset(), afd.getLength());
|
||||
} finally {
|
||||
try {
|
||||
afd.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DictPackSettings {
|
||||
final SharedPreferences mDictPreferences;
|
||||
public DictPackSettings(final Context context) {
|
||||
mDictPreferences = null == context ? null
|
||||
: context.getSharedPreferences(COMMON_PREFERENCES_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
}
|
||||
public boolean isWordListActive(final String dictId) {
|
||||
if (null == mDictPreferences) {
|
||||
// If we don't have preferences it basically means we can't find the dictionary
|
||||
// pack - either it's not installed, or it's disabled, or there is some strange
|
||||
// bug. Either way, a word list with no settings should be on by default: default
|
||||
// dictionaries in LatinIME are on if there is no settings at all, and if for some
|
||||
// reason some dictionaries have been installed BUT the dictionary pack can't be
|
||||
// found anymore it's safer to actually supply installed dictionaries.
|
||||
return true;
|
||||
}
|
||||
// The default is true here for the same reasons as above. We got the dictionary
|
||||
// pack but if we don't have any settings for it it means the user has never been
|
||||
// to the settings yet. So by default, the main dictionaries should be on.
|
||||
return mDictPreferences.getBoolean(dictId, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class for the {@link #getCachedWordLists} method
|
||||
*/
|
||||
private static final class FileAndMatchLevel {
|
||||
final File mFile;
|
||||
final int mMatchLevel;
|
||||
public FileAndMatchLevel(final File file, final int matchLevel) {
|
||||
mFile = file;
|
||||
mMatchLevel = matchLevel;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of cached files for a specific locale, one for each category.
|
||||
*
|
||||
* This will return exactly one file for each word list category that matches
|
||||
* the passed locale. If several files match the locale for any given category,
|
||||
* this returns the file with the closest match to the locale. For example, if
|
||||
* the passed word list is en_US, and for a category we have an en and an en_US
|
||||
* word list available, we'll return only the en_US one.
|
||||
* Thus, the list will contain as many files as there are categories.
|
||||
*
|
||||
* @param locale the locale to find the dictionary files for, as a string.
|
||||
* @param context the context on which to open the files upon.
|
||||
* @return an array of binary dictionary files, which may be empty but may not be null.
|
||||
*/
|
||||
public static File[] getCachedWordLists(final String locale, final Context context, final boolean weakMatchAcceptable) {
|
||||
final File[] directoryList = DictionaryInfoUtils.getCachedDirectoryList(context);
|
||||
if (null == directoryList) return EMPTY_FILE_ARRAY;
|
||||
Arrays.sort(directoryList);
|
||||
final HashMap<String, FileAndMatchLevel> cacheFiles = new HashMap<>();
|
||||
for (File directory : directoryList) {
|
||||
if (!directory.isDirectory()) continue;
|
||||
final String dirLocale =
|
||||
DictionaryInfoUtils.getWordListIdFromFileName(directory.getName()).toLowerCase(Locale.ENGLISH);
|
||||
final int matchLevel = LocaleUtils.getMatchLevel(dirLocale, locale.toLowerCase(Locale.ENGLISH));
|
||||
if (weakMatchAcceptable ? LocaleUtils.isMatchWeak(matchLevel) : LocaleUtils.isMatch(matchLevel)) {
|
||||
final File[] wordLists = directory.listFiles();
|
||||
if (null != wordLists) {
|
||||
for (File wordList : wordLists) {
|
||||
final String category =
|
||||
DictionaryInfoUtils.getCategoryFromFileName(wordList.getName());
|
||||
final FileAndMatchLevel currentBestMatch = cacheFiles.get(category);
|
||||
if (null == currentBestMatch || currentBestMatch.mMatchLevel <= matchLevel) {
|
||||
// todo: not nice, related to todo in getDictionaryFiles
|
||||
// this is so user-added main dict has priority over internal main dict
|
||||
// actually any user-added dict has priority, but there aren't any other built-in types
|
||||
if (wordList.getName().endsWith(USER_DICTIONARY_SUFFIX) || currentBestMatch == null)
|
||||
cacheFiles.put(category, new FileAndMatchLevel(wordList, matchLevel));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cacheFiles.isEmpty()) return EMPTY_FILE_ARRAY;
|
||||
final File[] result = new File[cacheFiles.size()];
|
||||
int index = 0;
|
||||
for (final FileAndMatchLevel entry : cacheFiles.values()) {
|
||||
result[index++] = entry.mFile;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of file addresses for a given locale, trying relevant methods in order.
|
||||
*
|
||||
* Tries to get binary dictionaries from various sources, in order:
|
||||
* - Uses a content provider to get a public dictionary set, as per the protocol described
|
||||
* in BinaryDictionaryFileDumper.
|
||||
* If that fails:
|
||||
* - Gets a file name from the built-in dictionary for this locale, if any.
|
||||
* If that fails:
|
||||
* - Returns null.
|
||||
* @return The list of addresses of valid dictionary files, or null.
|
||||
*/
|
||||
// todo: the way of using assets and cached lists should be improved, so that the assets file
|
||||
// doesn't need to be in cached dir just for checking whether it's a good match
|
||||
public static ArrayList<AssetFileAddress> getDictionaryFiles(final Locale locale,
|
||||
final Context context, final boolean weakMatchAcceptable) {
|
||||
loadDictionaryFromAssets(locale.toString(), context, weakMatchAcceptable); // will copy dict to cached word lists if not existing
|
||||
final File[] cachedWordLists = getCachedWordLists(locale.toString(), context, weakMatchAcceptable);
|
||||
final String mainDictId = DictionaryInfoUtils.getMainDictId(locale);
|
||||
final DictPackSettings dictPackSettings = new DictPackSettings(context);
|
||||
|
||||
boolean foundMainDict = false;
|
||||
final ArrayList<AssetFileAddress> fileList = new ArrayList<>();
|
||||
// cachedWordLists may not be null, see doc for getCachedDictionaryList
|
||||
for (final File f : cachedWordLists) {
|
||||
final String wordListId = DictionaryInfoUtils.getWordListIdFromFileName(f.getName());
|
||||
final boolean canUse = f.canRead();
|
||||
if (canUse && DictionaryInfoUtils.isMainWordListId(wordListId)) {
|
||||
foundMainDict = true;
|
||||
}
|
||||
if (!dictPackSettings.isWordListActive(wordListId)) continue;
|
||||
if (canUse) {
|
||||
final AssetFileAddress afa = AssetFileAddress.makeFromFileName(f.getPath());
|
||||
if (null != afa) fileList.add(afa);
|
||||
} else {
|
||||
Log.e(TAG, "Found a cached dictionary file for " + locale + " but cannot read or use it");
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundMainDict && dictPackSettings.isWordListActive(mainDictId)) {
|
||||
final File dict = loadDictionaryFromAssets(locale.toString(), context, weakMatchAcceptable);
|
||||
if (dict != null) {
|
||||
final AssetFileAddress fallbackAsset = AssetFileAddress.makeFromFileName(dict.getPath());
|
||||
if (fallbackAsset != null)
|
||||
fileList.add(fallbackAsset);
|
||||
}
|
||||
}
|
||||
|
||||
return fileList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the best matching main dictionary from assets.
|
||||
*
|
||||
* Actually copies the dictionary to cache folder, and then returns that file. This allows
|
||||
* the dictionaries to be stored in a compressed way, reducing APK size.
|
||||
* On next load, the dictionary in cache folder is found by getCachedWordLists
|
||||
*
|
||||
* Returns null on IO errors or if no matching dictionary is found
|
||||
*/
|
||||
public static File loadDictionaryFromAssets(final String locale, final Context context, final boolean weakMatchAcceptable) {
|
||||
final String[] dictionaryList = getAssetsDictionaryList(context);
|
||||
if (null == dictionaryList) return null;
|
||||
String bestMatchName = null;
|
||||
int bestMatchLevel = 0;
|
||||
for (String dictionary : dictionaryList) {
|
||||
final String dictLocale =
|
||||
extractLocaleFromAssetsDictionaryFile(dictionary);
|
||||
if (dictLocale == null) continue;
|
||||
// assets files may contain the locale in lowercase, but dictionary headers usually
|
||||
// have an upper case country code, so we compare lowercase here
|
||||
final int matchLevel = LocaleUtils.getMatchLevel(dictLocale.toLowerCase(Locale.ENGLISH), locale.toLowerCase(Locale.ENGLISH));
|
||||
if ((weakMatchAcceptable ? LocaleUtils.isMatchWeak(matchLevel) : LocaleUtils.isMatch(matchLevel)) && matchLevel > bestMatchLevel) {
|
||||
bestMatchName = dictionary;
|
||||
bestMatchLevel = matchLevel;
|
||||
}
|
||||
}
|
||||
if (bestMatchName == null) return null;
|
||||
|
||||
// we have a match, now copy contents of the dictionary to cached word lists folder
|
||||
final String bestMatchLocale = extractLocaleFromAssetsDictionaryFile(bestMatchName);
|
||||
if (bestMatchLocale == null) return null;
|
||||
File dictFile = DictionaryInfoUtils.getMainDictFile(locale, context);
|
||||
if (dictFile.exists())
|
||||
return dictFile;
|
||||
try {
|
||||
FileUtils.copyStreamToNewFile(
|
||||
context.getAssets().open(ASSETS_DICTIONARY_FOLDER + File.separator + bestMatchName),
|
||||
dictFile);
|
||||
return dictFile;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "exception while looking for locale " + locale, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the locale for a dictionary file name stored in assets.
|
||||
*
|
||||
* Assumes file name main_[locale].dict
|
||||
*
|
||||
* Returns the locale, or null if file name does not match the pattern
|
||||
*/
|
||||
public static String extractLocaleFromAssetsDictionaryFile(final String dictionaryFileName) {
|
||||
if (dictionaryFileName.startsWith(DictionaryInfoUtils.MAIN_DICT_PREFIX)
|
||||
&& dictionaryFileName.endsWith(".dict")) {
|
||||
return dictionaryFileName.substring(
|
||||
DictionaryInfoUtils.MAIN_DICT_PREFIX.length(),
|
||||
dictionaryFileName.lastIndexOf('.')
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] getAssetsDictionaryList(final Context context) {
|
||||
final String[] dictionaryList;
|
||||
try {
|
||||
dictionaryList = context.getAssets().list(ASSETS_DICTIONARY_FOLDER);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
return dictionaryList;
|
||||
}
|
||||
}
|
|
@ -424,7 +424,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
|||
|
||||
// load blacklist
|
||||
if (noExistingDictsForThisLocale) {
|
||||
newDictGroup.blacklistFileName = context.getFilesDir().getAbsolutePath() + File.separator + "blacklists" + File.separator + locale.toString().toLowerCase(Locale.ENGLISH) + ".txt";
|
||||
newDictGroup.blacklistFileName = context.getFilesDir().getAbsolutePath() + File.separator + "blacklists" + File.separator + locale.toLanguageTag() + ".txt";
|
||||
if (!new File(newDictGroup.blacklistFileName).exists())
|
||||
new File(context.getFilesDir().getAbsolutePath() + File.separator + "blacklists").mkdirs();
|
||||
newDictGroup.blacklist.addAll(readBlacklistFile(newDictGroup.blacklistFileName));
|
||||
|
@ -494,7 +494,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
|||
mainDicts[i] = null;
|
||||
continue;
|
||||
}
|
||||
mainDicts[i] = DictionaryFactory.createMainDictionaryFromManager(context, dictionaryGroup.mLocale);
|
||||
mainDicts[i] = DictionaryFactoryKt.createMainDictionary(context, dictionaryGroup.mLocale);
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
* modified
|
||||
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.dslul.openboard.inputmethod.latin;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.dslul.openboard.inputmethod.latin.makedict.DictionaryHeader;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.DictionaryInfoUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Factory for dictionary instances.
|
||||
*/
|
||||
public final class DictionaryFactory {
|
||||
|
||||
/**
|
||||
* Initializes a main dictionary collection from a dictionary pack, with explicit flags.
|
||||
* <p>
|
||||
* This searches for a content provider providing a dictionary pack for the specified
|
||||
* locale. If none is found, it falls back to the built-in dictionary - if any.
|
||||
* @param context application context for reading resources
|
||||
* @param locale the locale for which to create the dictionary
|
||||
* @return an initialized instance of DictionaryCollection
|
||||
*/
|
||||
public static DictionaryCollection createMainDictionaryFromManager(final Context context, @NonNull final Locale locale) {
|
||||
final LinkedList<Dictionary> dictList = new LinkedList<>();
|
||||
ArrayList<AssetFileAddress> assetFileList =
|
||||
BinaryDictionaryGetter.getDictionaryFiles(locale, context, false);
|
||||
|
||||
boolean mainFound = false;
|
||||
for (AssetFileAddress fileAddress : assetFileList) {
|
||||
if (fileAddress.mFilename.contains("main")) {
|
||||
mainFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mainFound) // try again and allow weaker match
|
||||
assetFileList = BinaryDictionaryGetter.getDictionaryFiles(locale, context, true);
|
||||
|
||||
for (final AssetFileAddress f : assetFileList) {
|
||||
final DictionaryHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(new File(f.mFilename), f.mOffset, f.mLength);
|
||||
String dictType = Dictionary.TYPE_MAIN;
|
||||
if (header != null) {
|
||||
// make sure the suggested words dictionary has the correct type
|
||||
dictType = header.mIdString.split(":")[0];
|
||||
}
|
||||
final ReadOnlyBinaryDictionary readOnlyBinaryDictionary =
|
||||
new ReadOnlyBinaryDictionary(f.mFilename, f.mOffset, f.mLength,
|
||||
false /* useFullEditDistance */, locale, dictType);
|
||||
if (readOnlyBinaryDictionary.isValidDictionary()) {
|
||||
if(locale.getLanguage().equals("ko")) {
|
||||
// Use KoreanDictionary for Korean locale
|
||||
dictList.add(new KoreanDictionary(readOnlyBinaryDictionary));
|
||||
} else {
|
||||
dictList.add(readOnlyBinaryDictionary);
|
||||
}
|
||||
} else {
|
||||
readOnlyBinaryDictionary.close();
|
||||
// Prevent this dictionary to do any further harm.
|
||||
killDictionary(context, f);
|
||||
}
|
||||
}
|
||||
|
||||
// If the list is empty, that means we should not use any dictionary (for example, the user
|
||||
// explicitly disabled the main dictionary), so the following is okay. dictList is never
|
||||
// null, but if for some reason it is, DictionaryCollection handles it gracefully.
|
||||
return new DictionaryCollection(Dictionary.TYPE_MAIN, locale, dictList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kills a dictionary so that it is never used again, if possible.
|
||||
* @param context The context to contact the dictionary provider, if possible.
|
||||
* @param f A file address to the dictionary to kill.
|
||||
*/
|
||||
public static void killDictionary(final Context context, final AssetFileAddress f) {
|
||||
if (f.pointsToPhysicalFile()) {
|
||||
f.deleteUnderlyingFile();
|
||||
// notify the user if possible (toast not showing up on Android 13+ when not in settings)
|
||||
// but not that important, as the not working dictionary should be obvious
|
||||
final String wordlistId = DictionaryInfoUtils.getWordListIdFromFileName(new File(f.mFilename).getName());
|
||||
new Handler(Looper.getMainLooper()).post(() ->
|
||||
Toast.makeText(context, "dictionary "+wordlistId+" is invalid, deleting", Toast.LENGTH_LONG).show()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
* modified
|
||||
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||
*/
|
||||
package org.dslul.openboard.inputmethod.latin
|
||||
|
||||
import android.content.Context
|
||||
import org.dslul.openboard.inputmethod.latin.common.FileUtils
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils.constructLocale
|
||||
import org.dslul.openboard.inputmethod.latin.settings.USER_DICTIONARY_SUFFIX
|
||||
import org.dslul.openboard.inputmethod.latin.utils.DictionaryInfoUtils
|
||||
import org.dslul.openboard.inputmethod.latin.utils.Log
|
||||
import java.io.File
|
||||
import java.util.LinkedList
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Initializes a main dictionary collection from a dictionary pack, with explicit flags.
|
||||
*
|
||||
*
|
||||
* This searches for a content provider providing a dictionary pack for the specified
|
||||
* locale. If none is found, it falls back to the built-in dictionary - if any.
|
||||
* @param context application context for reading resources
|
||||
* @param locale the locale for which to create the dictionary
|
||||
* @return an initialized instance of DictionaryCollection
|
||||
*/
|
||||
fun createMainDictionary(context: Context, locale: Locale): DictionaryCollection {
|
||||
val cacheDir = DictionaryInfoUtils.getCacheDirectoryForLocale(locale, context)
|
||||
val dictList = LinkedList<Dictionary>()
|
||||
// get cached dict files
|
||||
val (userDicts, extractedDicts) = DictionaryInfoUtils.getCachedDictsForLocale(locale, context)
|
||||
.partition { it.name.endsWith(USER_DICTIONARY_SUFFIX) }
|
||||
// add user dicts to list
|
||||
userDicts.forEach { checkAndAddDictionaryToListIfNotExisting(it, dictList, locale) }
|
||||
// add extracted dicts to list (after userDicts, to skip extracted dicts of same type)
|
||||
extractedDicts.forEach { checkAndAddDictionaryToListIfNotExisting(it, dictList, locale) }
|
||||
if (dictList.any { it.mDictType == Dictionary.TYPE_MAIN })
|
||||
return DictionaryCollection(Dictionary.TYPE_MAIN, locale, dictList)
|
||||
|
||||
// no main dict found -> check assets
|
||||
val assetsDicts = DictionaryInfoUtils.getAssetsDictionaryList(context)
|
||||
// file name is <type>_<language tag>.dict
|
||||
val dictsByType = assetsDicts?.groupBy { it.substringBefore("_") }
|
||||
// for each type find the best match
|
||||
dictsByType?.forEach { (dictType, dicts) ->
|
||||
val bestMatch = LocaleUtils.getBestMatch(locale, dicts) { it.substringAfter("_")
|
||||
.substringBefore(".").constructLocale() } ?: return@forEach
|
||||
// extract dict and add extracted file
|
||||
val targetFile = File(cacheDir, "$dictType.dict")
|
||||
FileUtils.copyStreamToNewFile(
|
||||
context.assets.open(DictionaryInfoUtils.ASSETS_DICTIONARY_FOLDER + File.separator + bestMatch),
|
||||
targetFile
|
||||
)
|
||||
checkAndAddDictionaryToListIfNotExisting(targetFile, dictList, locale)
|
||||
}
|
||||
// If the list is empty, that means we should not use any dictionary (for example, the user
|
||||
// explicitly disabled the main dictionary), so the following is okay. dictList is never
|
||||
// null, but if for some reason it is, DictionaryCollection handles it gracefully.
|
||||
return DictionaryCollection(Dictionary.TYPE_MAIN, locale, dictList)
|
||||
}
|
||||
|
||||
/**
|
||||
* add dictionary created from [file] to [dicts]
|
||||
* if [file] cannot be loaded it is deleted
|
||||
* if the dictionary type already exists in [dicts], the [file] is skipped
|
||||
*/
|
||||
private fun checkAndAddDictionaryToListIfNotExisting(file: File, dicts: MutableList<Dictionary>, locale: Locale) {
|
||||
if (!file.isFile) return
|
||||
val header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(file) ?: return killDictionary(file)
|
||||
val dictType = header.mIdString.split(":").first()
|
||||
if (dicts.any { it.mDictType == dictType }) return
|
||||
val readOnlyBinaryDictionary = ReadOnlyBinaryDictionary(
|
||||
file.absolutePath, 0, file.length(), false, locale, dictType
|
||||
)
|
||||
|
||||
if (readOnlyBinaryDictionary.isValidDictionary) {
|
||||
if (locale.language == "ko") {
|
||||
// Use KoreanDictionary for Korean locale
|
||||
dicts.add(KoreanDictionary(readOnlyBinaryDictionary))
|
||||
} else {
|
||||
dicts.add(readOnlyBinaryDictionary)
|
||||
}
|
||||
} else {
|
||||
readOnlyBinaryDictionary.close()
|
||||
killDictionary(file)
|
||||
}
|
||||
}
|
||||
|
||||
private fun killDictionary(file: File) {
|
||||
Log.e("DictionaryFactory", "could not load dictionary ${file.parentFile?.name}/${file.name}, deleting")
|
||||
file.delete()
|
||||
}
|
|
@ -14,7 +14,6 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import com.android.inputmethod.latin.BinaryDictionary;
|
||||
|
||||
import org.dslul.openboard.inputmethod.annotations.UsedForTesting;
|
||||
import org.dslul.openboard.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
|
||||
import org.dslul.openboard.inputmethod.latin.common.ComposedData;
|
||||
import org.dslul.openboard.inputmethod.latin.common.FileUtils;
|
||||
|
@ -27,14 +26,12 @@ import org.dslul.openboard.inputmethod.latin.settings.SettingsValuesForSuggestio
|
|||
import org.dslul.openboard.inputmethod.latin.utils.AsyncResultHolder;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.CombinedFormatUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ExecutorUtils;
|
||||
import com.android.inputmethod.latin.utils.WordInputEventForPersonalization;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
@ -44,7 +41,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|||
* Abstract base class for an expandable dictionary that can be created and updated dynamically
|
||||
* during runtime. When updated it automatically generates a new binary dictionary to handle future
|
||||
* queries in native code. This binary dictionary is written to internal storage.
|
||||
*
|
||||
* <p>
|
||||
* A class that extends this abstract class must have a static factory method named
|
||||
* getDictionary(Context context, Locale locale, File dictFile, String dictNamePrefix)
|
||||
*/
|
||||
|
@ -96,8 +93,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
|
|||
|
||||
private final ReentrantReadWriteLock mLock;
|
||||
|
||||
private Map<String, String> mAdditionalAttributeMap = null;
|
||||
|
||||
/* A extension for a binary dictionary file. */
|
||||
protected static final String DICT_FILE_EXTENSION = ".dict";
|
||||
|
||||
|
@ -151,7 +146,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
|
|||
|
||||
public static String getDictName(final String name, final Locale locale,
|
||||
final File dictFile) {
|
||||
return dictFile != null ? dictFile.getName() : name + "." + locale.toString();
|
||||
return dictFile != null ? dictFile.getName() : name + "." + locale.toLanguageTag();
|
||||
}
|
||||
|
||||
private void asyncExecuteTaskWithWriteLock(final Runnable task) {
|
||||
|
@ -191,9 +186,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
|
|||
|
||||
protected Map<String, String> getHeaderAttributeMap() {
|
||||
HashMap<String, String> attributeMap = new HashMap<>();
|
||||
if (mAdditionalAttributeMap != null) {
|
||||
attributeMap.putAll(mAdditionalAttributeMap);
|
||||
}
|
||||
attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, mDictName);
|
||||
attributeMap.put(DictionaryHeader.DICTIONARY_LOCALE_KEY, mLocale.toString());
|
||||
attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY,
|
||||
|
@ -343,41 +335,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by Sketch.
|
||||
* {@see https://cs.corp.google.com/#android/vendor/unbundled_google/packages/LatinIMEGoogle/tools/sketch/ime-simulator/src/com/android/inputmethod/sketch/imesimulator/ImeSimulator.java&q=updateEntriesForInputEventsCallback&l=286}
|
||||
*/
|
||||
@UsedForTesting
|
||||
public interface UpdateEntriesForInputEventsCallback {
|
||||
void onFinished();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically update entries according to input events.
|
||||
*
|
||||
* Used by Sketch.
|
||||
* {@see https://cs.corp.google.com/#android/vendor/unbundled_google/packages/LatinIMEGoogle/tools/sketch/ime-simulator/src/com/android/inputmethod/sketch/imesimulator/ImeSimulator.java&q=updateEntriesForInputEventsCallback&l=286}
|
||||
*/
|
||||
@UsedForTesting
|
||||
public void updateEntriesForInputEvents(
|
||||
@NonNull final ArrayList<WordInputEventForPersonalization> inputEvents,
|
||||
final UpdateEntriesForInputEventsCallback callback) {
|
||||
reloadDictionaryIfRequired();
|
||||
asyncExecuteTaskWithWriteLock(() -> {
|
||||
try {
|
||||
final BinaryDictionary binaryDictionary = getBinaryDictionary();
|
||||
if (binaryDictionary == null) {
|
||||
return;
|
||||
}
|
||||
binaryDictionary.updateEntriesForInputEvents(inputEvents.toArray(new WordInputEventForPersonalization[0]));
|
||||
} finally {
|
||||
if (callback != null) {
|
||||
callback.onFinished();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData,
|
||||
final NgramContext ngramContext, final long proximityInfoHandle,
|
||||
|
@ -608,24 +565,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
|
|||
return result.get(null /* defaultValue */, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS);
|
||||
}
|
||||
|
||||
@UsedForTesting
|
||||
public void waitAllTasksForTests() {
|
||||
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
asyncExecuteTaskWithWriteLock(countDownLatch::countDown);
|
||||
try {
|
||||
countDownLatch.await();
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(TAG, "Interrupted while waiting for finishing dictionary operations.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@UsedForTesting
|
||||
public void clearAndFlushDictionaryWithAdditionalAttributes(
|
||||
final Map<String, String> attributeMap) {
|
||||
mAdditionalAttributeMap = attributeMap;
|
||||
clear();
|
||||
}
|
||||
|
||||
public void dumpAllWordsForDebug() {
|
||||
reloadDictionaryIfRequired();
|
||||
final String tag = TAG;
|
||||
|
|
|
@ -41,6 +41,7 @@ import android.view.inputmethod.InputMethodSubtype;
|
|||
|
||||
import org.dslul.openboard.inputmethod.accessibility.AccessibilityUtils;
|
||||
import org.dslul.openboard.inputmethod.annotations.UsedForTesting;
|
||||
import org.dslul.openboard.inputmethod.compat.ConfigurationCompatKt;
|
||||
import org.dslul.openboard.inputmethod.compat.EditorInfoCompatUtils;
|
||||
import org.dslul.openboard.inputmethod.compat.InsetsOutlineProvider;
|
||||
import org.dslul.openboard.inputmethod.compat.ViewOutlineProviderCompatUtils;
|
||||
|
@ -281,7 +282,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
case MSG_RESUME_SUGGESTIONS:
|
||||
latinIme.mInputLogic.restartSuggestionsOnWordTouchedByCursor(
|
||||
latinIme.mSettings.getCurrent(),
|
||||
latinIme.mKeyboardSwitcher.getCurrentKeyboardScriptId());
|
||||
latinIme.mKeyboardSwitcher.getCurrentKeyboardScript());
|
||||
break;
|
||||
case MSG_REOPEN_DICTIONARIES:
|
||||
// We need to re-evaluate the currently composing word in case the script has
|
||||
|
@ -698,7 +699,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
// case, we are about to go down but we still don't know it, however the system tells
|
||||
// us there is no current subtype.
|
||||
Log.e(TAG, "System is reporting no current subtype.");
|
||||
subtypeLocale = getResources().getConfiguration().locale;
|
||||
subtypeLocale = ConfigurationCompatKt.locale(getResources().getConfiguration());
|
||||
} else {
|
||||
subtypeLocale = subtypeSwitcherLocale;
|
||||
}
|
||||
|
@ -1467,7 +1468,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
mInputLogic.finishInput();
|
||||
int newPosition = mInputLogic.mConnection.mExpectedSelStart + moveSteps;
|
||||
mInputLogic.mConnection.setSelection(newPosition, newPosition);
|
||||
mInputLogic.restartSuggestionsOnWordTouchedByCursor(mSettings.getCurrent(), mKeyboardSwitcher.getCurrentKeyboardScriptId());
|
||||
mInputLogic.restartSuggestionsOnWordTouchedByCursor(mSettings.getCurrent(), mKeyboardSwitcher.getCurrentKeyboardScript());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1606,7 +1607,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
final InputTransaction completeInputTransaction =
|
||||
mInputLogic.onCodeInput(mSettings.getCurrent(), event,
|
||||
mKeyboardSwitcher.getKeyboardShiftMode(),
|
||||
mKeyboardSwitcher.getCurrentKeyboardScriptId(), mHandler);
|
||||
mKeyboardSwitcher.getCurrentKeyboardScript(), mHandler);
|
||||
updateStateAfterInputTransaction(completeInputTransaction);
|
||||
mKeyboardSwitcher.onEvent(event, getCurrentAutoCapsState(), getCurrentRecapitalizeState());
|
||||
}
|
||||
|
@ -1763,7 +1764,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
final InputTransaction completeInputTransaction = mInputLogic.onPickSuggestionManually(
|
||||
mSettings.getCurrent(), suggestionInfo,
|
||||
mKeyboardSwitcher.getKeyboardShiftMode(),
|
||||
mKeyboardSwitcher.getCurrentKeyboardScriptId(),
|
||||
mKeyboardSwitcher.getCurrentKeyboardScript(),
|
||||
mHandler);
|
||||
updateStateAfterInputTransaction(completeInputTransaction);
|
||||
}
|
||||
|
@ -1915,7 +1916,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
mInputLogic.onCodeInput(mSettings.getCurrent(), event,
|
||||
mKeyboardSwitcher.getKeyboardShiftMode(),
|
||||
// TODO: this is not necessarily correct for a hardware keyboard right now
|
||||
mKeyboardSwitcher.getCurrentKeyboardScriptId(),
|
||||
mKeyboardSwitcher.getCurrentKeyboardScript(),
|
||||
mHandler);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -645,10 +645,10 @@ public final class RichInputConnection implements PrivateCommandPerformer {
|
|||
mIC.performContextMenuAction(android.R.id.selectAll);
|
||||
}
|
||||
|
||||
public void selectWord(final SpacingAndPunctuations spacingAndPunctuations, final int scriptId) {
|
||||
public void selectWord(final SpacingAndPunctuations spacingAndPunctuations, final String script) {
|
||||
if (!isConnected()) return;
|
||||
if (mExpectedSelStart != mExpectedSelEnd) return; // already something selected
|
||||
final TextRange range = getWordRangeAtCursor(spacingAndPunctuations, scriptId, false);
|
||||
final TextRange range = getWordRangeAtCursor(spacingAndPunctuations, script, false);
|
||||
if (range == null) return;
|
||||
mIC.setSelection(mExpectedSelStart - range.getNumberOfCharsInWordBeforeCursor(), mExpectedSelStart + range.getNumberOfCharsInWordAfterCursor());
|
||||
}
|
||||
|
@ -726,23 +726,23 @@ public final class RichInputConnection implements PrivateCommandPerformer {
|
|||
}
|
||||
|
||||
private static boolean isPartOfCompositionForScript(final int codePoint,
|
||||
final SpacingAndPunctuations spacingAndPunctuations, final int scriptId) {
|
||||
final SpacingAndPunctuations spacingAndPunctuations, final String script) {
|
||||
// We always consider word connectors part of compositions.
|
||||
return spacingAndPunctuations.isWordConnector(codePoint)
|
||||
// Otherwise, it's part of composition if it's part of script and not a separator.
|
||||
|| (!spacingAndPunctuations.isWordSeparator(codePoint)
|
||||
&& ScriptUtils.isLetterPartOfScript(codePoint, scriptId));
|
||||
&& ScriptUtils.isLetterPartOfScript(codePoint, script));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text surrounding the cursor.
|
||||
*
|
||||
* @param spacingAndPunctuations the rules for spacing and punctuation
|
||||
* @param scriptId the script we consider to be writing words, as one of ScriptUtils.SCRIPT_*
|
||||
* @param script the script we consider to be writing words, as one of ScriptUtils.SCRIPT_*
|
||||
* @return a range containing the text surrounding the cursor
|
||||
*/
|
||||
public TextRange getWordRangeAtCursor(final SpacingAndPunctuations spacingAndPunctuations,
|
||||
final int scriptId, final boolean justDeleted) {
|
||||
final String script, final boolean justDeleted) {
|
||||
mIC = mParent.getCurrentInputConnection();
|
||||
if (!isConnected()) {
|
||||
return null;
|
||||
|
@ -764,7 +764,7 @@ public final class RichInputConnection implements PrivateCommandPerformer {
|
|||
// we need text before, and text after is either empty or a separator or similar
|
||||
if (justDeleted && before.length() > 0 &&
|
||||
(after.length() == 0
|
||||
|| !isPartOfCompositionForScript(Character.codePointAt(after, 0), spacingAndPunctuations, scriptId)
|
||||
|| !isPartOfCompositionForScript(Character.codePointAt(after, 0), spacingAndPunctuations, script)
|
||||
)
|
||||
) {
|
||||
// issue:
|
||||
|
@ -786,7 +786,7 @@ public final class RichInputConnection implements PrivateCommandPerformer {
|
|||
int endIndexInAfter = -1;
|
||||
while (startIndexInBefore > 0) {
|
||||
final int codePoint = Character.codePointBefore(before, startIndexInBefore);
|
||||
if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, scriptId)) {
|
||||
if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, script)) {
|
||||
if (Character.isWhitespace(codePoint) || !spacingAndPunctuations.mCurrentLanguageHasSpaces)
|
||||
break;
|
||||
// continue to the next whitespace and see whether this contains a sometimesWordConnector
|
||||
|
@ -815,7 +815,7 @@ public final class RichInputConnection implements PrivateCommandPerformer {
|
|||
if (endIndexInAfter == -1) {
|
||||
while (++endIndexInAfter < after.length()) {
|
||||
final int codePoint = Character.codePointAt(after, endIndexInAfter);
|
||||
if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, scriptId)) {
|
||||
if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, script)) {
|
||||
if (Character.isWhitespace(codePoint) || !spacingAndPunctuations.mCurrentLanguageHasSpaces)
|
||||
break;
|
||||
// continue to the next whitespace and see whether this contains a sometimesWordConnector
|
||||
|
|
|
@ -11,19 +11,20 @@ import android.content.SharedPreferences;
|
|||
import android.inputmethodservice.InputMethodService;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.IBinder;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.Log;
|
||||
import android.view.inputmethod.InputMethodInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.inputmethod.InputMethodSubtype;
|
||||
|
||||
import org.dslul.openboard.inputmethod.annotations.UsedForTesting;
|
||||
import org.dslul.openboard.inputmethod.compat.InputMethodSubtypeCompatUtils;
|
||||
import org.dslul.openboard.inputmethod.compat.ConfigurationCompatKt;
|
||||
import org.dslul.openboard.inputmethod.latin.settings.Settings;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.DeviceProtectedUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.LanguageOnSpacebarUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.Log;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeLocaleUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeSettingsKt;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeUtilsKt;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -293,14 +294,14 @@ public class RichInputMethodManager {
|
|||
return keyboardCount > 1;
|
||||
}
|
||||
|
||||
public InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet(final String localeString,
|
||||
public InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet(final Locale locale,
|
||||
final String keyboardLayoutSetName) {
|
||||
final InputMethodInfo myImi = getInputMethodInfoOfThisIme();
|
||||
final int count = myImi.getSubtypeCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final InputMethodSubtype subtype = myImi.getSubtypeAt(i);
|
||||
final String layoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
|
||||
if (localeString.equals(subtype.getLocale())
|
||||
if (locale.equals(SubtypeUtilsKt.locale(subtype))
|
||||
&& keyboardLayoutSetName.equals(layoutName)) {
|
||||
return subtype;
|
||||
}
|
||||
|
@ -316,7 +317,7 @@ public class RichInputMethodManager {
|
|||
// search for exact match
|
||||
for (int i = 0; i < count; ++i) {
|
||||
final InputMethodSubtype subtype = subtypes.get(i);
|
||||
final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
|
||||
final Locale subtypeLocale = SubtypeUtilsKt.locale(subtype);
|
||||
if (subtypeLocale.equals(locale)) {
|
||||
return subtype;
|
||||
}
|
||||
|
@ -324,7 +325,7 @@ public class RichInputMethodManager {
|
|||
// search for language + country + variant match
|
||||
for (int i = 0; i < count; ++i) {
|
||||
final InputMethodSubtype subtype = subtypes.get(i);
|
||||
final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
|
||||
final Locale subtypeLocale = SubtypeUtilsKt.locale(subtype);
|
||||
if (subtypeLocale.getLanguage().equals(locale.getLanguage()) &&
|
||||
subtypeLocale.getCountry().equals(locale.getCountry()) &&
|
||||
subtypeLocale.getVariant().equals(locale.getVariant())) {
|
||||
|
@ -334,7 +335,7 @@ public class RichInputMethodManager {
|
|||
// search for language + country match
|
||||
for (int i = 0; i < count; ++i) {
|
||||
final InputMethodSubtype subtype = subtypes.get(i);
|
||||
final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
|
||||
final Locale subtypeLocale = SubtypeUtilsKt.locale(subtype);
|
||||
if (subtypeLocale.getLanguage().equals(locale.getLanguage()) &&
|
||||
subtypeLocale.getCountry().equals(locale.getCountry())) {
|
||||
return subtype;
|
||||
|
@ -344,7 +345,7 @@ public class RichInputMethodManager {
|
|||
final SharedPreferences prefs = DeviceProtectedUtils.getSharedPreferences(mContext);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
final InputMethodSubtype subtype = subtypes.get(i);
|
||||
final String subtypeLocale = subtype.getLocale();
|
||||
final Locale subtypeLocale = SubtypeUtilsKt.locale(subtype);
|
||||
final List<Locale> secondaryLocales = Settings.getSecondaryLocales(prefs, subtypeLocale);
|
||||
for (final Locale secondaryLocale : secondaryLocales) {
|
||||
if (secondaryLocale.equals(locale)) {
|
||||
|
@ -355,7 +356,7 @@ public class RichInputMethodManager {
|
|||
// search for language match
|
||||
for (int i = 0; i < count; ++i) {
|
||||
final InputMethodSubtype subtype = subtypes.get(i);
|
||||
final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
|
||||
final Locale subtypeLocale = SubtypeUtilsKt.locale(subtype);
|
||||
if (subtypeLocale.getLanguage().equals(locale.getLanguage())) {
|
||||
return subtype;
|
||||
}
|
||||
|
@ -363,7 +364,7 @@ public class RichInputMethodManager {
|
|||
// search for secondary language match
|
||||
for (int i = 0; i < count; ++i) {
|
||||
final InputMethodSubtype subtype = subtypes.get(i);
|
||||
final String subtypeLocale = subtype.getLocale();
|
||||
final Locale subtypeLocale = SubtypeUtilsKt.locale(subtype);
|
||||
final List<Locale> secondaryLocales = Settings.getSecondaryLocales(prefs, subtypeLocale);
|
||||
for (final Locale secondaryLocale : secondaryLocales) {
|
||||
if (secondaryLocale.getLanguage().equals(locale.getLanguage())) {
|
||||
|
@ -374,12 +375,12 @@ public class RichInputMethodManager {
|
|||
|
||||
// extra: if current script is not compatible to current subtype, search for compatible script
|
||||
// this is acceptable only because this function is only used for switching to a certain locale using EditorInfo.hintLocales
|
||||
final int script = ScriptUtils.getScriptFromSpellCheckerLocale(locale);
|
||||
if (script != ScriptUtils.getScriptFromSpellCheckerLocale(getCurrentSubtypeLocale())) {
|
||||
final String script = ScriptUtils.script(locale);
|
||||
if (!script.equals(ScriptUtils.script(getCurrentSubtypeLocale()))) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
final InputMethodSubtype subtype = subtypes.get(i);
|
||||
final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
|
||||
if (ScriptUtils.getScriptFromSpellCheckerLocale(subtypeLocale) == script) {
|
||||
final Locale subtypeLocale = SubtypeUtilsKt.locale(subtype);
|
||||
if (ScriptUtils.script(subtypeLocale).equals(script)) {
|
||||
return subtype;
|
||||
}
|
||||
}
|
||||
|
@ -418,7 +419,7 @@ public class RichInputMethodManager {
|
|||
final RichInputMethodSubtype richSubtype = mCurrentRichInputMethodSubtype;
|
||||
final boolean implicitlyEnabledSubtype = checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(
|
||||
richSubtype.getRawSubtype());
|
||||
final Locale systemLocale = mContext.getResources().getConfiguration().locale;
|
||||
final Locale systemLocale = ConfigurationCompatKt.locale(mContext.getResources().getConfiguration());
|
||||
LanguageOnSpacebarUtils.onSubtypeChanged(
|
||||
richSubtype, implicitlyEnabledSubtype, systemLocale);
|
||||
LanguageOnSpacebarUtils.setEnabledSubtypes(getMyEnabledInputMethodSubtypeList(
|
||||
|
|
|
@ -6,17 +6,15 @@
|
|||
|
||||
package org.dslul.openboard.inputmethod.latin;
|
||||
|
||||
import android.os.Build;
|
||||
import android.view.inputmethod.InputMethodSubtype;
|
||||
|
||||
import org.dslul.openboard.inputmethod.compat.InputMethodSubtypeCompatUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.common.Constants;
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.CustomLayoutUtilsKt;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.Log;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeLocaleUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeUtilsKt;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.dslul.openboard.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE;
|
||||
|
@ -33,30 +31,18 @@ import androidx.annotation.Nullable;
|
|||
public class RichInputMethodSubtype {
|
||||
private static final String TAG = RichInputMethodSubtype.class.getSimpleName();
|
||||
|
||||
// todo: remove this map when switching (rich input) subtype to use language tag
|
||||
private static final HashMap<Locale, Locale> sLocaleMap = initializeLocaleMap();
|
||||
private static HashMap<Locale, Locale> initializeLocaleMap() {
|
||||
final HashMap<Locale, Locale> map = new HashMap<>();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
// Locale#forLanguageTag is available on API Level 21+.
|
||||
// TODO: Remove this workaround once when we become able to deal with "sr-Latn".
|
||||
map.put(Locale.forLanguageTag("sr-Latn"), new Locale("sr", "ZZ"));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private final InputMethodSubtype mSubtype;
|
||||
@NonNull
|
||||
private final Locale mLocale;
|
||||
@NonNull
|
||||
private final Locale mOriginalLocale;
|
||||
// The subtype is considered RTL if the language of the main subtype is RTL.
|
||||
// Cached because it might get read frequently, e.g. when moving pointer with space bar
|
||||
private final boolean mIsRtl;
|
||||
|
||||
public RichInputMethodSubtype(@NonNull final InputMethodSubtype subtype) {
|
||||
mSubtype = subtype;
|
||||
mOriginalLocale = InputMethodSubtypeCompatUtils.getLocaleObject(mSubtype);
|
||||
final Locale mappedLocale = sLocaleMap.get(mOriginalLocale);
|
||||
mLocale = mappedLocale != null ? mappedLocale : mOriginalLocale;
|
||||
mLocale = SubtypeUtilsKt.locale(mSubtype);
|
||||
mIsRtl = LocaleUtils.isRtlLanguage(mLocale);
|
||||
}
|
||||
|
||||
// Extra values are determined by the primary subtype. This is probably right, but
|
||||
|
@ -71,7 +57,7 @@ public class RichInputMethodSubtype {
|
|||
}
|
||||
|
||||
public boolean isNoLanguage() {
|
||||
return SubtypeLocaleUtils.NO_LANGUAGE.equals(mSubtype.getLocale());
|
||||
return SubtypeLocaleUtils.NO_LANGUAGE.equals(mLocale.getLanguage());
|
||||
}
|
||||
|
||||
public boolean isCustom() {
|
||||
|
@ -105,7 +91,7 @@ public class RichInputMethodSubtype {
|
|||
if (isNoLanguage()) {
|
||||
return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(mSubtype);
|
||||
}
|
||||
return SubtypeLocaleUtils.getSubtypeLocaleDisplayName(mSubtype.getLocale());
|
||||
return SubtypeLocaleUtils.getSubtypeLocaleDisplayName(mLocale);
|
||||
}
|
||||
|
||||
// Get the RichInputMethodSubtype's middle display name in its locale.
|
||||
|
@ -114,7 +100,7 @@ public class RichInputMethodSubtype {
|
|||
if (isNoLanguage()) {
|
||||
return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(mSubtype);
|
||||
}
|
||||
return SubtypeLocaleUtils.getSubtypeLanguageDisplayName(mSubtype.getLocale());
|
||||
return SubtypeLocaleUtils.getSubtypeLanguageDisplayName(mLocale);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -141,14 +127,8 @@ public class RichInputMethodSubtype {
|
|||
return mLocale;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Locale getOriginalLocale() {
|
||||
return mOriginalLocale;
|
||||
}
|
||||
|
||||
public boolean isRtlSubtype() {
|
||||
// The subtype is considered RTL if the language of the main subtype is RTL.
|
||||
return LocaleUtils.isRtlLanguage(mLocale);
|
||||
return mIsRtl;
|
||||
}
|
||||
|
||||
// TODO: remove this method
|
||||
|
@ -215,7 +195,7 @@ public class RichInputMethodSubtype {
|
|||
RichInputMethodSubtype noLanguageSubtype = sNoLanguageSubtype;
|
||||
if (noLanguageSubtype == null) {
|
||||
final InputMethodSubtype rawNoLanguageSubtype = RichInputMethodManager.getInstance()
|
||||
.findSubtypeByLocaleAndKeyboardLayoutSet(SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.QWERTY);
|
||||
.findSubtypeByLocaleAndKeyboardLayoutSet(LocaleUtils.constructLocale(SubtypeLocaleUtils.NO_LANGUAGE), SubtypeLocaleUtils.QWERTY);
|
||||
if (rawNoLanguageSubtype != null) {
|
||||
noLanguageSubtype = new RichInputMethodSubtype(rawNoLanguageSubtype);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
package org.dslul.openboard.inputmethod.latin;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
|
@ -55,6 +54,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
|
|||
private static final String NAME = "userunigram";
|
||||
|
||||
private ContentObserver mObserver;
|
||||
// this really needs to be the locale string, as it interacts with system
|
||||
final private String mLocaleString;
|
||||
final private boolean mAlsoUseMoreRestrictiveLocales;
|
||||
|
||||
|
@ -71,7 +71,6 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
|
|||
mLocaleString = localeStr;
|
||||
}
|
||||
mAlsoUseMoreRestrictiveLocales = alsoUseMoreRestrictiveLocales;
|
||||
ContentResolver cres = context.getContentResolver();
|
||||
|
||||
mObserver = new ContentObserver(null) {
|
||||
@Override
|
||||
|
@ -79,7 +78,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
|
|||
setNeedsToRecreate();
|
||||
}
|
||||
};
|
||||
cres.registerContentObserver(Words.CONTENT_URI, true, mObserver);
|
||||
context.getContentResolver().registerContentObserver(Words.CONTENT_URI, true, mObserver);
|
||||
reloadDictionaryIfRequired();
|
||||
}
|
||||
|
||||
|
@ -104,7 +103,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
|
|||
public void loadInitialContentsLocked() {
|
||||
// Split the locale. For example "en" => ["en"], "de_DE" => ["de", "DE"],
|
||||
// "en_US_foo_bar_qux" => ["en", "US", "foo_bar_qux"] because of the limit of 3.
|
||||
// This is correct for locale processing.
|
||||
// This is correct for locale processing. (well, and it sucks e.g. for sr-Latn, resp. sr__#Latn as string)
|
||||
// For this example, we'll look at the "en_US_POSIX" case.
|
||||
final String[] localeElements =
|
||||
TextUtils.isEmpty(mLocaleString) ? new String[] {} : mLocaleString.split("_", 3);
|
||||
|
@ -131,8 +130,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
|
|||
// and request = "(locale is NULL) or (locale=?) or (locale=?) or (locale=?)"
|
||||
|
||||
final String[] requestArguments;
|
||||
// If length == 3, we already have all the arguments we need (common prefix is meaningless
|
||||
// inside variants
|
||||
// If length == 3, we already have all the arguments we need (common prefix is meaningless inside variants)
|
||||
if (mAlsoUseMoreRestrictiveLocales && length < 3) {
|
||||
request.append(" or (locale like ?)");
|
||||
// The following creates an array with one more (null) position
|
||||
|
@ -151,18 +149,15 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
|
|||
}
|
||||
final String requestString = request.toString();
|
||||
try {
|
||||
addWordsFromProjectionLocked(PROJECTION_QUERY_WITH_SHORTCUT, requestString,
|
||||
requestArguments);
|
||||
addWordsFromProjectionLocked(PROJECTION_QUERY_WITH_SHORTCUT, requestString, requestArguments);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// This may happen on some non-compliant devices where the declared API is JB+ but
|
||||
// the SHORTCUT column is not present for some reason.
|
||||
addWordsFromProjectionLocked(PROJECTION_QUERY_WITHOUT_SHORTCUT, requestString,
|
||||
requestArguments);
|
||||
addWordsFromProjectionLocked(PROJECTION_QUERY_WITHOUT_SHORTCUT, requestString, requestArguments);
|
||||
}
|
||||
}
|
||||
|
||||
private void addWordsFromProjectionLocked(final String[] query, String request,
|
||||
final String[] requestArguments)
|
||||
private void addWordsFromProjectionLocked(final String[] query, String request, final String[] requestArguments)
|
||||
throws IllegalArgumentException {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
|
|
|
@ -1,204 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
* modified
|
||||
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.dslul.openboard.inputmethod.latin.common;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.dslul.openboard.inputmethod.latin.R;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* A class to help with handling Locales in string form.
|
||||
* <p>
|
||||
* This file has the same meaning and features (and shares all of its code) with the one with the
|
||||
* same name in Latin IME. They need to be kept synchronized; for any update/bugfix to
|
||||
* this file, consider also updating/fixing the version in Latin IME.
|
||||
*/
|
||||
public final class LocaleUtils {
|
||||
private LocaleUtils() {
|
||||
// Intentional empty constructor for utility class.
|
||||
}
|
||||
|
||||
// Locale match level constants.
|
||||
// A higher level of match is guaranteed to have a higher numerical value.
|
||||
// Some room is left within constants to add match cases that may arise necessary
|
||||
// in the future, for example differentiating between the case where the countries
|
||||
// are both present and different, and the case where one of the locales does not
|
||||
// specify the countries. This difference is not needed now.
|
||||
|
||||
// Nothing matches.
|
||||
public static final int LOCALE_NO_MATCH = 0;
|
||||
// The languages matches, but the country are different. Or, the reference locale requires a
|
||||
// country and the tested locale does not have one.
|
||||
public static final int LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER = 3;
|
||||
// The languages and country match, but the variants are different. Or, the reference locale
|
||||
// requires a variant and the tested locale does not have one.
|
||||
public static final int LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER = 6;
|
||||
// The required locale is null or empty so it will accept anything, and the tested locale
|
||||
// is non-null and non-empty.
|
||||
public static final int LOCALE_ANY_MATCH = 10;
|
||||
// The language matches, and the tested locale specifies a country but the reference locale
|
||||
// does not require one.
|
||||
public static final int LOCALE_LANGUAGE_MATCH = 15;
|
||||
// The language and the country match, and the tested locale specifies a variant but the
|
||||
// reference locale does not require one.
|
||||
public static final int LOCALE_LANGUAGE_AND_COUNTRY_MATCH = 20;
|
||||
// The compared locales are fully identical. This is the best match level.
|
||||
public static final int LOCALE_FULL_MATCH = 30;
|
||||
|
||||
// The level at which a match is "normally" considered a locale match with standard algorithms.
|
||||
// Don't use this directly, use #isMatch to test.
|
||||
private static final int LOCALE_MATCH = LOCALE_ANY_MATCH;
|
||||
|
||||
/**
|
||||
* Return how well a tested locale matches a reference locale.
|
||||
* <p>
|
||||
* This will check the tested locale against the reference locale and return a measure of how
|
||||
* a well it matches the reference. The general idea is that the tested locale has to match
|
||||
* every specified part of the required locale. A full match occur when they are equal, a
|
||||
* partial match when the tested locale agrees with the reference locale but is more specific,
|
||||
* and a difference when the tested locale does not comply with all requirements from the
|
||||
* reference locale.
|
||||
* In more detail, if the reference locale specifies at least a language and the testedLocale
|
||||
* does not specify one, or specifies a different one, LOCALE_NO_MATCH is returned. If the
|
||||
* reference locale is empty or null, it will match anything - in the form of LOCALE_FULL_MATCH
|
||||
* if the tested locale is empty or null, and LOCALE_ANY_MATCH otherwise. If the reference and
|
||||
* tested locale agree on the language, but not on the country,
|
||||
* LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER is returned if the reference locale specifies a country,
|
||||
* and LOCALE_LANGUAGE_MATCH otherwise.
|
||||
* If they agree on both the language and the country, but not on the variant,
|
||||
* LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER is returned if the reference locale
|
||||
* specifies a variant, and LOCALE_LANGUAGE_AND_COUNTRY_MATCH otherwise. If everything matches,
|
||||
* LOCALE_FULL_MATCH is returned.
|
||||
* Examples:
|
||||
* en <=> en_US => LOCALE_LANGUAGE_MATCH
|
||||
* en_US <=> en => LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER
|
||||
* en_US_POSIX <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER
|
||||
* en_US <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH
|
||||
* sp_US <=> en_US => LOCALE_NO_MATCH
|
||||
* de <=> de => LOCALE_FULL_MATCH
|
||||
* en_US <=> en_US => LOCALE_FULL_MATCH
|
||||
* "" <=> en_US => LOCALE_ANY_MATCH
|
||||
*
|
||||
* @param referenceLocale the reference locale to test against.
|
||||
* @param testedLocale the locale to test.
|
||||
* @return a constant that measures how well the tested locale matches the reference locale.
|
||||
*/
|
||||
public static int getMatchLevel(@Nullable final String referenceLocale,
|
||||
@Nullable final String testedLocale) {
|
||||
if (StringUtils.isEmpty(referenceLocale)) {
|
||||
return StringUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH;
|
||||
}
|
||||
if (null == testedLocale) return LOCALE_NO_MATCH;
|
||||
final String[] referenceParams = referenceLocale.split("_", 3);
|
||||
final String[] testedParams = testedLocale.split("_", 3);
|
||||
// By spec of String#split, [0] cannot be null and length cannot be 0.
|
||||
if (!referenceParams[0].equals(testedParams[0])) return LOCALE_NO_MATCH;
|
||||
switch (referenceParams.length) {
|
||||
case 1:
|
||||
return 1 == testedParams.length ? LOCALE_FULL_MATCH : LOCALE_LANGUAGE_MATCH;
|
||||
case 2:
|
||||
if (1 == testedParams.length) return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
|
||||
if (!referenceParams[1].equals(testedParams[1]))
|
||||
return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
|
||||
if (3 == testedParams.length) return LOCALE_LANGUAGE_AND_COUNTRY_MATCH;
|
||||
return LOCALE_FULL_MATCH;
|
||||
case 3:
|
||||
if (1 == testedParams.length) return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
|
||||
if (!referenceParams[1].equals(testedParams[1]))
|
||||
return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
|
||||
if (2 == testedParams.length) return LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER;
|
||||
if (!referenceParams[2].equals(testedParams[2]))
|
||||
return LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER;
|
||||
return LOCALE_FULL_MATCH;
|
||||
}
|
||||
// It should be impossible to come here
|
||||
return LOCALE_NO_MATCH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out whether a match level should be considered a match.
|
||||
* <p>
|
||||
* This method takes a match level as returned by the #getMatchLevel method, and returns whether
|
||||
* it should be considered a match in the usual sense with standard Locale functions.
|
||||
*
|
||||
* @param level the match level, as returned by getMatchLevel.
|
||||
* @return whether this is a match or not.
|
||||
*/
|
||||
public static boolean isMatch(final int level) {
|
||||
return LOCALE_MATCH <= level;
|
||||
}
|
||||
|
||||
/** similar to isMatch, but returns true if there is anything matching (used for fallback) */
|
||||
public static boolean isMatchWeak(final int level) {
|
||||
return level > LOCALE_NO_MATCH;
|
||||
}
|
||||
|
||||
private static final HashMap<String, Locale> sLocaleCache = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Creates a locale from a string specification.
|
||||
* @param localeString a string specification of a locale, in a format of "ll_cc_variant" where
|
||||
* "ll" is a language code, "cc" is a country code.
|
||||
*/
|
||||
@NonNull
|
||||
public static Locale constructLocaleFromString(@NonNull final String localeString) {
|
||||
synchronized (sLocaleCache) {
|
||||
if (sLocaleCache.containsKey(localeString)) {
|
||||
return sLocaleCache.get(localeString);
|
||||
}
|
||||
final String[] elements = localeString.split("_", 3);
|
||||
final Locale locale;
|
||||
if (elements.length == 1) {
|
||||
locale = new Locale(elements[0]);
|
||||
} else if (elements.length == 2) {
|
||||
locale = new Locale(elements[0], elements[1]);
|
||||
} else { // localeParams.length == 3
|
||||
locale = new Locale(elements[0], elements[1], elements[2]);
|
||||
}
|
||||
sLocaleCache.put(localeString, locale);
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Get this information from the framework instead of maintaining here by ourselves.
|
||||
private static final HashSet<String> sRtlLanguageCodes = new HashSet<>();
|
||||
static {
|
||||
// List of known Right-To-Left language codes.
|
||||
sRtlLanguageCodes.add("ar"); // Arabic
|
||||
sRtlLanguageCodes.add("fa"); // Persian
|
||||
sRtlLanguageCodes.add("iw"); // Hebrew
|
||||
sRtlLanguageCodes.add("ku"); // Kurdish
|
||||
sRtlLanguageCodes.add("ps"); // Pashto
|
||||
sRtlLanguageCodes.add("sd"); // Sindhi
|
||||
sRtlLanguageCodes.add("ug"); // Uyghur
|
||||
sRtlLanguageCodes.add("ur"); // Urdu
|
||||
sRtlLanguageCodes.add("yi"); // Yiddish
|
||||
}
|
||||
|
||||
public static boolean isRtlLanguage(@NonNull final Locale locale) {
|
||||
return sRtlLanguageCodes.contains(locale.getLanguage());
|
||||
}
|
||||
|
||||
public static String getLocaleDisplayNameInSystemLocale(final Locale locale, final Context context) {
|
||||
final String localeString = locale.toString();
|
||||
if (localeString.equals("zz"))
|
||||
return context.getString(R.string.subtype_no_language);
|
||||
if (localeString.endsWith("_ZZ") || localeString.endsWith("_zz")) {
|
||||
final int resId = context.getResources().getIdentifier("subtype_"+localeString, "string", context.getPackageName());
|
||||
if (resId != 0)
|
||||
return context.getString(resId);
|
||||
}
|
||||
return locale.getDisplayName(context.getResources().getConfiguration().locale);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
* modified
|
||||
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||
*/
|
||||
package org.dslul.openboard.inputmethod.latin.common
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import org.dslul.openboard.inputmethod.compat.locale
|
||||
import org.dslul.openboard.inputmethod.latin.R
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils.script
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeLocaleUtils
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* A class to help with handling Locales in string form.
|
||||
*
|
||||
*
|
||||
* This file has the same meaning and features (and shares all of its code) with the one with the
|
||||
* same name in Latin IME. They need to be kept synchronized; for any update/bugfix to
|
||||
* this file, consider also updating/fixing the version in Latin IME.
|
||||
*/
|
||||
object LocaleUtils {
|
||||
// Locale match level constants.
|
||||
// A higher level of match is guaranteed to have a higher numerical value.
|
||||
// Some room is left within constants to add match cases that may arise necessary
|
||||
// in the future, for example differentiating between the case where the countries
|
||||
// are both present and different, and the case where one of the locales does not
|
||||
// specify the countries. This difference is not needed now.
|
||||
// Nothing matches.
|
||||
private const val LOCALE_NO_MATCH = 0
|
||||
|
||||
// The language (and maybe more) matches, but the script is different
|
||||
private const val LOCALE_MATCH_SCRIPT_DIFFER = 1
|
||||
|
||||
// The languages matches, but the country are different. Or, the reference locale requires a
|
||||
// country and the tested locale does not have one.
|
||||
private const val LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER = 3
|
||||
|
||||
// The languages and country match, but the variants are different. Or, the reference locale
|
||||
// requires a variant and the tested locale does not have one.
|
||||
private const val LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER = 6
|
||||
|
||||
// The required locale is null or empty so it will accept anything, and the tested locale
|
||||
// is non-null and non-empty.
|
||||
private const val LOCALE_ANY_MATCH = 10
|
||||
|
||||
// The language matches, and the tested locale specifies a country but the reference locale
|
||||
// does not require one.
|
||||
private const val LOCALE_LANGUAGE_MATCH = 15
|
||||
|
||||
// The language and the country match, and the tested locale specifies a variant but the
|
||||
// reference locale does not require one.
|
||||
private const val LOCALE_LANGUAGE_AND_COUNTRY_MATCH = 20
|
||||
|
||||
// The compared locales are fully identical. This is the best match level.
|
||||
private const val LOCALE_FULL_MATCH = 30
|
||||
|
||||
/**
|
||||
* Return how well a tested locale matches a reference locale.
|
||||
*
|
||||
*
|
||||
* This will check the tested locale against the reference locale and return a measure of how
|
||||
* a well it matches the reference. The general idea is that the tested locale has to match
|
||||
* every specified part of the required locale. A full match occur when they are equal, a
|
||||
* partial match when the tested locale agrees with the reference locale but is more specific,
|
||||
* and a difference when the tested locale does not comply with all requirements from the
|
||||
* reference locale.
|
||||
* In more detail, if the reference locale specifies at least a language and the testedLocale
|
||||
* does not specify one, or specifies a different one, LOCALE_NO_MATCH is returned. If the
|
||||
* reference locale is empty or null, it will match anything - in the form of LOCALE_FULL_MATCH
|
||||
* if the tested locale is empty or null, and LOCALE_ANY_MATCH otherwise. If the reference and
|
||||
* tested locale agree on the language, but not on the country,
|
||||
* LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER is returned if the reference locale specifies a country,
|
||||
* and LOCALE_LANGUAGE_MATCH otherwise.
|
||||
* If they agree on both the language and the country, but not on the variant,
|
||||
* LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER is returned if the reference locale
|
||||
* specifies a variant, and LOCALE_LANGUAGE_AND_COUNTRY_MATCH otherwise. If everything matches,
|
||||
* LOCALE_FULL_MATCH is returned.
|
||||
* Examples:
|
||||
* en <=> en_US => LOCALE_LANGUAGE_MATCH
|
||||
* en_US <=> en => LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER
|
||||
* en_US_POSIX <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER
|
||||
* en_US <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH
|
||||
* sp_US <=> en_US => LOCALE_NO_MATCH
|
||||
* de <=> de => LOCALE_FULL_MATCH
|
||||
* en_US <=> en_US => LOCALE_FULL_MATCH
|
||||
* "" <=> en_US => LOCALE_ANY_MATCH
|
||||
*
|
||||
* @param reference the reference locale to test against.
|
||||
* @param tested the locale to test.
|
||||
* @return a constant that measures how well the tested locale matches the reference locale.
|
||||
*/
|
||||
private fun getMatchLevel(reference: Locale, tested: Locale): Int {
|
||||
if (reference == tested) return LOCALE_FULL_MATCH
|
||||
if (reference.toString().isEmpty()) return LOCALE_ANY_MATCH
|
||||
if (reference.language != tested.language) return LOCALE_NO_MATCH
|
||||
// language matches
|
||||
if (reference.script() != tested.script()) {
|
||||
return LOCALE_MATCH_SCRIPT_DIFFER
|
||||
}
|
||||
// script matches
|
||||
if (reference.country != tested.country) {
|
||||
return if (reference.country.isEmpty()) LOCALE_LANGUAGE_MATCH
|
||||
else LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER
|
||||
}
|
||||
// country matches
|
||||
return if (reference.variant == tested.variant) LOCALE_FULL_MATCH
|
||||
else if (reference.variant.isEmpty()) LOCALE_LANGUAGE_AND_COUNTRY_MATCH
|
||||
else LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER
|
||||
}
|
||||
|
||||
fun <T> getBestMatch(locale: Locale, collection: Collection<T>, toLocale: (T) -> Locale): T? {
|
||||
var best: T? = null
|
||||
var bestLevel = 0
|
||||
collection.forEach {
|
||||
val level = getMatchLevel(locale, toLocale(it))
|
||||
if (level > bestLevel && level >= LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER) {
|
||||
bestLevel = level
|
||||
best = it
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
private val sLocaleCache = HashMap<String, Locale>()
|
||||
|
||||
/**
|
||||
* Creates a locale from a string specification or language tag.
|
||||
* Ideally this works as reverse of Locale.toString and Locale.toLanguageTag
|
||||
* If a localeString contains "-" it is always interpreted as language tag.
|
||||
* localeString is a string specification of a locale, in a format of "ll_cc_variant" where
|
||||
* "ll" is a language code, "cc" is a country code.
|
||||
* The script may also be part of the locale string, e.g. "ll_cc_#script"
|
||||
* Converts "ZZ" regions that used to signal latin script into actual latin script.
|
||||
* "cc" / region should be uppercase and language should be lowercase, this is automatically converted
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.constructLocale(): Locale {
|
||||
synchronized(sLocaleCache) {
|
||||
sLocaleCache[this]?.let { return it }
|
||||
if (contains("-")) {
|
||||
// looks like it's actually a language tag, and not a locale string
|
||||
val locale = Locale.forLanguageTag(this)
|
||||
sLocaleCache[this] = locale
|
||||
return locale
|
||||
}
|
||||
val elements = split("_", limit = 3)
|
||||
val language = elements[0].lowercase()
|
||||
val region = elements.getOrNull(1)?.uppercase()
|
||||
val locale = if (elements.size == 1) {
|
||||
Locale(language) // "zz" works both in constructor and forLanguageTag
|
||||
} else if (elements.size == 2) {
|
||||
if (region == "ZZ") Locale.forLanguageTag(elements[0] + "-Latn")
|
||||
else Locale(language, region!!)
|
||||
} else if (language == "zz") { // localeParams.length == 3
|
||||
Locale.Builder().setLanguage(language).setVariant(elements[2]).setScript("Latn").build()
|
||||
} else if (elements[2].startsWith("#")) {
|
||||
// best guess: elements[2] is a script, e.g. sr-Latn locale to string is sr__#Latn
|
||||
Locale.Builder().setLanguage(language).setRegion(region).setScript(elements[2].substringAfter("#")).build()
|
||||
} else {
|
||||
Locale(language, region!!, elements[2])
|
||||
}
|
||||
sLocaleCache[this] = locale
|
||||
return locale
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isRtlLanguage(locale: Locale): Boolean {
|
||||
val displayName = locale.displayName
|
||||
if (displayName.isEmpty()) return true
|
||||
return when (Character.getDirectionality(displayName.codePointAt(0))) {
|
||||
Character.DIRECTIONALITY_RIGHT_TO_LEFT, Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getLocaleDisplayNameInSystemLocale(locale: Locale, context: Context): String {
|
||||
val languageTag = locale.toLanguageTag()
|
||||
if (languageTag == SubtypeLocaleUtils.NO_LANGUAGE) return context.getString(R.string.subtype_no_language)
|
||||
if (locale.script() != locale.language.constructLocale().script()) {
|
||||
val resId = context.resources.getIdentifier("subtype_${languageTag.replace("-", "_")}", "string", context.packageName)
|
||||
if (resId != 0) return context.getString(resId)
|
||||
}
|
||||
return locale.getDisplayName(context.resources.configuration.locale())
|
||||
}
|
||||
}
|
|
@ -261,7 +261,7 @@ public final class InputLogic {
|
|||
// interface
|
||||
public InputTransaction onPickSuggestionManually(final SettingsValues settingsValues,
|
||||
final SuggestedWordInfo suggestionInfo, final int keyboardShiftState,
|
||||
final int currentKeyboardScriptId, final LatinIME.UIHandler handler) {
|
||||
final String currentKeyboardScript, final LatinIME.UIHandler handler) {
|
||||
final SuggestedWords suggestedWords = mSuggestedWords;
|
||||
final String suggestion = suggestionInfo.mWord;
|
||||
// If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
|
||||
|
@ -271,7 +271,7 @@ public final class InputLogic {
|
|||
// Word separators are suggested before the user inputs something.
|
||||
// Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
|
||||
final Event event = Event.createPunctuationSuggestionPickedEvent(suggestionInfo);
|
||||
return onCodeInput(settingsValues, event, keyboardShiftState, currentKeyboardScriptId, handler);
|
||||
return onCodeInput(settingsValues, event, keyboardShiftState, currentKeyboardScript, handler);
|
||||
}
|
||||
|
||||
final Event event = Event.createSuggestionPickedEvent(suggestionInfo);
|
||||
|
@ -426,11 +426,11 @@ public final class InputLogic {
|
|||
*/
|
||||
public InputTransaction onCodeInput(final SettingsValues settingsValues,
|
||||
@NonNull final Event event, final int keyboardShiftMode,
|
||||
final int currentKeyboardScriptId, final LatinIME.UIHandler handler) {
|
||||
final String currentKeyboardScript, final LatinIME.UIHandler handler) {
|
||||
mWordBeingCorrectedByCursor = null;
|
||||
mJustRevertedACommit = false;
|
||||
final Event processedEvent;
|
||||
if (currentKeyboardScriptId == ScriptUtils.SCRIPT_HANGUL
|
||||
if (currentKeyboardScript.equals(ScriptUtils.SCRIPT_HANGUL)
|
||||
// only use the Hangul chain if codepoint may actually be Hangul
|
||||
// todo: this whole hangul-related logic should probably be somewhere else
|
||||
// need to use hangul combiner for whitespace, because otherwise the current word
|
||||
|
@ -472,7 +472,7 @@ public final class InputLogic {
|
|||
if (currentEvent.isConsumed()) {
|
||||
handleConsumedEvent(currentEvent, inputTransaction);
|
||||
} else if (currentEvent.isFunctionalKeyEvent()) {
|
||||
handleFunctionalEvent(currentEvent, inputTransaction, currentKeyboardScriptId, handler);
|
||||
handleFunctionalEvent(currentEvent, inputTransaction, currentKeyboardScript, handler);
|
||||
} else {
|
||||
handleNonFunctionalEvent(currentEvent, inputTransaction, handler);
|
||||
}
|
||||
|
@ -484,7 +484,7 @@ public final class InputLogic {
|
|||
&& (settingsValues.isWordCodePoint(processedEvent.getMCodePoint())
|
||||
|| processedEvent.getMKeyCode() == Constants.CODE_DELETE)
|
||||
) {
|
||||
mWordBeingCorrectedByCursor = getWordAtCursor(settingsValues, currentKeyboardScriptId);
|
||||
mWordBeingCorrectedByCursor = getWordAtCursor(settingsValues, currentKeyboardScript);
|
||||
}
|
||||
if (!inputTransaction.didAutoCorrect() && processedEvent.getMKeyCode() != Constants.CODE_SHIFT
|
||||
&& processedEvent.getMKeyCode() != Constants.CODE_CAPSLOCK
|
||||
|
@ -645,10 +645,10 @@ public final class InputLogic {
|
|||
* @param inputTransaction The transaction in progress.
|
||||
*/
|
||||
private void handleFunctionalEvent(final Event event, final InputTransaction inputTransaction,
|
||||
final int currentKeyboardScriptId, final LatinIME.UIHandler handler) {
|
||||
final String currentKeyboardScript, final LatinIME.UIHandler handler) {
|
||||
switch (event.getMKeyCode()) {
|
||||
case Constants.CODE_DELETE:
|
||||
handleBackspaceEvent(event, inputTransaction, currentKeyboardScriptId);
|
||||
handleBackspaceEvent(event, inputTransaction, currentKeyboardScript);
|
||||
// Backspace is a functional key, but it affects the contents of the editor.
|
||||
inputTransaction.setDidAffectContents();
|
||||
break;
|
||||
|
@ -707,7 +707,7 @@ public final class InputLogic {
|
|||
mConnection.selectAll();
|
||||
break;
|
||||
case Constants.CODE_SELECT_WORD:
|
||||
mConnection.selectWord(inputTransaction.getMSettingsValues().mSpacingAndPunctuations, currentKeyboardScriptId);
|
||||
mConnection.selectWord(inputTransaction.getMSettingsValues().mSpacingAndPunctuations, currentKeyboardScript);
|
||||
break;
|
||||
case Constants.CODE_COPY:
|
||||
mConnection.copyText();
|
||||
|
@ -1098,7 +1098,7 @@ public final class InputLogic {
|
|||
* @param inputTransaction The transaction in progress.
|
||||
*/
|
||||
private void handleBackspaceEvent(final Event event, final InputTransaction inputTransaction,
|
||||
final int currentKeyboardScriptId) {
|
||||
final String currentKeyboardScript) {
|
||||
mSpaceState = SpaceState.NONE;
|
||||
mDeleteCount++;
|
||||
|
||||
|
@ -1160,7 +1160,7 @@ public final class InputLogic {
|
|||
&& inputTransaction.getMSettingsValues().mSpacingAndPunctuations.mCurrentLanguageHasSpaces
|
||||
&& !mConnection.isCursorFollowedByWordCharacter(
|
||||
inputTransaction.getMSettingsValues().mSpacingAndPunctuations)) {
|
||||
restartSuggestionsOnWordTouchedByCursor(inputTransaction.getMSettingsValues(), currentKeyboardScriptId);
|
||||
restartSuggestionsOnWordTouchedByCursor(inputTransaction.getMSettingsValues(), currentKeyboardScript);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -1235,7 +1235,7 @@ public final class InputLogic {
|
|||
// consider unlearning here because we may have already reached
|
||||
// the previous word, and will lose it after next deletion.
|
||||
hasUnlearnedWordBeingDeleted |= unlearnWordBeingDeleted(
|
||||
inputTransaction.getMSettingsValues(), currentKeyboardScriptId);
|
||||
inputTransaction.getMSettingsValues(), currentKeyboardScript);
|
||||
sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
|
||||
totalDeletedLength++;
|
||||
}
|
||||
|
@ -1267,7 +1267,7 @@ public final class InputLogic {
|
|||
// consider unlearning here because we may have already reached
|
||||
// the previous word, and will lose it after next deletion.
|
||||
hasUnlearnedWordBeingDeleted |= unlearnWordBeingDeleted(
|
||||
inputTransaction.getMSettingsValues(), currentKeyboardScriptId);
|
||||
inputTransaction.getMSettingsValues(), currentKeyboardScript);
|
||||
final int codePointBeforeCursorToDeleteAgain =
|
||||
mConnection.getCodePointBeforeCursor();
|
||||
if (codePointBeforeCursorToDeleteAgain != Constants.NOT_A_CODE) {
|
||||
|
@ -1284,7 +1284,7 @@ public final class InputLogic {
|
|||
if (!hasUnlearnedWordBeingDeleted) {
|
||||
// Consider unlearning the word being deleted (if we have not done so already).
|
||||
unlearnWordBeingDeleted(
|
||||
inputTransaction.getMSettingsValues(), currentKeyboardScriptId);
|
||||
inputTransaction.getMSettingsValues(), currentKeyboardScript);
|
||||
}
|
||||
if (mConnection.hasSlowInputConnection()) {
|
||||
mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
|
||||
|
@ -1292,18 +1292,18 @@ public final class InputLogic {
|
|||
&& inputTransaction.getMSettingsValues().mSpacingAndPunctuations.mCurrentLanguageHasSpaces
|
||||
&& !mConnection.isCursorFollowedByWordCharacter(
|
||||
inputTransaction.getMSettingsValues().mSpacingAndPunctuations)) {
|
||||
restartSuggestionsOnWordTouchedByCursor(inputTransaction.getMSettingsValues(), currentKeyboardScriptId);
|
||||
restartSuggestionsOnWordTouchedByCursor(inputTransaction.getMSettingsValues(), currentKeyboardScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String getWordAtCursor(final SettingsValues settingsValues, final int currentKeyboardScriptId) {
|
||||
String getWordAtCursor(final SettingsValues settingsValues, final String currentKeyboardScript) {
|
||||
if (!mConnection.hasSelection()
|
||||
&& settingsValues.isSuggestionsEnabledPerUserSettings()
|
||||
&& settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) {
|
||||
final TextRange range = mConnection.getWordRangeAtCursor(
|
||||
settingsValues.mSpacingAndPunctuations,
|
||||
currentKeyboardScriptId, false);
|
||||
currentKeyboardScript, false);
|
||||
if (range != null) {
|
||||
return range.mWord.toString();
|
||||
}
|
||||
|
@ -1312,7 +1312,7 @@ public final class InputLogic {
|
|||
}
|
||||
|
||||
boolean unlearnWordBeingDeleted(
|
||||
final SettingsValues settingsValues, final int currentKeyboardScriptId) {
|
||||
final SettingsValues settingsValues, final String currentKeyboardScript) {
|
||||
if (mConnection.hasSlowInputConnection()) {
|
||||
// TODO: Refactor unlearning so that it does not incur any extra calls
|
||||
// to the InputConnection. That way it can still be performed on a slow
|
||||
|
@ -1324,7 +1324,7 @@ public final class InputLogic {
|
|||
// entered the composing state yet), unlearn the word.
|
||||
// TODO: Consider tracking whether or not this word was typed by the user.
|
||||
if (!mConnection.isCursorFollowedByWordCharacter(settingsValues.mSpacingAndPunctuations)) {
|
||||
final String wordBeingDeleted = getWordAtCursor(settingsValues, currentKeyboardScriptId);
|
||||
final String wordBeingDeleted = getWordAtCursor(settingsValues, currentKeyboardScript);
|
||||
if (!TextUtils.isEmpty(wordBeingDeleted)) {
|
||||
unlearnWord(wordBeingDeleted, settingsValues, Constants.EVENT_BACKSPACE);
|
||||
return true;
|
||||
|
@ -1625,7 +1625,7 @@ public final class InputLogic {
|
|||
*/
|
||||
public void restartSuggestionsOnWordTouchedByCursor(final SettingsValues settingsValues,
|
||||
// TODO: remove this argument, put it into settingsValues
|
||||
final int currentKeyboardScriptId) {
|
||||
final String currentKeyboardScript) {
|
||||
// HACK: We may want to special-case some apps that exhibit bad behavior in case of
|
||||
// recorrection. This is a temporary, stopgap measure that will be removed later.
|
||||
// TODO: remove this.
|
||||
|
@ -1653,7 +1653,7 @@ public final class InputLogic {
|
|||
return;
|
||||
}
|
||||
final TextRange range =
|
||||
mConnection.getWordRangeAtCursor(settingsValues.mSpacingAndPunctuations, currentKeyboardScriptId, true);
|
||||
mConnection.getWordRangeAtCursor(settingsValues.mSpacingAndPunctuations, currentKeyboardScript, true);
|
||||
if (null == range) return; // Happens if we don't have an input connection at all
|
||||
if (range.length() <= 0) {
|
||||
// Race condition, or touching a word in a non-supported script.
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.dslul.openboard.inputmethod.latin.makedict
|
||||
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils.constructLocale
|
||||
import org.dslul.openboard.inputmethod.latin.makedict.FormatSpec.DictionaryOptions
|
||||
import org.dslul.openboard.inputmethod.latin.makedict.FormatSpec.FormatOptions
|
||||
import java.text.DateFormat
|
||||
|
@ -44,7 +45,7 @@ class DictionaryHeader(
|
|||
fun info(locale: Locale): String {
|
||||
val date = if (mDate == null) ""
|
||||
else DateFormat.getDateInstance(DateFormat.SHORT, locale).format(Date(mDate * 1000L)) + "\n"
|
||||
return mIdString + "\n" + LocaleUtils.constructLocaleFromString(mLocaleString).getDisplayName(locale) +
|
||||
return mIdString + "\n" + mLocaleString.constructLocale().getDisplayName(locale) +
|
||||
"\nv" + mVersionString + "\n" + date + description
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import androidx.appcompat.app.AlertDialog
|
|||
import androidx.preference.Preference
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.dslul.openboard.inputmethod.compat.locale
|
||||
import org.dslul.openboard.inputmethod.dictionarypack.DictionaryPackConstants
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ChecksumCalculator
|
||||
import org.dslul.openboard.inputmethod.keyboard.KeyboardLayoutSet
|
||||
|
@ -28,6 +29,7 @@ import org.dslul.openboard.inputmethod.latin.BuildConfig
|
|||
import org.dslul.openboard.inputmethod.latin.R
|
||||
import org.dslul.openboard.inputmethod.latin.SystemBroadcastReceiver
|
||||
import org.dslul.openboard.inputmethod.latin.common.FileUtils
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils.constructLocale
|
||||
import org.dslul.openboard.inputmethod.latin.settings.SeekBarDialogPreference.ValueProxy
|
||||
import org.dslul.openboard.inputmethod.latin.utils.CUSTOM_LAYOUT_PREFIX
|
||||
import org.dslul.openboard.inputmethod.latin.utils.JniUtils
|
||||
|
@ -114,7 +116,7 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
|||
findPreference<Preference>("custom_background_image")?.setOnPreferenceClickListener { onClickLoadImage() }
|
||||
|
||||
findPreference<Preference>("custom_symbols_layout")?.setOnPreferenceClickListener {
|
||||
val layoutName = Settings.readSymbolsLayoutName(context, context.resources.configuration.locale).takeIf { it.startsWith(CUSTOM_LAYOUT_PREFIX) }
|
||||
val layoutName = Settings.readSymbolsLayoutName(context, context.resources.configuration.locale()).takeIf { it.startsWith(CUSTOM_LAYOUT_PREFIX) }
|
||||
val oldLayout = if (layoutName != null) null else context.assets.open("layouts${File.separator}symbols.txt").reader().readText()
|
||||
editCustomLayout(layoutName ?: "${CUSTOM_LAYOUT_PREFIX}symbols.txt", context, oldLayout, true)
|
||||
true
|
||||
|
@ -169,7 +171,6 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
|||
}
|
||||
|
||||
val checksum = ChecksumCalculator.checksum(tmpfile.inputStream()) ?: ""
|
||||
Log.i("test", "cs $checksum")
|
||||
if (checksum == JniUtils.expectedDefaultChecksum()) {
|
||||
renameToLibfileAndRestart(tmpfile, checksum)
|
||||
} else {
|
||||
|
@ -310,7 +311,8 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
|||
val filesDir = requireContext().filesDir?.path ?: return
|
||||
while (entry != null) {
|
||||
if (backupFilePatterns.any { entry!!.name.matches(it) }) {
|
||||
val file = File(filesDir, entry.name)
|
||||
val targetFileName = upgradeFileNames(entry.name)
|
||||
val file = File(filesDir, targetFileName)
|
||||
FileUtils.copyStreamToNewFile(zip, file)
|
||||
} else if (entry.name == PREFS_FILE_NAME) {
|
||||
val prefLines = String(zip.readBytes()).split("\n")
|
||||
|
@ -335,6 +337,38 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
// todo (later): remove this when new package name has been in use for long enough, this is only for migrating from old openboard name
|
||||
private fun upgradeFileNames(originalName: String): String {
|
||||
return when {
|
||||
originalName.endsWith(USER_DICTIONARY_SUFFIX) -> {
|
||||
// replace directory after switch to language tag
|
||||
val dirName = originalName.substringAfter(File.separator).substringBefore(File.separator)
|
||||
originalName.replace(dirName, dirName.constructLocale().toLanguageTag())
|
||||
}
|
||||
originalName.startsWith("blacklists") -> {
|
||||
// replace file name after switch to language tag
|
||||
val fileName = originalName.substringAfter("blacklists${File.separator}").substringBefore(".txt")
|
||||
originalName.replace(fileName, fileName.constructLocale().toLanguageTag())
|
||||
}
|
||||
originalName.startsWith("layouts") -> {
|
||||
// replace file name after switch to language tag
|
||||
// but only if it's not a symbols layout
|
||||
val localeString = originalName.substringAfter(".").substringBefore(".")
|
||||
val locale = localeString.constructLocale()
|
||||
if (locale.toLanguageTag() != "und")
|
||||
originalName.replace(localeString, locale.toLanguageTag())
|
||||
else
|
||||
originalName // no valid locale -> must be symbols layout, don't change
|
||||
}
|
||||
originalName.startsWith("UserHistoryDictionary") -> {
|
||||
val localeString = originalName.substringAfter(".").substringBefore(".")
|
||||
val locale = localeString.constructLocale()
|
||||
originalName.replace(localeString, locale.toLanguageTag())
|
||||
}
|
||||
else -> originalName
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupKeyLongpressTimeoutSettings() {
|
||||
val prefs = sharedPreferences
|
||||
findPreference<SeekBarDialogPreference>(Settings.PREF_KEY_LONGPRESS_TIMEOUT)?.setInterface(object : ValueProxy {
|
||||
|
|
|
@ -28,7 +28,6 @@ import org.dslul.openboard.inputmethod.latin.utils.isAdditionalSubtype
|
|||
import org.dslul.openboard.inputmethod.latin.utils.locale
|
||||
import org.dslul.openboard.inputmethod.latin.utils.removeEnabledSubtype
|
||||
import org.dslul.openboard.inputmethod.latin.utils.showMissingDictionaryDialog
|
||||
import org.dslul.openboard.inputmethod.latin.utils.toLocale
|
||||
|
||||
class LanguageFilterList(searchField: EditText, recyclerView: RecyclerView) {
|
||||
|
||||
|
@ -124,7 +123,7 @@ private class LanguageAdapter(list: List<MutableList<SubtypeInfo>> = listOf(), c
|
|||
setOnCheckedChangeListener { _, b ->
|
||||
if (b) {
|
||||
if (!infos.first().hasDictionary)
|
||||
showMissingDictionaryDialog(context, infos.first().subtype.locale().toLocale())
|
||||
showMissingDictionaryDialog(context, infos.first().subtype.locale())
|
||||
addEnabledSubtype(prefs, infos.first().subtype)
|
||||
infos.first().isEnabled = true
|
||||
} else {
|
||||
|
|
|
@ -18,16 +18,18 @@ import androidx.core.view.get
|
|||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.size
|
||||
import org.dslul.openboard.inputmethod.compat.locale
|
||||
import org.dslul.openboard.inputmethod.dictionarypack.DictionaryPackConstants
|
||||
import org.dslul.openboard.inputmethod.keyboard.KeyboardLayoutSet
|
||||
import org.dslul.openboard.inputmethod.keyboard.KeyboardSwitcher
|
||||
import org.dslul.openboard.inputmethod.latin.BinaryDictionaryGetter
|
||||
import org.dslul.openboard.inputmethod.latin.R
|
||||
import org.dslul.openboard.inputmethod.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils.constructLocale
|
||||
import org.dslul.openboard.inputmethod.latin.databinding.LanguageListItemBinding
|
||||
import org.dslul.openboard.inputmethod.latin.databinding.LocaleSettingsDialogBinding
|
||||
import org.dslul.openboard.inputmethod.latin.utils.*
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils.script
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
|
@ -40,8 +42,7 @@ class LanguageSettingsDialog(
|
|||
) : AlertDialog(context), LanguageSettingsFragment.Listener {
|
||||
private val prefs = DeviceProtectedUtils.getSharedPreferences(context)!!
|
||||
private val binding = LocaleSettingsDialogBinding.inflate(LayoutInflater.from(context))
|
||||
private val mainLocaleString = infos.first().subtype.locale()
|
||||
private val mainLocale = mainLocaleString.toLocale()
|
||||
private val mainLocale = infos.first().subtype.locale()
|
||||
private var hasInternalDictForLanguage = false
|
||||
private val userDicts = mutableSetOf<File>()
|
||||
|
||||
|
@ -97,7 +98,7 @@ class LanguageSettingsDialog(
|
|||
}
|
||||
|
||||
private fun addSubtype(name: String) {
|
||||
val newSubtype = AdditionalSubtypeUtils.createEmojiCapableAdditionalSubtype(mainLocaleString, name, infos.first().subtype.isAsciiCapable)
|
||||
val newSubtype = AdditionalSubtypeUtils.createEmojiCapableAdditionalSubtype(mainLocale, name, infos.first().subtype.isAsciiCapable)
|
||||
val newSubtypeInfo = newSubtype.toSubtypeInfo(mainLocale, context, true, infos.first().hasDictionary) // enabled by default
|
||||
val displayName = SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(newSubtype)
|
||||
val old = infos.firstOrNull { isAdditionalSubtype(it.subtype) && displayName == SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(it.subtype) }
|
||||
|
@ -148,14 +149,15 @@ class LanguageSettingsDialog(
|
|||
.setItems(displayNames.toTypedArray()) { di, i ->
|
||||
di.dismiss()
|
||||
val fileName = context.assets.list("layouts")!!.firstOrNull { it.startsWith(layouts[i]) } ?: return@setItems
|
||||
loadCustomLayout(context.assets.open("layouts${File.separator}$fileName").reader().readText(), displayNames[i], mainLocaleString, context) { addSubtype(it) }
|
||||
loadCustomLayout(context.assets.open("layouts${File.separator}$fileName").reader().readText(),
|
||||
displayNames[i], mainLocale.toLanguageTag(), context) { addSubtype(it) }
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun onNewLayoutFile(uri: Uri?) {
|
||||
loadCustomLayout(uri, mainLocaleString, context) { addSubtype(it) }
|
||||
loadCustomLayout(uri, mainLocale.toLanguageTag(), context) { addSubtype(it) }
|
||||
}
|
||||
|
||||
private fun addSubtypeToView(subtype: SubtypeInfo) {
|
||||
|
@ -215,10 +217,10 @@ class LanguageSettingsDialog(
|
|||
// can only use multilingual typing if there is more than one dictionary available
|
||||
val availableSecondaryLocales = getAvailableSecondaryLocales(
|
||||
context,
|
||||
mainLocaleString,
|
||||
mainLocale,
|
||||
infos.first().subtype.isAsciiCapable
|
||||
)
|
||||
val selectedSecondaryLocales = Settings.getSecondaryLocales(prefs, mainLocaleString)
|
||||
val selectedSecondaryLocales = Settings.getSecondaryLocales(prefs, mainLocale)
|
||||
selectedSecondaryLocales.forEach {
|
||||
addSecondaryLocaleView(it)
|
||||
}
|
||||
|
@ -226,14 +228,14 @@ class LanguageSettingsDialog(
|
|||
binding.addSecondaryLanguage.apply {
|
||||
isVisible = true
|
||||
setOnClickListener {
|
||||
val locales = (availableSecondaryLocales - Settings.getSecondaryLocales(prefs, mainLocaleString)).sortedBy { it.displayName }
|
||||
val locales = (availableSecondaryLocales - Settings.getSecondaryLocales(prefs, mainLocale)).sortedBy { it.displayName }
|
||||
val localeNames = locales.map { LocaleUtils.getLocaleDisplayNameInSystemLocale(it, context) }.toTypedArray()
|
||||
Builder(context)
|
||||
.setTitle(R.string.button_select_language)
|
||||
.setItems(localeNames) { di, i ->
|
||||
val locale = locales[i]
|
||||
val localeStrings = Settings.getSecondaryLocales(prefs, mainLocaleString).map { it.toString() }
|
||||
Settings.setSecondaryLocales(prefs, mainLocaleString, localeStrings + locale.toString())
|
||||
val currentSecondaryLocales = Settings.getSecondaryLocales(prefs, mainLocale)
|
||||
Settings.setSecondaryLocales(prefs, mainLocale, currentSecondaryLocales + locale)
|
||||
addSecondaryLocaleView(locale)
|
||||
di.dismiss()
|
||||
reloadSetting()
|
||||
|
@ -256,8 +258,8 @@ class LanguageSettingsDialog(
|
|||
rowBinding.deleteButton.apply {
|
||||
isVisible = true
|
||||
setOnClickListener {
|
||||
val localeStrings = Settings.getSecondaryLocales(prefs, mainLocaleString).map { it.toString() }
|
||||
Settings.setSecondaryLocales(prefs, mainLocaleString, localeStrings - locale.toString())
|
||||
val currentSecondaryLocales = Settings.getSecondaryLocales(prefs, mainLocale)
|
||||
Settings.setSecondaryLocales(prefs, mainLocale, currentSecondaryLocales - locale)
|
||||
binding.secondaryLocales.removeView(rowBinding.root)
|
||||
reloadSetting()
|
||||
reloadDictionaries()
|
||||
|
@ -280,7 +282,7 @@ class LanguageSettingsDialog(
|
|||
dialog.show()
|
||||
(dialog.findViewById<View>(android.R.id.message) as? TextView)?.movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
val userDictsAndHasInternal = getUserAndInternalDictionaries(context, mainLocaleString)
|
||||
val userDictsAndHasInternal = getUserAndInternalDictionaries(context, mainLocale)
|
||||
hasInternalDictForLanguage = userDictsAndHasInternal.second
|
||||
userDicts.addAll(userDictsAndHasInternal.first)
|
||||
if (hasInternalDictForLanguage) {
|
||||
|
@ -330,11 +332,7 @@ class LanguageSettingsDialog(
|
|||
}
|
||||
rowBinding.languageText.setOnClickListener {
|
||||
if (header == null) return@setOnClickListener
|
||||
val locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
context.resources.configuration.locales[0]
|
||||
} else {
|
||||
@Suppress("Deprecation") context.resources.configuration.locale
|
||||
}
|
||||
val locale = context.resources.configuration.locale()
|
||||
Builder(context)
|
||||
.setMessage(header.info(locale))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
|
@ -369,12 +367,12 @@ class LanguageSettingsDialog(
|
|||
private fun setupPopupSettings() {
|
||||
binding.popupOrder.setOnClickListener {
|
||||
val moreKeyTypesDefault = prefs.getString(Settings.PREF_MORE_KEYS_ORDER, MORE_KEYS_ORDER_DEFAULT)!!
|
||||
reorderMoreKeysDialog(context, Settings.PREF_MORE_KEYS_ORDER + "_" + mainLocaleString, moreKeyTypesDefault, R.string.popup_order)
|
||||
reorderMoreKeysDialog(context, Settings.PREF_MORE_KEYS_ORDER + "_" + mainLocale.toLanguageTag(), moreKeyTypesDefault, R.string.popup_order)
|
||||
KeyboardLayoutSet.onKeyboardThemeChanged()
|
||||
}
|
||||
binding.popupLabelPriority.setOnClickListener {
|
||||
val moreKeyTypesDefault = prefs.getString(Settings.PREF_MORE_KEYS_LABELS_ORDER, MORE_KEYS_LABEL_DEFAULT)!!
|
||||
reorderMoreKeysDialog(context, Settings.PREF_MORE_KEYS_LABELS_ORDER + "_" + mainLocaleString, moreKeyTypesDefault, R.string.hint_source)
|
||||
reorderMoreKeysDialog(context, Settings.PREF_MORE_KEYS_LABELS_ORDER + "_" + mainLocale.toLanguageTag(), moreKeyTypesDefault, R.string.hint_source)
|
||||
KeyboardLayoutSet.onKeyboardThemeChanged()
|
||||
}
|
||||
}
|
||||
|
@ -383,11 +381,10 @@ class LanguageSettingsDialog(
|
|||
}
|
||||
|
||||
/** @return list of user dictionary files and whether an internal dictionary exists */
|
||||
fun getUserAndInternalDictionaries(context: Context, locale: String): Pair<List<File>, Boolean> {
|
||||
val localeString = locale.lowercase() // internal files and folders always use lowercase
|
||||
fun getUserAndInternalDictionaries(context: Context, locale: Locale): Pair<List<File>, Boolean> {
|
||||
val userDicts = mutableListOf<File>()
|
||||
var hasInternalDict = false
|
||||
val userLocaleDir = File(DictionaryInfoUtils.getWordListCacheDirectory(context), localeString)
|
||||
val userLocaleDir = File(DictionaryInfoUtils.getCacheDirectoryForLocale(locale, context))
|
||||
if (userLocaleDir.exists() && userLocaleDir.isDirectory) {
|
||||
userLocaleDir.listFiles()?.forEach {
|
||||
if (it.name.endsWith(USER_DICTIONARY_SUFFIX))
|
||||
|
@ -398,37 +395,24 @@ fun getUserAndInternalDictionaries(context: Context, locale: String): Pair<List<
|
|||
}
|
||||
if (hasInternalDict)
|
||||
return userDicts to true
|
||||
val language = localeString.languageConsideringZZ()
|
||||
BinaryDictionaryGetter.getAssetsDictionaryList(context)?.forEach { dictFile ->
|
||||
BinaryDictionaryGetter.extractLocaleFromAssetsDictionaryFile(dictFile)?.let {
|
||||
if (it == localeString || it.languageConsideringZZ() == language)
|
||||
return userDicts to true
|
||||
}
|
||||
val internalDicts = DictionaryInfoUtils.getAssetsDictionaryList(context) ?: return userDicts to false
|
||||
val best = LocaleUtils.getBestMatch(locale, internalDicts.toList()) {
|
||||
DictionaryInfoUtils.extractLocaleFromAssetsDictionaryFile(it)?.constructLocale() ?: SubtypeLocaleUtils.NO_LANGUAGE.constructLocale()
|
||||
}
|
||||
return userDicts to false
|
||||
}
|
||||
|
||||
private fun String.languageConsideringZZ(): String {
|
||||
return if (endsWith("zz", false))
|
||||
this
|
||||
else
|
||||
substringBefore("_")
|
||||
return userDicts to (best != null)
|
||||
}
|
||||
|
||||
// get locales with same script as main locale, but different language
|
||||
private fun getAvailableSecondaryLocales(context: Context, mainLocaleString: String, asciiCapable: Boolean): Set<Locale> {
|
||||
val mainLocale = mainLocaleString.toLocale()
|
||||
private fun getAvailableSecondaryLocales(context: Context, mainLocale: Locale, asciiCapable: Boolean): Set<Locale> {
|
||||
val locales = getDictionaryLocales(context)
|
||||
val mainScript = if (asciiCapable) ScriptUtils.SCRIPT_LATIN
|
||||
else ScriptUtils.getScriptFromSpellCheckerLocale(mainLocale)
|
||||
// ScriptUtils.getScriptFromSpellCheckerLocale may return latin when it should not
|
||||
// e.g. for persian or chinese
|
||||
else mainLocale.script()
|
||||
// script() extension function may return latin in case script cannot be determined
|
||||
// workaround: don't allow secondary locales for these locales
|
||||
if (!asciiCapable && mainScript == ScriptUtils.SCRIPT_LATIN) return emptySet()
|
||||
|
||||
locales.removeAll {
|
||||
it.language == mainLocale.language
|
||||
|| ScriptUtils.getScriptFromSpellCheckerLocale(it) != mainScript
|
||||
it.language == mainLocale.language || it.script() != mainScript
|
||||
}
|
||||
return locales
|
||||
}
|
||||
|
|
|
@ -19,8 +19,10 @@ import androidx.core.content.edit
|
|||
import androidx.fragment.app.Fragment
|
||||
import org.dslul.openboard.inputmethod.latin.R
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils.constructLocale
|
||||
import org.dslul.openboard.inputmethod.latin.utils.DeviceProtectedUtils
|
||||
import org.dslul.openboard.inputmethod.latin.utils.DictionaryInfoUtils
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils.script
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeLocaleUtils
|
||||
import org.dslul.openboard.inputmethod.latin.utils.getAllAvailableSubtypes
|
||||
import org.dslul.openboard.inputmethod.latin.utils.getDictionaryLocales
|
||||
|
@ -32,13 +34,13 @@ import java.util.*
|
|||
// not a SettingsFragment, because with androidx.preferences it's very complicated or
|
||||
// impossible to have the languages RecyclerView scrollable (this way it works nicely out of the box)
|
||||
class LanguageSettingsFragment : Fragment(R.layout.language_settings) {
|
||||
private val sortedSubtypes = LinkedHashMap<String, MutableList<SubtypeInfo>>()
|
||||
private val sortedSubtypesByDisplayName = LinkedHashMap<String, MutableList<SubtypeInfo>>()
|
||||
private val enabledSubtypes = mutableListOf<InputMethodSubtype>()
|
||||
private val systemLocales = mutableListOf<Locale>()
|
||||
private lateinit var languageFilterList: LanguageFilterList
|
||||
private lateinit var sharedPreferences: SharedPreferences
|
||||
private lateinit var systemOnlySwitch: Switch
|
||||
private val dictionaryLocales by lazy { getDictionaryLocales(requireContext()).mapTo(HashSet()) { it.languageConsideringZZ() } }
|
||||
private val dictionaryLocales by lazy { getDictionaryLocales(requireContext()) }
|
||||
|
||||
private val dictionaryFilePicker = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode != Activity.RESULT_OK) return@registerForActivityResult
|
||||
|
@ -62,11 +64,7 @@ class LanguageSettingsFragment : Fragment(R.layout.language_settings) {
|
|||
systemLocales.addAll(getSystemLocales())
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view = super.onCreateView(inflater, container, savedInstanceState) ?: return null
|
||||
systemOnlySwitch = view.findViewById(R.id.language_switch)
|
||||
systemOnlySwitch.isChecked = sharedPreferences.getBoolean(Settings.PREF_USE_SYSTEM_LOCALES, true)
|
||||
|
@ -97,46 +95,47 @@ class LanguageSettingsFragment : Fragment(R.layout.language_settings) {
|
|||
}
|
||||
|
||||
private fun loadSubtypes(systemOnly: Boolean) {
|
||||
sortedSubtypes.clear()
|
||||
sortedSubtypesByDisplayName.clear()
|
||||
// list of all subtypes, any subtype added to sortedSubtypes will be removed to avoid duplicates
|
||||
val allSubtypes = getAllAvailableSubtypes().toMutableList()
|
||||
// todo: re-write this, it's hard to understand
|
||||
// also consider that more _ZZ languages might be added
|
||||
fun List<Locale>.sortedAddToSubtypesAndRemoveFromAllSubtypes() {
|
||||
val subtypesToAdd = mutableListOf<SubtypeInfo>()
|
||||
forEach { locale ->
|
||||
val localeString = locale.toString()
|
||||
val iterator = allSubtypes.iterator()
|
||||
var added = false
|
||||
while (iterator.hasNext()) {
|
||||
val subtype = iterator.next()
|
||||
if (subtype.locale() == localeString) {
|
||||
if (subtype.locale() == locale) {
|
||||
// add subtypes with matching locale
|
||||
subtypesToAdd.add(subtype.toSubtypeInfo(locale))
|
||||
iterator.remove()
|
||||
added = true
|
||||
}
|
||||
}
|
||||
// try again, but with language only
|
||||
// if locale has a country try again, but match language and script only
|
||||
if (!added && locale.country.isNotEmpty()) {
|
||||
val languageString = locale.language
|
||||
val language = locale.language
|
||||
val script = locale.script()
|
||||
val iter = allSubtypes.iterator()
|
||||
while (iter.hasNext()) {
|
||||
val subtype = iter.next()
|
||||
if (subtype.locale() == languageString) {
|
||||
subtypesToAdd.add(subtype.toSubtypeInfo(LocaleUtils.constructLocaleFromString(languageString)))
|
||||
val subtypeLocale = subtype.locale()
|
||||
if (subtypeLocale.toLanguageTag() == subtypeLocale.language && subtypeLocale.language == language && script == subtypeLocale.script()) {
|
||||
// add subtypes using the language only
|
||||
subtypesToAdd.add(subtype.toSubtypeInfo(language.constructLocale()))
|
||||
iter.remove()
|
||||
added = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// special treatment for the known languages with _ZZ types
|
||||
if (!added && (locale.language == "sr" || locale.language == "hi")) {
|
||||
val languageString = locale.language
|
||||
// try again if script is not the default script, match language only
|
||||
if (!added && locale.script() != locale.language.constructLocale().script()) {
|
||||
val language = locale.language
|
||||
val iter = allSubtypes.iterator()
|
||||
while (iter.hasNext()) {
|
||||
val subtype = iter.next()
|
||||
if (subtype.locale().substringBefore("_") == languageString) {
|
||||
subtypesToAdd.add(subtype.toSubtypeInfo(LocaleUtils.constructLocaleFromString(subtype.locale())))
|
||||
if (subtype.locale().language == language) {
|
||||
subtypesToAdd.add(subtype.toSubtypeInfo(subtype.locale()))
|
||||
iter.remove()
|
||||
}
|
||||
}
|
||||
|
@ -146,12 +145,12 @@ class LanguageSettingsFragment : Fragment(R.layout.language_settings) {
|
|||
}
|
||||
|
||||
// add enabled subtypes
|
||||
enabledSubtypes.map { it.toSubtypeInfo(LocaleUtils.constructLocaleFromString(it.locale()), true) }
|
||||
enabledSubtypes.map { it.toSubtypeInfo(it.locale(), true) }
|
||||
.sortedBy { it.displayName }.addToSortedSubtypes()
|
||||
allSubtypes.removeAll(enabledSubtypes)
|
||||
|
||||
if (systemOnly) { // don't add anything else
|
||||
languageFilterList.setLanguages(sortedSubtypes.values, systemOnly)
|
||||
languageFilterList.setLanguages(sortedSubtypesByDisplayName.values, systemOnly)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -160,7 +159,7 @@ class LanguageSettingsFragment : Fragment(R.layout.language_settings) {
|
|||
if (!dir.isDirectory)
|
||||
return@mapNotNull null
|
||||
if (dir.list()?.any { it.endsWith(USER_DICTIONARY_SUFFIX) } == true)
|
||||
LocaleUtils.constructLocaleFromString(dir.name)
|
||||
dir.name.constructLocale()
|
||||
else null
|
||||
}
|
||||
localesWithDictionary?.sortedAddToSubtypesAndRemoveFromAllSubtypes()
|
||||
|
@ -169,22 +168,22 @@ class LanguageSettingsFragment : Fragment(R.layout.language_settings) {
|
|||
systemLocales.sortedAddToSubtypesAndRemoveFromAllSubtypes()
|
||||
|
||||
// add the remaining ones
|
||||
allSubtypes.map { it.toSubtypeInfo(LocaleUtils.constructLocaleFromString(it.locale())) }
|
||||
.sortedBy { if (it.subtype.locale().equals("zz", true))
|
||||
"zz" // "No language (Alphabet)" should be last
|
||||
allSubtypes.map { it.toSubtypeInfo(it.locale()) }
|
||||
.sortedBy { if (it.subtype.locale().toLanguageTag().equals(SubtypeLocaleUtils.NO_LANGUAGE, true))
|
||||
SubtypeLocaleUtils.NO_LANGUAGE // "No language (Alphabet)" should be last
|
||||
else it.displayName
|
||||
}.addToSortedSubtypes()
|
||||
|
||||
// set languages
|
||||
languageFilterList.setLanguages(sortedSubtypes.values, systemOnly)
|
||||
languageFilterList.setLanguages(sortedSubtypesByDisplayName.values, systemOnly)
|
||||
}
|
||||
|
||||
private fun InputMethodSubtype.toSubtypeInfo(locale: Locale, isEnabled: Boolean = false) =
|
||||
toSubtypeInfo(locale, requireContext(), isEnabled, dictionaryLocales.contains(locale.languageConsideringZZ()))
|
||||
toSubtypeInfo(locale, requireContext(), isEnabled, LocaleUtils.getBestMatch(locale, dictionaryLocales) {it} != null)
|
||||
|
||||
private fun List<SubtypeInfo>.addToSortedSubtypes() {
|
||||
forEach {
|
||||
sortedSubtypes.getOrPut(it.displayName) { mutableListOf() }.add(it)
|
||||
sortedSubtypesByDisplayName.getOrPut(it.displayName) { mutableListOf() }.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,11 +229,4 @@ class SubtypeInfo(val displayName: String, val subtype: InputMethodSubtype, var
|
|||
fun InputMethodSubtype.toSubtypeInfo(locale: Locale, context: Context, isEnabled: Boolean, hasDictionary: Boolean): SubtypeInfo =
|
||||
SubtypeInfo(LocaleUtils.getLocaleDisplayNameInSystemLocale(locale, context), this, isEnabled, hasDictionary)
|
||||
|
||||
private fun Locale.languageConsideringZZ(): String {
|
||||
return if (country.equals("zz", false))
|
||||
"${language}_zz"
|
||||
else
|
||||
language
|
||||
}
|
||||
|
||||
const val USER_DICTIONARY_SUFFIX = "user.dict"
|
||||
|
|
|
@ -526,7 +526,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
|
|||
return name;
|
||||
}
|
||||
}
|
||||
return ScriptUtils.getScriptFromSpellCheckerLocale(locale) == ScriptUtils.SCRIPT_ARABIC ? "symbols_arabic" : "symbols";
|
||||
return ScriptUtils.script(locale).equals(ScriptUtils.SCRIPT_ARABIC) ? "symbols_arabic" : "symbols";
|
||||
}
|
||||
|
||||
public static String readShiftedSymbolsLayoutName(final Context context) {
|
||||
|
@ -571,27 +571,27 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
|
|||
sCachedBackgroundNight = null;
|
||||
}
|
||||
|
||||
public static List<Locale> getSecondaryLocales(final SharedPreferences prefs, final String mainLocaleString) {
|
||||
final String localesString = prefs.getString(PREF_SECONDARY_LOCALES_PREFIX + mainLocaleString.toLowerCase(Locale.ROOT), "");
|
||||
public static List<Locale> getSecondaryLocales(final SharedPreferences prefs, final Locale mainLocale) {
|
||||
final String localesString = prefs.getString(PREF_SECONDARY_LOCALES_PREFIX + mainLocale.toLanguageTag(), "");
|
||||
|
||||
final ArrayList<Locale> locales = new ArrayList<>();
|
||||
for (String locale : localesString.split(";")) {
|
||||
if (locale.isEmpty()) continue;
|
||||
locales.add(LocaleUtils.constructLocaleFromString(locale));
|
||||
for (String languageTag : localesString.split(";")) {
|
||||
if (languageTag.isEmpty()) continue;
|
||||
locales.add(LocaleUtils.constructLocale(languageTag));
|
||||
}
|
||||
return locales;
|
||||
}
|
||||
|
||||
public static void setSecondaryLocales(final SharedPreferences prefs, final String mainLocaleString, final List<String> locales) {
|
||||
public static void setSecondaryLocales(final SharedPreferences prefs, final Locale mainLocale, final List<Locale> locales) {
|
||||
if (locales.isEmpty()) {
|
||||
prefs.edit().putString(PREF_SECONDARY_LOCALES_PREFIX + mainLocaleString.toLowerCase(Locale.ROOT), "").apply();
|
||||
prefs.edit().putString(PREF_SECONDARY_LOCALES_PREFIX + mainLocale, "").apply();
|
||||
return;
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (String locale : locales) {
|
||||
sb.append(";").append(locale);
|
||||
for (Locale locale : locales) {
|
||||
sb.append(";").append(locale.toLanguageTag());
|
||||
}
|
||||
prefs.edit().putString(PREF_SECONDARY_LOCALES_PREFIX + mainLocaleString.toLowerCase(Locale.ROOT), sb.toString()).apply();
|
||||
prefs.edit().putString(PREF_SECONDARY_LOCALES_PREFIX + mainLocale, sb.toString()).apply();
|
||||
}
|
||||
|
||||
public static Colors getColorsForCurrentTheme(final Context context, final SharedPreferences prefs) {
|
||||
|
|
|
@ -16,6 +16,7 @@ import android.view.inputmethod.InputMethodSubtype;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.dslul.openboard.inputmethod.compat.ConfigurationCompatKt;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.LocaleKeyTextsKt;
|
||||
import org.dslul.openboard.inputmethod.latin.InputAttributes;
|
||||
import org.dslul.openboard.inputmethod.latin.R;
|
||||
|
@ -27,6 +28,7 @@ import org.dslul.openboard.inputmethod.latin.utils.Log;
|
|||
import org.dslul.openboard.inputmethod.latin.utils.MoreKeysUtilsKt;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeSettingsKt;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeUtilsKt;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
@ -127,7 +129,7 @@ public class SettingsValues {
|
|||
// creation of Colors and SpacingAndPunctuations are the slowest parts in here, but still ok
|
||||
public SettingsValues(final Context context, final SharedPreferences prefs, final Resources res,
|
||||
@NonNull final InputAttributes inputAttributes) {
|
||||
mLocale = res.getConfiguration().locale;
|
||||
mLocale = ConfigurationCompatKt.locale(res.getConfiguration());
|
||||
|
||||
// Store the input attributes
|
||||
mInputAttributes = inputAttributes;
|
||||
|
@ -207,7 +209,7 @@ public class SettingsValues {
|
|||
} else
|
||||
mOneHandedModeScale = 1f;
|
||||
final InputMethodSubtype selectedSubtype = SubtypeSettingsKt.getSelectedSubtype(prefs);
|
||||
mSecondaryLocales = Settings.getSecondaryLocales(prefs, selectedSubtype.getLocale());
|
||||
mSecondaryLocales = Settings.getSecondaryLocales(prefs, SubtypeUtilsKt.locale(selectedSubtype));
|
||||
mShowMoreMoreKeys = selectedSubtype.isAsciiCapable()
|
||||
? Settings.readMoreMoreKeysPref(prefs)
|
||||
: LocaleKeyTextsKt.MORE_KEYS_NORMAL;
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.dslul.openboard.inputmethod.latin.settings;
|
|||
|
||||
import android.content.res.Resources;
|
||||
|
||||
import org.dslul.openboard.inputmethod.compat.ConfigurationCompatKt;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.MoreKeySpec;
|
||||
import org.dslul.openboard.inputmethod.latin.PunctuationSuggestions;
|
||||
import org.dslul.openboard.inputmethod.latin.R;
|
||||
|
@ -50,7 +51,7 @@ public final class SpacingAndPunctuations {
|
|||
mCurrentLanguageHasSpaces = res.getBoolean(R.bool.current_language_has_spaces);
|
||||
// make it empty if language doesn't have spaces, to avoid weird glitches
|
||||
mSortedSometimesWordConnectors = (urlDetection && mCurrentLanguageHasSpaces) ? StringUtils.toSortedCodePointArray(res.getString(R.string.symbols_sometimes_word_connectors)) : new int[0];
|
||||
final Locale locale = res.getConfiguration().locale;
|
||||
final Locale locale = ConfigurationCompatKt.locale(res.getConfiguration());
|
||||
// Heuristic: we use American Typography rules because it's the most common rules for all
|
||||
// English variants. German rules (not "German typography") also have small gotchas.
|
||||
mUsesAmericanTypography = Locale.ENGLISH.getLanguage().equals(locale.getLanguage());
|
||||
|
|
|
@ -22,10 +22,12 @@ import android.widget.EditText;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.dslul.openboard.inputmethod.compat.ConfigurationCompatKt;
|
||||
import org.dslul.openboard.inputmethod.latin.R;
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class UserDictionaryAddWordContents {
|
||||
|
@ -49,7 +51,7 @@ public class UserDictionaryAddWordContents {
|
|||
private final EditText mWordEditText;
|
||||
private final EditText mShortcutEditText;
|
||||
private final EditText mWeightEditText;
|
||||
private String mLocaleString;
|
||||
private Locale mLocale;
|
||||
private final String mOldWord;
|
||||
private final String mOldShortcut;
|
||||
private final String mOldWeight;
|
||||
|
@ -94,7 +96,8 @@ public class UserDictionaryAddWordContents {
|
|||
|
||||
mOldWord = args.getString(EXTRA_WORD);
|
||||
mOldWeight = args.getString(EXTRA_WEIGHT);
|
||||
updateLocale(mContext, args.getString(EXTRA_LOCALE));
|
||||
final String extraLocale = args.getString(EXTRA_LOCALE);
|
||||
updateLocale(mContext, extraLocale == null ? null : LocaleUtils.constructLocale(extraLocale));
|
||||
}
|
||||
|
||||
UserDictionaryAddWordContents(final View view, final UserDictionaryAddWordContents oldInstanceToBeEdited) {
|
||||
|
@ -105,26 +108,26 @@ public class UserDictionaryAddWordContents {
|
|||
mOldWord = oldInstanceToBeEdited.mSavedWord;
|
||||
mOldShortcut = oldInstanceToBeEdited.mSavedShortcut;
|
||||
mOldWeight = oldInstanceToBeEdited.mSavedWeight;
|
||||
updateLocale(mContext, mLocaleString);
|
||||
updateLocale(mContext, mLocale);
|
||||
}
|
||||
|
||||
// locale may be null, this means system locale
|
||||
// It may also be the empty string, which means "For all languages"
|
||||
void updateLocale(final Context context, final String locale) {
|
||||
void updateLocale(final Context context, @Nullable final Locale locale) {
|
||||
mContext = context;
|
||||
|
||||
mLocaleString = null == locale
|
||||
? mContext.getResources().getConfiguration().locale.toString()
|
||||
mLocale = null == locale
|
||||
? ConfigurationCompatKt.locale(mContext.getResources().getConfiguration())
|
||||
: locale;
|
||||
// The keyboard uses the language layout of the user dictionary
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
mWordEditText.setImeHintLocales(new LocaleList(LocaleUtils.constructLocaleFromString(mLocaleString)));
|
||||
mWordEditText.setImeHintLocales(new LocaleList(mLocale));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
String getLocale() {
|
||||
return mLocaleString;
|
||||
Locale getLocale() {
|
||||
return mLocale;
|
||||
}
|
||||
|
||||
void delete(final Context context) {
|
||||
|
@ -133,13 +136,14 @@ public class UserDictionaryAddWordContents {
|
|||
return;
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
// Remove the old entry.
|
||||
UserDictionarySettings.deleteWordInEditMode(mOldWord, mOldShortcut, mOldWeight, mLocaleString, resolver);
|
||||
UserDictionarySettings.deleteWordInEditMode(mOldWord, mOldShortcut, mOldWeight, mLocale.toString(), resolver);
|
||||
}
|
||||
|
||||
// requires use of locale string for interaction with Android system
|
||||
public final int apply(@NonNull final Context context) {
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final String newWord = mWordEditText.getText().toString();
|
||||
final String locale = mLocaleString;
|
||||
final String localeString = mLocale.toString();
|
||||
|
||||
if (TextUtils.isEmpty(newWord)) {
|
||||
// If the word is empty, don't insert it.
|
||||
|
@ -159,35 +163,33 @@ public class UserDictionaryAddWordContents {
|
|||
mSavedWord = newWord;
|
||||
|
||||
// In edit mode, everything is modified without overwriting other existing words
|
||||
if (MODE_EDIT == mMode && hasWord(newWord, locale, context) && newWord.equals(mOldWord)) {
|
||||
UserDictionarySettings.deleteWordInEditMode(mOldWord, mOldShortcut, mOldWeight, locale, resolver);
|
||||
if (MODE_EDIT == mMode && hasWord(newWord, localeString, context) && newWord.equals(mOldWord)) {
|
||||
UserDictionarySettings.deleteWordInEditMode(mOldWord, mOldShortcut, mOldWeight, localeString, resolver);
|
||||
} else {
|
||||
mMode = MODE_INSERT;
|
||||
}
|
||||
|
||||
if (mMode == MODE_INSERT && hasWord(newWord, locale, context)) {
|
||||
if (mMode == MODE_INSERT && hasWord(newWord, localeString, context)) {
|
||||
return CODE_ALREADY_PRESENT;
|
||||
}
|
||||
|
||||
if (mMode == MODE_INSERT) {
|
||||
// Delete duplicate when adding or updating new word
|
||||
UserDictionarySettings.deleteWordInEditMode(mOldWord, mOldShortcut, mOldWeight, locale, resolver);
|
||||
UserDictionarySettings.deleteWordInEditMode(mOldWord, mOldShortcut, mOldWeight, localeString, resolver);
|
||||
// Update the existing word by adding a new one
|
||||
UserDictionary.Words.addWord(context, newWord,
|
||||
Integer.parseInt(mSavedWeight), mSavedShortcut, TextUtils.isEmpty(mLocaleString) ?
|
||||
null : LocaleUtils.constructLocaleFromString(mLocaleString));
|
||||
UserDictionary.Words.addWord(context, newWord, Integer.parseInt(mSavedWeight),
|
||||
mSavedShortcut, TextUtils.isEmpty(mLocale.toString()) ? null : mLocale);
|
||||
|
||||
return CODE_UPDATED;
|
||||
}
|
||||
|
||||
// Delete duplicates
|
||||
UserDictionarySettings.deleteWord(newWord, locale, resolver);
|
||||
UserDictionarySettings.deleteWord(newWord, localeString, resolver);
|
||||
|
||||
// In this class we use the empty string to represent 'all locales' and mLocale cannot
|
||||
// be null. However the addWord method takes null to mean 'all locales'.
|
||||
UserDictionary.Words.addWord(context, newWord,
|
||||
Integer.parseInt(mSavedWeight), mSavedShortcut, TextUtils.isEmpty(mLocaleString) ?
|
||||
null : LocaleUtils.constructLocaleFromString(mLocaleString));
|
||||
UserDictionary.Words.addWord(context, newWord, Integer.parseInt(mSavedWeight),
|
||||
mSavedShortcut, TextUtils.isEmpty(mLocale.toString()) ? null : mLocale);
|
||||
|
||||
return CODE_WORD_ADDED;
|
||||
}
|
||||
|
@ -195,7 +197,7 @@ public class UserDictionaryAddWordContents {
|
|||
public boolean isExistingWord(final Context context) {
|
||||
final String newWord = mWordEditText.getText().toString();
|
||||
if (mMode != MODE_EDIT) {
|
||||
return hasWord(newWord, mLocaleString, context);
|
||||
return hasWord(newWord, mLocale.toString(), context);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -207,17 +209,18 @@ public class UserDictionaryAddWordContents {
|
|||
private static final String HAS_WORD_AND_ALL_LOCALES_SELECTION = UserDictionary.Words.WORD + "=? AND "
|
||||
+ UserDictionary.Words.LOCALE + " is null";
|
||||
|
||||
private boolean hasWord(final String word, final String locale, final Context context) {
|
||||
// requires use of locale string for interaction with Android system
|
||||
private boolean hasWord(final String word, final String localeString, final Context context) {
|
||||
final Cursor cursor;
|
||||
|
||||
if ("".equals(locale)) {
|
||||
if ("".equals(localeString)) {
|
||||
cursor = context.getContentResolver().query(UserDictionary.Words.CONTENT_URI,
|
||||
HAS_WORD_PROJECTION, HAS_WORD_AND_ALL_LOCALES_SELECTION,
|
||||
new String[] { word }, null);
|
||||
} else {
|
||||
cursor = context.getContentResolver().query(UserDictionary.Words.CONTENT_URI,
|
||||
HAS_WORD_PROJECTION, HAS_WORD_AND_LOCALE_SELECTION,
|
||||
new String[] { word, locale}, null);
|
||||
new String[] { word, localeString}, null);
|
||||
}
|
||||
try {
|
||||
if (null == cursor) return false;
|
||||
|
@ -230,28 +233,34 @@ public class UserDictionaryAddWordContents {
|
|||
public static class LocaleRenderer {
|
||||
private final String mLocaleString;
|
||||
private final String mDescription;
|
||||
private final Locale mLocale;
|
||||
|
||||
public LocaleRenderer(final Context context, @Nullable final String localeString) {
|
||||
mLocaleString = localeString;
|
||||
public LocaleRenderer(final Context context, @NonNull final Locale locale) {
|
||||
mLocaleString = locale.toString();
|
||||
mLocale = locale;
|
||||
|
||||
if (null == localeString || "".equals(localeString)) {
|
||||
if ("".equals(locale.toString())) {
|
||||
mDescription = context.getString(R.string.user_dict_settings_all_languages);
|
||||
} else {
|
||||
mDescription = UserDictionarySettings.getLocaleDisplayName(context, localeString);
|
||||
mDescription = UserDictionarySettings.getLocaleDisplayName(context, locale);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
// used in ArrayAdapter of spinner in UserDictionaryAddWordFragment
|
||||
public String toString() {
|
||||
return mDescription;
|
||||
}
|
||||
public String getLocaleString() {
|
||||
return mLocaleString;
|
||||
}
|
||||
public Locale getLocale() {
|
||||
return mLocale;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void addLocaleDisplayNameToList(final Context context,
|
||||
final ArrayList<LocaleRenderer> list, final String locale) {
|
||||
final ArrayList<LocaleRenderer> list, final Locale locale) {
|
||||
if (null != locale) {
|
||||
list.add(new LocaleRenderer(context, locale));
|
||||
}
|
||||
|
@ -259,26 +268,26 @@ public class UserDictionaryAddWordContents {
|
|||
|
||||
// Helper method to get the list of locales and subtypes to display for this word
|
||||
public ArrayList<LocaleRenderer> getLocaleRendererList(final Context context) {
|
||||
final TreeSet<String> sortedLanguages = UserDictionaryListFragment.getSortedDictionaryLocaleStrings(context);
|
||||
final TreeSet<Locale> sortedLocales = UserDictionaryListFragment.getSortedDictionaryLocales(context);
|
||||
|
||||
// mLocale is removed from the language list as it will be added to the top of the list
|
||||
sortedLanguages.remove(mLocaleString);
|
||||
sortedLocales.remove(mLocale);
|
||||
// "For all languages" is removed from the language list as it will be added at the end of the list
|
||||
sortedLanguages.remove("");
|
||||
sortedLocales.remove(new Locale(""));
|
||||
|
||||
// final list of locales to show
|
||||
final ArrayList<LocaleRenderer> localesList = new ArrayList<>();
|
||||
// First, add the language of the personal dictionary at the top of the list
|
||||
addLocaleDisplayNameToList(context, localesList, mLocaleString);
|
||||
addLocaleDisplayNameToList(context, localesList, mLocale);
|
||||
|
||||
// Next, add all other languages which will be sorted alphabetically in UserDictionaryAddWordFragment.updateSpinner()
|
||||
for (String language : sortedLanguages) {
|
||||
addLocaleDisplayNameToList(context, localesList, language);
|
||||
for (Locale locale : sortedLocales) {
|
||||
addLocaleDisplayNameToList(context, localesList, locale);
|
||||
}
|
||||
|
||||
// Finally, add "All languages" at the end of the list
|
||||
if (!"".equals(mLocaleString)) {
|
||||
addLocaleDisplayNameToList(context, localesList, "");
|
||||
if (!"".equals(mLocale.toString())) {
|
||||
addLocaleDisplayNameToList(context, localesList, new Locale(""));
|
||||
}
|
||||
|
||||
return localesList;
|
||||
|
|
|
@ -32,6 +32,7 @@ import androidx.core.graphics.drawable.DrawableKt;
|
|||
import androidx.core.widget.TextViewKt;
|
||||
|
||||
import org.dslul.openboard.inputmethod.latin.R;
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.settings.UserDictionaryAddWordContents.LocaleRenderer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -165,22 +166,22 @@ public class UserDictionaryAddWordFragment extends SubScreenFragment {
|
|||
localeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
final LocaleRenderer locale = (LocaleRenderer)parent.getItemAtPosition(position);
|
||||
final LocaleRenderer localeRenderer = (LocaleRenderer)parent.getItemAtPosition(position);
|
||||
|
||||
mContents.updateLocale(requireContext(), locale.getLocaleString());
|
||||
mContents.updateLocale(requireContext(), localeRenderer.getLocale());
|
||||
// To have the selected language at the top of the list, this one is removed from the list
|
||||
localesList.remove(position);
|
||||
// The other languages are then sorted alphabetically by name, with the exception of "For all languages"
|
||||
Collections.sort(localesList, (locale1, locale2) -> {
|
||||
if (!locale1.getLocaleString().equals("") && !locale2.getLocaleString().equals("")) {
|
||||
return locale1.toString().compareToIgnoreCase(locale2.toString());
|
||||
Collections.sort(localesList, (localeRenderer1, localeRenderer2) -> {
|
||||
if (!localeRenderer1.getLocaleString().equals("") && !localeRenderer2.getLocaleString().equals("")) {
|
||||
return localeRenderer1.toString().compareToIgnoreCase(localeRenderer2.toString());
|
||||
} else {
|
||||
return locale1.getLocaleString().compareToIgnoreCase(locale2.getLocaleString());
|
||||
return localeRenderer1.getLocaleString().compareToIgnoreCase(localeRenderer2.getLocaleString());
|
||||
}
|
||||
});
|
||||
|
||||
// Set "For all languages" to the end of the list
|
||||
if (!locale.getLocaleString().equals("")) {
|
||||
if (!localeRenderer.getLocaleString().equals("")) {
|
||||
// After alphabetical sorting, "For all languages" is always in 1st position.
|
||||
// (The position is 0 because the spinner menu item count starts at 0)
|
||||
final LocaleRenderer forAllLanguages = adapter.getItem(0);
|
||||
|
@ -191,13 +192,13 @@ public class UserDictionaryAddWordFragment extends SubScreenFragment {
|
|||
}
|
||||
|
||||
// Finally, we add the selected language to the top of the list.
|
||||
localesList.add(0, locale);
|
||||
localesList.add(0, localeRenderer);
|
||||
|
||||
// When a language is selected, the keyboard layout changes automatically
|
||||
mInput.restartInput(mWordEditText);
|
||||
|
||||
// The action bar subtitle is updated when a language is selected in the drop-down menu
|
||||
mActionBar.setSubtitle(locale.toString());
|
||||
mActionBar.setSubtitle(localeRenderer.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -205,7 +206,8 @@ public class UserDictionaryAddWordFragment extends SubScreenFragment {
|
|||
// I'm not sure we can come here, but if we do, that's the right thing to do.
|
||||
final Bundle args = getArguments();
|
||||
if (args == null) return;
|
||||
mContents.updateLocale(requireContext(), args.getString(UserDictionaryAddWordContents.EXTRA_LOCALE));
|
||||
final String localeString = args.getString(UserDictionaryAddWordContents.EXTRA_LOCALE);
|
||||
mContents.updateLocale(requireContext(), localeString == null ? null : LocaleUtils.constructLocale(localeString));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -25,8 +25,11 @@ import androidx.preference.PreferenceGroup;
|
|||
|
||||
import org.dslul.openboard.inputmethod.latin.R;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.DeviceProtectedUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeLocaleUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeSettingsKt;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeUtilsKt;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Locale;
|
||||
import java.util.TreeSet;
|
||||
|
||||
|
@ -91,54 +94,58 @@ public class UserDictionaryListFragment extends SubScreenFragment {
|
|||
* @param userDictGroup The group to put the settings in.
|
||||
*/
|
||||
private void createUserDictSettings(final PreferenceGroup userDictGroup) {
|
||||
final TreeSet<String> sortedLanguages = getSortedDictionaryLocaleStrings(requireContext());
|
||||
final TreeSet<Locale> sortedLocales = getSortedDictionaryLocales(requireContext());
|
||||
|
||||
// Add preference "for all locales"
|
||||
userDictGroup.addPreference(createUserDictionaryPreference(""));
|
||||
userDictGroup.addPreference(createUserDictionaryPreference(new Locale("")));
|
||||
// Add preference for each dictionary locale
|
||||
for (String localeUserDictionary : sortedLanguages) {
|
||||
userDictGroup.addPreference(createUserDictionaryPreference(localeUserDictionary));
|
||||
for (final Locale locale : sortedLocales) {
|
||||
userDictGroup.addPreference(createUserDictionaryPreference(locale));
|
||||
}
|
||||
}
|
||||
|
||||
static TreeSet<String> getSortedDictionaryLocaleStrings(final Context context) {
|
||||
static TreeSet<Locale> getSortedDictionaryLocales(final Context context) {
|
||||
final SharedPreferences prefs = DeviceProtectedUtils.getSharedPreferences(context);
|
||||
final boolean localeSystemOnly = prefs.getBoolean(Settings.PREF_USE_SYSTEM_LOCALES, true);
|
||||
final TreeSet<String> sortedLanguages = new TreeSet<>(String::compareToIgnoreCase);
|
||||
final TreeSet<Locale> sortedLocales = new TreeSet<>(new LocaleComparator());
|
||||
|
||||
// Add the main language selected in the "Language and Layouts" setting except "No language"
|
||||
for (InputMethodSubtype mainSubtype : SubtypeSettingsKt.getEnabledSubtypes(prefs, true)) {
|
||||
if (!mainSubtype.getLocale().equals("zz")) {
|
||||
sortedLanguages.add(mainSubtype.getLocale());
|
||||
final Locale mainLocale = SubtypeUtilsKt.locale(mainSubtype);
|
||||
if (!mainLocale.toLanguageTag().equals(SubtypeLocaleUtils.NO_LANGUAGE)) {
|
||||
sortedLocales.add(mainLocale);
|
||||
}
|
||||
// Secondary language is added only if main language is selected and if system language is not enabled
|
||||
if (!localeSystemOnly) {
|
||||
for (Locale secondaryLocale : Settings.getSecondaryLocales(prefs, mainSubtype.getLocale())) {
|
||||
sortedLanguages.add(secondaryLocale.toString());
|
||||
}
|
||||
sortedLocales.addAll(Settings.getSecondaryLocales(prefs, mainLocale));
|
||||
}
|
||||
}
|
||||
|
||||
for (Locale systemSubtype : SubtypeSettingsKt.getSystemLocales()) {
|
||||
sortedLanguages.add(systemSubtype.toString());
|
||||
sortedLocales.addAll(SubtypeSettingsKt.getSystemLocales());
|
||||
return sortedLocales;
|
||||
}
|
||||
|
||||
private static class LocaleComparator implements Comparator<Locale> {
|
||||
@Override
|
||||
public int compare(Locale locale1, Locale locale2) {
|
||||
return locale1.toLanguageTag().compareToIgnoreCase(locale2.toLanguageTag());
|
||||
}
|
||||
return sortedLanguages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single User Dictionary Preference object, with its parameters set.
|
||||
* @param localeString The locale for which this user dictionary is for.
|
||||
* @param locale The locale for which this user dictionary is for.
|
||||
* @return The corresponding preference.
|
||||
*/
|
||||
private Preference createUserDictionaryPreference(@NonNull final String localeString) {
|
||||
private Preference createUserDictionaryPreference(@NonNull final Locale locale) {
|
||||
final Preference newPref = new Preference(requireContext());
|
||||
|
||||
if (localeString.isEmpty()) {
|
||||
if (locale.toString().isEmpty()) {
|
||||
newPref.setTitle(getString(R.string.user_dict_settings_all_languages));
|
||||
} else {
|
||||
newPref.setTitle(UserDictionarySettings.getLocaleDisplayName(requireContext(), localeString));
|
||||
newPref.setTitle(UserDictionarySettings.getLocaleDisplayName(requireContext(), locale));
|
||||
}
|
||||
newPref.getExtras().putString("locale", localeString);
|
||||
newPref.getExtras().putString("locale", locale.toLanguageTag());
|
||||
newPref.setIconSpaceReserved(false);
|
||||
newPref.setFragment(UserDictionarySettings.class.getName());
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ public class UserDictionarySettings extends ListFragment {
|
|||
|
||||
private Cursor mCursor;
|
||||
|
||||
protected String mLocale;
|
||||
protected Locale mLocale;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -122,13 +122,13 @@ public class UserDictionarySettings extends ListFragment {
|
|||
final Bundle arguments = getArguments();
|
||||
final String localeFromArguments = null == arguments ? null : arguments.getString("locale");
|
||||
|
||||
final String locale;
|
||||
final String localeString;
|
||||
if (null != localeFromArguments) {
|
||||
locale = localeFromArguments;
|
||||
} else locale = localeFromIntent;
|
||||
localeString = localeFromArguments;
|
||||
} else localeString = localeFromIntent;
|
||||
mLocale = localeString == null ? null : LocaleUtils.constructLocale(localeString);
|
||||
|
||||
mLocale = locale;
|
||||
createCursor(locale);
|
||||
createCursor(mLocale == null ? null : mLocale.toString());
|
||||
TextView emptyView = view.findViewById(android.R.id.empty);
|
||||
emptyView.setText(R.string.user_dict_settings_empty_text);
|
||||
|
||||
|
@ -158,8 +158,9 @@ public class UserDictionarySettings extends ListFragment {
|
|||
}
|
||||
}
|
||||
|
||||
private void createCursor(final String locale) {
|
||||
// Locale can be any of:
|
||||
// cursor must be created using localeString to be in line with Android system
|
||||
private void createCursor(@Nullable final String localeString) {
|
||||
// localeString can be any of:
|
||||
// - The string representation of a locale, as returned by Locale#toString()
|
||||
// - The empty string. This means we want a cursor returning words valid for all locales.
|
||||
// - null. This means we want a cursor for the current locale, whatever this is.
|
||||
|
@ -172,13 +173,13 @@ public class UserDictionarySettings extends ListFragment {
|
|||
// human-readable, like "all_locales" and "current_locales" strings, provided they
|
||||
// can be guaranteed not to match locales that may exist.
|
||||
|
||||
if ("".equals(locale)) {
|
||||
if ("".equals(localeString)) {
|
||||
// Case-insensitive sort
|
||||
mCursor = requireContext().getContentResolver().query(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION,
|
||||
QUERY_SELECTION_ALL_LOCALES, null,
|
||||
"UPPER(" + UserDictionary.Words.WORD + ")");
|
||||
} else {
|
||||
final String queryLocale = null != locale ? locale : Locale.getDefault().toString();
|
||||
final String queryLocale = null != localeString ? localeString : Locale.getDefault().toString();
|
||||
mCursor = requireContext().getContentResolver().query(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION,
|
||||
QUERY_SELECTION, new String[] { queryLocale },
|
||||
"UPPER(" + UserDictionary.Words.WORD + ")");
|
||||
|
@ -200,13 +201,12 @@ public class UserDictionarySettings extends ListFragment {
|
|||
}
|
||||
}
|
||||
|
||||
public static String getLocaleDisplayName(Context context, String localeStr) {
|
||||
if (TextUtils.isEmpty(localeStr)) {
|
||||
public static String getLocaleDisplayName(Context context, Locale locale) {
|
||||
if (locale.toString().isEmpty()) {
|
||||
// CAVEAT: localeStr should not be null because a null locale stands for the system
|
||||
// locale in UserDictionary.Words.addWord.
|
||||
return context.getResources().getString(R.string.user_dict_settings_all_languages);
|
||||
}
|
||||
final Locale locale = LocaleUtils.constructLocaleFromString(localeStr);
|
||||
return LocaleUtils.getLocaleDisplayNameInSystemLocale(locale, context);
|
||||
}
|
||||
|
||||
|
@ -223,7 +223,7 @@ public class UserDictionarySettings extends ListFragment {
|
|||
args.putString(UserDictionaryAddWordContents.EXTRA_WORD, editingWord);
|
||||
args.putString(UserDictionaryAddWordContents.EXTRA_SHORTCUT, editingShortcut);
|
||||
args.putString(UserDictionaryAddWordContents.EXTRA_WEIGHT, editingWeight);
|
||||
args.putString(UserDictionaryAddWordContents.EXTRA_LOCALE, mLocale);
|
||||
args.putString(UserDictionaryAddWordContents.EXTRA_LOCALE, mLocale.toLanguageTag());
|
||||
AppCompatActivity activity = (AppCompatActivity) requireActivity();
|
||||
activity.getSupportFragmentManager().beginTransaction()
|
||||
.replace(android.R.id.content, UserDictionaryAddWordFragment.class, args)
|
||||
|
@ -252,27 +252,28 @@ public class UserDictionarySettings extends ListFragment {
|
|||
return mCursor.getString(mCursor.getColumnIndexOrThrow(column));
|
||||
}
|
||||
|
||||
// requires use of locale string for interaction with Android system
|
||||
public static void deleteWordInEditMode(final String word, final String shortcut, final String weight,
|
||||
final String locale, final ContentResolver resolver) {
|
||||
final String localeString, final ContentResolver resolver) {
|
||||
if (TextUtils.isEmpty(shortcut)) {
|
||||
if ("".equals(locale)) {
|
||||
if ("".equals(localeString)) {
|
||||
resolver.delete(
|
||||
UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITHOUT_SHORTCUT_AND_WITH_ALL_LOCALES,
|
||||
new String[] { word, weight });
|
||||
} else {
|
||||
resolver.delete(
|
||||
UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITHOUT_SHORTCUT_AND_WITH_LOCALE,
|
||||
new String[] { word, weight, locale });
|
||||
new String[] { word, weight, localeString });
|
||||
}
|
||||
} else {
|
||||
if ("".equals(locale)) {
|
||||
if ("".equals(localeString)) {
|
||||
resolver.delete(
|
||||
UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITH_SHORTCUT_AND_WITH_ALL_LOCALES,
|
||||
new String[] { word, shortcut, weight });
|
||||
} else {
|
||||
resolver.delete(
|
||||
UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITH_SHORTCUT_AND_WITH_LOCALE,
|
||||
new String[] { word, shortcut, weight, locale });
|
||||
new String[] { word, shortcut, weight, localeString });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import org.dslul.openboard.inputmethod.latin.common.ComposedData;
|
|||
import org.dslul.openboard.inputmethod.latin.settings.SettingsValuesForSuggestion;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.AdditionalSubtypeUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.DeviceProtectedUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeSettingsKt;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SuggestionResults;
|
||||
|
||||
import java.util.Locale;
|
||||
|
@ -81,36 +81,17 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
|
|||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mRecommendedThreshold = Float.parseFloat(
|
||||
getString(R.string.spellchecker_recommended_threshold_value));
|
||||
mRecommendedThreshold = Float.parseFloat(getString(R.string.spellchecker_recommended_threshold_value));
|
||||
final SharedPreferences prefs = DeviceProtectedUtils.getSharedPreferences(this);
|
||||
prefs.registerOnSharedPreferenceChangeListener(this);
|
||||
onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY);
|
||||
SubtypeSettingsKt.init(this);
|
||||
}
|
||||
|
||||
public float getRecommendedThreshold() {
|
||||
return mRecommendedThreshold;
|
||||
}
|
||||
|
||||
private static String getKeyboardLayoutNameForLocale(final Locale locale) {
|
||||
// See b/19963288.
|
||||
if (locale.getLanguage().equals("sr") || locale.getLanguage().equals("mk")) {
|
||||
return locale.getLanguage();
|
||||
}
|
||||
final int script = ScriptUtils.getScriptFromSpellCheckerLocale(locale);
|
||||
return switch (script) {
|
||||
case ScriptUtils.SCRIPT_LATIN -> "qwerty";
|
||||
case ScriptUtils.SCRIPT_ARMENIAN -> "armenian_phonetic";
|
||||
case ScriptUtils.SCRIPT_CYRILLIC -> "ru";
|
||||
case ScriptUtils.SCRIPT_GREEK -> "greek";
|
||||
case ScriptUtils.SCRIPT_HEBREW -> "hebrew";
|
||||
case ScriptUtils.SCRIPT_BULGARIAN -> "bulgarian";
|
||||
case ScriptUtils.SCRIPT_GEORGIAN -> "georgian";
|
||||
case ScriptUtils.SCRIPT_BENGALI -> "bengali_unijoy";
|
||||
default -> throw new RuntimeException("Wrong script supplied: " + script);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
|
||||
if (!PREF_USE_CONTACTS_KEY.equals(key)) return;
|
||||
|
@ -201,17 +182,14 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
|
|||
Keyboard keyboard = mKeyboardCache.get(locale);
|
||||
if (keyboard == null) {
|
||||
keyboard = createKeyboardForLocale(locale);
|
||||
if (keyboard != null) {
|
||||
mKeyboardCache.put(locale, keyboard);
|
||||
}
|
||||
mKeyboardCache.put(locale, keyboard);
|
||||
}
|
||||
return keyboard;
|
||||
}
|
||||
|
||||
private Keyboard createKeyboardForLocale(final Locale locale) {
|
||||
final String keyboardLayoutName = getKeyboardLayoutNameForLocale(locale);
|
||||
final InputMethodSubtype subtype = AdditionalSubtypeUtils.createDummyAdditionalSubtype(
|
||||
locale.toString(), keyboardLayoutName);
|
||||
final String keyboardLayoutName = SubtypeSettingsKt.getMatchingLayoutSetNameForLocale(locale);
|
||||
final InputMethodSubtype subtype = AdditionalSubtypeUtils.createDummyAdditionalSubtype(locale, keyboardLayoutName);
|
||||
final KeyboardLayoutSet keyboardLayoutSet = createKeyboardSetForSpellChecker(subtype);
|
||||
return keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
|
||||
}
|
||||
|
|
|
@ -9,16 +9,16 @@ package org.dslul.openboard.inputmethod.latin.spellcheck;
|
|||
import android.content.res.Resources;
|
||||
import android.os.Binder;
|
||||
import android.text.TextUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.Log;
|
||||
import android.view.textservice.SentenceSuggestionsInfo;
|
||||
import android.view.textservice.SuggestionsInfo;
|
||||
import android.view.textservice.TextInfo;
|
||||
|
||||
import org.dslul.openboard.inputmethod.latin.NgramContext;
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.Log;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SpannableStringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSession {
|
||||
private static final String TAG = AndroidSpellCheckerSession.class.getSimpleName();
|
||||
|
@ -142,10 +142,9 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck
|
|||
synchronized(this) {
|
||||
sentenceLevelAdapter = mSentenceLevelAdapter;
|
||||
if (sentenceLevelAdapter == null) {
|
||||
final String localeStr = getLocale();
|
||||
if (!TextUtils.isEmpty(localeStr)) {
|
||||
sentenceLevelAdapter = new SentenceLevelAdapter(mResources,
|
||||
new Locale(localeStr));
|
||||
final String localeString = getLocale();
|
||||
if (!TextUtils.isEmpty(localeString)) {
|
||||
sentenceLevelAdapter = new SentenceLevelAdapter(mResources, LocaleUtils.constructLocale(localeString));
|
||||
mSentenceLevelAdapter = sentenceLevelAdapter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
|
|||
// Immutable, but not available in the constructor.
|
||||
private Locale mLocale;
|
||||
// Cache this for performance
|
||||
private int mScript; // One of SCRIPT_LATIN or SCRIPT_CYRILLIC for now.
|
||||
private String mScript;
|
||||
private final AndroidSpellCheckerService mService;
|
||||
protected final SuggestionsCache mSuggestionsCache = new SuggestionsCache();
|
||||
private final ContentObserver mObserver;
|
||||
|
@ -58,7 +58,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
|
|||
private static final String quotesRegexp =
|
||||
"(\\u0022|\\u0027|\\u0060|\\u00B4|\\u2018|\\u2018|\\u201C|\\u201D)";
|
||||
|
||||
private static final Map<Integer, String> scriptToPunctuationRegexMap = new TreeMap<>();
|
||||
private static final Map<String, String> scriptToPunctuationRegexMap = new TreeMap<>();
|
||||
|
||||
static {
|
||||
// TODO: add other non-English language specific punctuation later.
|
||||
|
@ -107,27 +107,26 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
|
|||
|
||||
AndroidWordLevelSpellCheckerSession(final AndroidSpellCheckerService service) {
|
||||
mService = service;
|
||||
final ContentResolver cres = service.getContentResolver();
|
||||
|
||||
mObserver = new ContentObserver(null) {
|
||||
@Override
|
||||
public void onChange(boolean self) {
|
||||
mSuggestionsCache.clearCache();
|
||||
}
|
||||
};
|
||||
cres.registerContentObserver(Words.CONTENT_URI, true, mObserver);
|
||||
service.getContentResolver().registerContentObserver(Words.CONTENT_URI, true, mObserver);
|
||||
}
|
||||
|
||||
private void updateLocale() {
|
||||
final String localeString = getLocale();
|
||||
|
||||
if (mLocale == null || !mLocale.toString().equals(localeString)) {
|
||||
final String oldLocal = mLocale == null ? "null" : mLocale.toString();
|
||||
Log.d(TAG, "Updating locale from " + oldLocal + " to " + localeString);
|
||||
final String oldLocale = mLocale == null ? "null" : mLocale.toString();
|
||||
Log.d(TAG, "Updating locale from " + oldLocale + " to " + localeString);
|
||||
|
||||
mLocale = (null == localeString) ? null
|
||||
: LocaleUtils.constructLocaleFromString(localeString);
|
||||
mScript = ScriptUtils.getScriptFromSpellCheckerLocale(mLocale);
|
||||
: LocaleUtils.constructLocale(localeString);
|
||||
if (mLocale == null) mScript = ScriptUtils.SCRIPT_UNKNOWN;
|
||||
else mScript = ScriptUtils.script(mLocale);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,7 +136,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getLocale() {
|
||||
public String getLocale() { // unfortunately this can only return a string, with the obvious issues for
|
||||
// This function was taken from https://github.com/LineageOS/android_frameworks_base/blob/1235c24a0f092d0e41fd8e86f332f8dc03896a7b/services/core/java/com/android/server/TextServicesManagerService.java#L544 and slightly adopted.
|
||||
|
||||
final InputMethodManager imm;
|
||||
|
@ -179,7 +178,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
|
|||
private static final int CHECKABILITY_TOO_SHORT = 5;
|
||||
/**
|
||||
* Finds out whether a particular string should be filtered out of spell checking.
|
||||
*
|
||||
* <p>
|
||||
* This will loosely match URLs, numbers, symbols. To avoid always underlining words that
|
||||
* we know we will never recognize, this accepts a script identifier that should be one
|
||||
* of the SCRIPT_* constants defined above, to rule out quickly characters from very
|
||||
|
@ -189,7 +188,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
|
|||
* @param script the identifier for the script this spell checker recognizes
|
||||
* @return one of the FILTER_OUT_* constants above.
|
||||
*/
|
||||
private static int getCheckabilityInScript(final String text, final int script) {
|
||||
private static int getCheckabilityInScript(final String text, final String script) {
|
||||
if (TextUtils.isEmpty(text) || text.length() <= 1) return CHECKABILITY_TOO_SHORT;
|
||||
|
||||
// TODO: check if an equivalent processing can't be done more quickly with a
|
||||
|
@ -228,7 +227,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
|
|||
|
||||
/**
|
||||
* Helper method to test valid capitalizations of a word.
|
||||
*
|
||||
* <p>
|
||||
* If the "text" is lower-case, we test only the exact string.
|
||||
* If the "Text" is capitalized, we test the exact string "Text" and the lower-cased
|
||||
* version of it "text".
|
||||
|
@ -276,9 +275,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
|
|||
.replaceAll("^" + quotesRegexp, "")
|
||||
.replaceAll(quotesRegexp + "$", "");
|
||||
|
||||
final String localeRegex = scriptToPunctuationRegexMap.get(
|
||||
ScriptUtils.getScriptFromSpellCheckerLocale(mLocale)
|
||||
);
|
||||
final String localeRegex = scriptToPunctuationRegexMap.get(ScriptUtils.script(mLocale));
|
||||
|
||||
if (localeRegex != null) {
|
||||
text = text.replaceAll(localeRegex, "");
|
||||
|
@ -332,8 +329,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
|
|||
if (null == keyboard) {
|
||||
Log.w(TAG, "onGetSuggestionsInternal() : No keyboard for locale: " + mLocale);
|
||||
// If there is no keyboard for this locale, don't do any spell-checking.
|
||||
return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
|
||||
false /* reportAsTypo */);
|
||||
return AndroidSpellCheckerService.getNotInDictEmptySuggestions(false);
|
||||
}
|
||||
|
||||
final WordComposer composer = new WordComposer();
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.dslul.openboard.inputmethod.latin.common.StringUtils;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.dslul.openboard.inputmethod.latin.common.Constants.Subtype.ExtraValue.ASCII_CAPABLE;
|
||||
import static org.dslul.openboard.inputmethod.latin.common.Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
|
||||
|
@ -40,7 +41,7 @@ public final class AdditionalSubtypeUtils {
|
|||
}
|
||||
|
||||
private static final String LOCALE_AND_LAYOUT_SEPARATOR = ":";
|
||||
private static final int INDEX_OF_LOCALE = 0;
|
||||
private static final int INDEX_OF_LANGUAGE_TAG = 0;
|
||||
private static final int INDEX_OF_KEYBOARD_LAYOUT = 1;
|
||||
private static final int INDEX_OF_EXTRA_VALUE = 2;
|
||||
private static final int LENGTH_WITHOUT_EXTRA_VALUE = (INDEX_OF_KEYBOARD_LAYOUT + 1);
|
||||
|
@ -48,17 +49,17 @@ public final class AdditionalSubtypeUtils {
|
|||
private static final String PREF_SUBTYPE_SEPARATOR = ";";
|
||||
|
||||
private static InputMethodSubtype createAdditionalSubtypeInternal(
|
||||
final String localeString, final String keyboardLayoutSetName,
|
||||
final Locale locale, final String keyboardLayoutSetName,
|
||||
final boolean isAsciiCapable, final boolean isEmojiCapable) {
|
||||
final int nameId = SubtypeLocaleUtils.getSubtypeNameId(localeString, keyboardLayoutSetName);
|
||||
final int nameId = SubtypeLocaleUtils.getSubtypeNameId(locale, keyboardLayoutSetName);
|
||||
final String platformVersionDependentExtraValues = getPlatformVersionDependentExtraValue(
|
||||
localeString, keyboardLayoutSetName, isAsciiCapable, isEmojiCapable);
|
||||
locale, keyboardLayoutSetName, isAsciiCapable, isEmojiCapable);
|
||||
final int platformVersionIndependentSubtypeId =
|
||||
getPlatformVersionIndependentSubtypeId(localeString, keyboardLayoutSetName);
|
||||
getPlatformVersionIndependentSubtypeId(locale, keyboardLayoutSetName);
|
||||
final InputMethodSubtype.InputMethodSubtypeBuilder builder = new InputMethodSubtype.InputMethodSubtypeBuilder()
|
||||
.setSubtypeNameResId(nameId)
|
||||
.setSubtypeIconResId(R.drawable.ic_ime_switcher)
|
||||
.setSubtypeLocale(localeString)
|
||||
.setSubtypeLocale(locale.toString())
|
||||
.setSubtypeMode(KEYBOARD_MODE)
|
||||
.setSubtypeExtraValue(platformVersionDependentExtraValues)
|
||||
.setIsAuxiliary(false)
|
||||
|
@ -66,28 +67,27 @@ public final class AdditionalSubtypeUtils {
|
|||
.setSubtypeId(platformVersionIndependentSubtypeId)
|
||||
.setIsAsciiCapable(isAsciiCapable);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
builder.setLanguageTag(LocaleUtils.constructLocaleFromString(localeString).toLanguageTag());
|
||||
builder.setLanguageTag(locale.toLanguageTag());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static InputMethodSubtype createDummyAdditionalSubtype(
|
||||
final String localeString, final String keyboardLayoutSetName) {
|
||||
return createAdditionalSubtypeInternal(localeString, keyboardLayoutSetName, false, false);
|
||||
final Locale locale, final String keyboardLayoutSetName) {
|
||||
return createAdditionalSubtypeInternal(locale, keyboardLayoutSetName, false, false);
|
||||
}
|
||||
|
||||
public static InputMethodSubtype createEmojiCapableAdditionalSubtype(
|
||||
final String localeString, final String keyboardLayoutSetName, final boolean asciiCapable) {
|
||||
return createAdditionalSubtypeInternal(localeString, keyboardLayoutSetName, asciiCapable, true);
|
||||
final Locale locale, final String keyboardLayoutSetName, final boolean asciiCapable) {
|
||||
return createAdditionalSubtypeInternal(locale, keyboardLayoutSetName, asciiCapable, true);
|
||||
}
|
||||
|
||||
public static String getPrefSubtype(final InputMethodSubtype subtype) {
|
||||
final String localeString = subtype.getLocale();
|
||||
private static String getPrefSubtype(final InputMethodSubtype subtype) {
|
||||
final String keyboardLayoutSetName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
|
||||
final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName;
|
||||
final String extraValue = StringUtils.removeFromCommaSplittableTextIfExists(
|
||||
layoutExtraValue, StringUtils.removeFromCommaSplittableTextIfExists(
|
||||
IS_ADDITIONAL_SUBTYPE, subtype.getExtraValue()));
|
||||
final String basePrefSubtype = localeString + LOCALE_AND_LAYOUT_SEPARATOR
|
||||
final String basePrefSubtype = SubtypeUtilsKt.locale(subtype).toLanguageTag() + LOCALE_AND_LAYOUT_SEPARATOR
|
||||
+ keyboardLayoutSetName;
|
||||
return extraValue.isEmpty() ? basePrefSubtype
|
||||
: basePrefSubtype + LOCALE_AND_LAYOUT_SEPARATOR + extraValue;
|
||||
|
@ -115,11 +115,12 @@ public final class AdditionalSubtypeUtils {
|
|||
Log.w(TAG, "Unknown additional subtype specified: " + prefSubtype);
|
||||
return null;
|
||||
}
|
||||
final String localeString = elems[INDEX_OF_LOCALE];
|
||||
final String languageTag = elems[INDEX_OF_LANGUAGE_TAG];
|
||||
final Locale locale = LocaleUtils.constructLocale(languageTag);
|
||||
final String keyboardLayoutSetName = elems[INDEX_OF_KEYBOARD_LAYOUT];
|
||||
final boolean asciiCapable = ScriptUtils.getScriptFromSpellCheckerLocale(LocaleUtils.constructLocaleFromString(localeString)) == ScriptUtils.SCRIPT_LATIN;
|
||||
final boolean asciiCapable = ScriptUtils.script(locale).equals(ScriptUtils.SCRIPT_LATIN);
|
||||
// Here we assume that all the additional subtypes are EmojiCapable
|
||||
final InputMethodSubtype subtype = createEmojiCapableAdditionalSubtype(localeString, keyboardLayoutSetName, asciiCapable);
|
||||
final InputMethodSubtype subtype = createEmojiCapableAdditionalSubtype(locale, keyboardLayoutSetName, asciiCapable);
|
||||
if (subtype.getNameResId() == SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT && !keyboardLayoutSetName.startsWith(CustomLayoutUtilsKt.CUSTOM_LAYOUT_PREFIX)) {
|
||||
// Skip unknown keyboard layout subtype. This may happen when predefined keyboard
|
||||
// layout has been removed.
|
||||
|
@ -164,14 +165,13 @@ public final class AdditionalSubtypeUtils {
|
|||
* assume that the extra values stored in a persistent storage are always valid. We need to
|
||||
* regenerate the extra value on the fly instead.
|
||||
* </p>
|
||||
* @param localeString the locale string (e.g., "en_US").
|
||||
* @param keyboardLayoutSetName the keyboard layout set name (e.g., "dvorak").
|
||||
* @param isAsciiCapable true when ASCII characters are supported with this layout.
|
||||
* @param isEmojiCapable true when Unicode Emoji characters are supported with this layout.
|
||||
* @return extra value that is optimized for the running OS.
|
||||
* @see #getPlatformVersionIndependentSubtypeId(String, String)
|
||||
* @see #getPlatformVersionIndependentSubtypeId(Locale, String)
|
||||
*/
|
||||
private static String getPlatformVersionDependentExtraValue(final String localeString,
|
||||
private static String getPlatformVersionDependentExtraValue(final Locale locale,
|
||||
final String keyboardLayoutSetName, final boolean isAsciiCapable,
|
||||
final boolean isEmojiCapable) {
|
||||
final ArrayList<String> extraValueItems = new ArrayList<>();
|
||||
|
@ -179,7 +179,7 @@ public final class AdditionalSubtypeUtils {
|
|||
if (isAsciiCapable) {
|
||||
extraValueItems.add(ASCII_CAPABLE);
|
||||
}
|
||||
if (SubtypeLocaleUtils.isExceptionalLocale(localeString)) {
|
||||
if (SubtypeLocaleUtils.isExceptionalLocale(locale)) {
|
||||
extraValueItems.add(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME + "=" +
|
||||
SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(keyboardLayoutSetName));
|
||||
}
|
||||
|
@ -202,12 +202,11 @@ public final class AdditionalSubtypeUtils {
|
|||
* method even when we need to add some new extra values for the actual instance of
|
||||
* {@link InputMethodSubtype}.
|
||||
* </p>
|
||||
* @param localeString the locale string (e.g., "en_US").
|
||||
* @param keyboardLayoutSetName the keyboard layout set name (e.g., "dvorak").
|
||||
* @return a platform-version independent subtype ID.
|
||||
* @see #getPlatformVersionDependentExtraValue(String, String, boolean, boolean)
|
||||
* @see #getPlatformVersionDependentExtraValue(Locale, String, boolean, boolean)
|
||||
*/
|
||||
private static int getPlatformVersionIndependentSubtypeId(final String localeString,
|
||||
private static int getPlatformVersionIndependentSubtypeId(final Locale locale,
|
||||
final String keyboardLayoutSetName) {
|
||||
// For compatibility reasons, we concatenate the extra values in the following order.
|
||||
// - KeyboardLayoutSet
|
||||
|
@ -218,7 +217,7 @@ public final class AdditionalSubtypeUtils {
|
|||
final ArrayList<String> compatibilityExtraValueItems = new ArrayList<>();
|
||||
compatibilityExtraValueItems.add(KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName);
|
||||
compatibilityExtraValueItems.add(ASCII_CAPABLE);
|
||||
if (SubtypeLocaleUtils.isExceptionalLocale(localeString)) {
|
||||
if (SubtypeLocaleUtils.isExceptionalLocale(locale)) {
|
||||
compatibilityExtraValueItems.add(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME + "=" +
|
||||
SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(keyboardLayoutSetName));
|
||||
}
|
||||
|
@ -226,7 +225,7 @@ public final class AdditionalSubtypeUtils {
|
|||
compatibilityExtraValueItems.add(IS_ADDITIONAL_SUBTYPE);
|
||||
final String compatibilityExtraValues = TextUtils.join(",", compatibilityExtraValueItems);
|
||||
return Arrays.hashCode(new Object[] {
|
||||
localeString,
|
||||
locale,
|
||||
KEYBOARD_MODE,
|
||||
compatibilityExtraValues,
|
||||
false /* isAuxiliary */,
|
||||
|
|
|
@ -22,7 +22,7 @@ import java.io.File
|
|||
import java.io.IOException
|
||||
import java.math.BigInteger
|
||||
|
||||
fun loadCustomLayout(uri: Uri?, localeString: String, context: Context, onAdded: (String) -> Unit) {
|
||||
fun loadCustomLayout(uri: Uri?, languageTag: String, context: Context, onAdded: (String) -> Unit) {
|
||||
if (uri == null)
|
||||
return infoDialog(context, context.getString(R.string.layout_error, "layout file not found"))
|
||||
val layoutContent: String
|
||||
|
@ -41,10 +41,10 @@ fun loadCustomLayout(uri: Uri?, localeString: String, context: Context, onAdded:
|
|||
name = it.getString(idx).substringBeforeLast(".")
|
||||
}
|
||||
}
|
||||
loadCustomLayout(layoutContent, name, localeString, context, onAdded)
|
||||
loadCustomLayout(layoutContent, name, languageTag, context, onAdded)
|
||||
}
|
||||
|
||||
fun loadCustomLayout(layoutContent: String, layoutName: String, localeString: String, context: Context, onAdded: (String) -> Unit) {
|
||||
fun loadCustomLayout(layoutContent: String, layoutName: String, languageTag: String, context: Context, onAdded: (String) -> Unit) {
|
||||
var name = layoutName
|
||||
val isJson = checkLayout(layoutContent, context)
|
||||
?: return infoDialog(context, context.getString(R.string.layout_error, "invalid layout file, ${Log.getLog(10).lastOrNull { it.tag == TAG }?.message}"))
|
||||
|
@ -60,7 +60,7 @@ fun loadCustomLayout(layoutContent: String, layoutName: String, localeString: St
|
|||
})
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
// name must be encoded to avoid issues with validity of subtype extra string or file name
|
||||
name = "$CUSTOM_LAYOUT_PREFIX${localeString}.${encodeBase36(name)}.${if (isJson) "json" else "txt"}"
|
||||
name = "$CUSTOM_LAYOUT_PREFIX${languageTag}.${encodeBase36(name)}.${if (isJson) "json" else "txt"}"
|
||||
val file = getFile(name, context)
|
||||
if (file.exists())
|
||||
file.delete()
|
||||
|
|
|
@ -15,7 +15,7 @@ import androidx.annotation.Nullable;
|
|||
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
|
||||
|
||||
import org.dslul.openboard.inputmethod.annotations.UsedForTesting;
|
||||
import org.dslul.openboard.inputmethod.latin.BinaryDictionaryGetter;
|
||||
import org.dslul.openboard.inputmethod.latin.Dictionary;
|
||||
import org.dslul.openboard.inputmethod.latin.define.DecoderSpecificConstants;
|
||||
import org.dslul.openboard.inputmethod.latin.makedict.DictionaryHeader;
|
||||
import org.dslul.openboard.inputmethod.latin.makedict.UnsupportedFormatException;
|
||||
|
@ -32,9 +32,11 @@ public class DictionaryInfoUtils {
|
|||
private static final String TAG = DictionaryInfoUtils.class.getSimpleName();
|
||||
public static final String DEFAULT_MAIN_DICT = "main";
|
||||
public static final String MAIN_DICT_PREFIX = DEFAULT_MAIN_DICT + "_";
|
||||
private static final String DICTIONARY_CATEGORY_SEPARATOR_EXPRESSION = "[" + BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR + "_]";
|
||||
// 6 digits - unicode is limited to 21 bits
|
||||
private static final int MAX_HEX_DIGITS_FOR_CODEPOINT = 6;
|
||||
public static final String ASSETS_DICTIONARY_FOLDER = "dicts";
|
||||
public static final String ID_CATEGORY_SEPARATOR = ":";
|
||||
private static final String DICTIONARY_CATEGORY_SEPARATOR_EXPRESSION = "[" + ID_CATEGORY_SEPARATOR + "_]";
|
||||
|
||||
private DictionaryInfoUtils() {
|
||||
// Private constructor to forbid instantation of this helper class.
|
||||
|
@ -49,7 +51,7 @@ public class DictionaryInfoUtils {
|
|||
if (codePoint >= 0x30 && codePoint <= 0x39) return true; // Digit
|
||||
if (codePoint >= 0x41 && codePoint <= 0x5A) return true; // Uppercase
|
||||
if (codePoint >= 0x61 && codePoint <= 0x7A) return true; // Lowercase
|
||||
return codePoint == '_'; // Underscore
|
||||
return codePoint == '_' || codePoint == '-';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,13 +85,6 @@ public class DictionaryInfoUtils {
|
|||
return context.getFilesDir() + File.separator + "dicts";
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get the top level temp directory.
|
||||
*/
|
||||
public static String getWordListTempDirectory(final Context context) {
|
||||
return context.getFilesDir() + File.separator + "tmp";
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse escaping done by {@link #replaceFileNameDangerousCharacters(String)}.
|
||||
*/
|
||||
|
@ -119,32 +114,11 @@ public class DictionaryInfoUtils {
|
|||
return new File(DictionaryInfoUtils.getWordListCacheDirectory(context)).listFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the category for a given file name.
|
||||
* <p>
|
||||
* This parses the file name, extracts the category, and returns it. See
|
||||
* {@link #getMainDictId(Locale)} and {@link #isMainWordListId(String)}.
|
||||
* @return The category as a string or null if it can't be found in the file name.
|
||||
*/
|
||||
@Nullable
|
||||
public static String getCategoryFromFileName(@NonNull final String fileName) {
|
||||
final String id = getWordListIdFromFileName(fileName);
|
||||
final String[] idArray = id.split(DICTIONARY_CATEGORY_SEPARATOR_EXPRESSION);
|
||||
// An id is supposed to be in format category:locale, so splitting on the separator
|
||||
// should yield a 2-elements array
|
||||
// Also allow '_' as separator, this is ok for locales like pt_br because
|
||||
// we're interested in the part before first separator anyway
|
||||
if (1 == idArray.length) {
|
||||
return null;
|
||||
}
|
||||
return idArray[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out the cache directory associated with a specific locale.
|
||||
*/
|
||||
public static String getCacheDirectoryForLocale(final String locale, final Context context) {
|
||||
final String relativeDirectoryName = replaceFileNameDangerousCharacters(locale).toLowerCase(Locale.ENGLISH);
|
||||
public static String getCacheDirectoryForLocale(final Locale locale, final Context context) {
|
||||
final String relativeDirectoryName = replaceFileNameDangerousCharacters(locale.toLanguageTag());
|
||||
final String absoluteDirectoryName = getWordListCacheDirectory(context) + File.separator + relativeDirectoryName;
|
||||
final File directory = new File(absoluteDirectoryName);
|
||||
if (!directory.exists()) {
|
||||
|
@ -155,16 +129,11 @@ public class DictionaryInfoUtils {
|
|||
return absoluteDirectoryName;
|
||||
}
|
||||
|
||||
public static boolean isMainWordListId(final String id) {
|
||||
final String[] idArray = id.split(DICTIONARY_CATEGORY_SEPARATOR_EXPRESSION);
|
||||
// An id is supposed to be in format category:locale, so splitting on the separator
|
||||
// should yield a 2-elements array
|
||||
// Also allow '_' as separator, this is ok for locales like pt_br because
|
||||
// we're interested in the part before first separator anyway
|
||||
if (1 == idArray.length) {
|
||||
return false;
|
||||
}
|
||||
return BinaryDictionaryGetter.MAIN_DICTIONARY_CATEGORY.equals(idArray[0]);
|
||||
public static File[] getCachedDictsForLocale(final Locale locale, final Context context) {
|
||||
final File cachedDir = new File(getCacheDirectoryForLocale(locale, context));
|
||||
if (!cachedDir.isDirectory())
|
||||
return new File[]{};
|
||||
return cachedDir.listFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -179,17 +148,11 @@ public class DictionaryInfoUtils {
|
|||
// This works because we don't include by default different dictionaries for
|
||||
// different countries. This actually needs to return the id that we would
|
||||
// like to use for word lists included in resources, and the following is okay.
|
||||
return BinaryDictionaryGetter.MAIN_DICTIONARY_CATEGORY +
|
||||
BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR + locale.toString().toLowerCase();
|
||||
return Dictionary.TYPE_MAIN + ID_CATEGORY_SEPARATOR + locale.toString().toLowerCase();
|
||||
}
|
||||
|
||||
public static String getMainDictFilename(@NonNull final String locale) {
|
||||
return MAIN_DICT_PREFIX + locale.toLowerCase(Locale.ENGLISH) + ".dict";
|
||||
}
|
||||
|
||||
public static File getMainDictFile(@NonNull final String locale, @NonNull final Context context) {
|
||||
return new File(DictionaryInfoUtils.getCacheDirectoryForLocale(locale, context) +
|
||||
File.separator + DictionaryInfoUtils.getMainDictFilename(locale));
|
||||
public static String getExtractedMainDictFilename() {
|
||||
return DEFAULT_MAIN_DICT + ".dict";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -202,6 +165,43 @@ public class DictionaryInfoUtils {
|
|||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static DictionaryHeader getDictionaryFileHeaderOrNull(final File file) {
|
||||
try {
|
||||
return BinaryDictionaryUtils.getHeader(file);
|
||||
} catch (UnsupportedFormatException | IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the locale for a dictionary file name stored in assets.
|
||||
* <p>
|
||||
* Assumes file name main_[locale].dict
|
||||
* <p>
|
||||
* Returns the locale, or null if file name does not match the pattern
|
||||
*/
|
||||
@Nullable public static String extractLocaleFromAssetsDictionaryFile(final String dictionaryFileName) {
|
||||
if (dictionaryFileName.startsWith(DictionaryInfoUtils.MAIN_DICT_PREFIX)
|
||||
&& dictionaryFileName.endsWith(".dict")) {
|
||||
return dictionaryFileName.substring(
|
||||
DictionaryInfoUtils.MAIN_DICT_PREFIX.length(),
|
||||
dictionaryFileName.lastIndexOf('.')
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable public static String[] getAssetsDictionaryList(final Context context) {
|
||||
final String[] dictionaryList;
|
||||
try {
|
||||
dictionaryList = context.getAssets().list(ASSETS_DICTIONARY_FOLDER);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
return dictionaryList;
|
||||
}
|
||||
|
||||
@UsedForTesting
|
||||
public static boolean looksValidForDictionaryInsertion(final CharSequence text,
|
||||
final SpacingAndPunctuations spacingAndPunctuations) {
|
||||
|
|
|
@ -8,8 +8,8 @@ import android.view.View
|
|||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.edit
|
||||
import org.dslul.openboard.inputmethod.latin.BinaryDictionaryGetter
|
||||
import org.dslul.openboard.inputmethod.latin.R
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils.constructLocale
|
||||
import org.dslul.openboard.inputmethod.latin.settings.Settings
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
@ -24,19 +24,15 @@ fun getDictionaryLocales(context: Context): MutableSet<Locale> {
|
|||
for (directory in cachedDirectoryList) {
|
||||
if (!directory.isDirectory) continue
|
||||
if (!hasAnythingOtherThanExtractedMainDictionary(directory)) continue
|
||||
val dirLocale = DictionaryInfoUtils.getWordListIdFromFileName(directory.name)
|
||||
val locale = dirLocale.toLocale()
|
||||
val locale = DictionaryInfoUtils.getWordListIdFromFileName(directory.name).constructLocale()
|
||||
locales.add(locale)
|
||||
}
|
||||
}
|
||||
// get assets dictionaries
|
||||
val assetsDictionaryList = BinaryDictionaryGetter.getAssetsDictionaryList(context)
|
||||
val assetsDictionaryList = DictionaryInfoUtils.getAssetsDictionaryList(context)
|
||||
if (assetsDictionaryList != null) {
|
||||
for (dictionary in assetsDictionaryList) {
|
||||
val dictLocale =
|
||||
BinaryDictionaryGetter.extractLocaleFromAssetsDictionaryFile(dictionary)
|
||||
?: continue
|
||||
val locale = dictLocale.toLocale()
|
||||
val locale = DictionaryInfoUtils.extractLocaleFromAssetsDictionaryFile(dictionary)?.constructLocale() ?: continue
|
||||
locales.add(locale)
|
||||
}
|
||||
}
|
||||
|
@ -73,15 +69,15 @@ fun cleanUnusedMainDicts(context: Context) {
|
|||
val dictionaryDir = File(DictionaryInfoUtils.getWordListCacheDirectory(context))
|
||||
val dirs = dictionaryDir.listFiles() ?: return
|
||||
val prefs = DeviceProtectedUtils.getSharedPreferences(context)
|
||||
val usedLocales = hashSetOf<String>()
|
||||
val usedLocaleLanguageTags = hashSetOf<String>()
|
||||
getEnabledSubtypes(prefs).forEach {
|
||||
val locale = it.locale()
|
||||
usedLocales.add(locale)
|
||||
Settings.getSecondaryLocales(prefs, locale).forEach { usedLocales.add(it.toString().lowercase()) }
|
||||
usedLocaleLanguageTags.add(locale.toLanguageTag())
|
||||
Settings.getSecondaryLocales(prefs, locale).forEach { usedLocaleLanguageTags.add(it.toLanguageTag()) }
|
||||
}
|
||||
for (dir in dirs) {
|
||||
if (!dir.isDirectory) continue
|
||||
if (dir.name in usedLocales) continue
|
||||
if (dir.name in usedLocaleLanguageTags) continue
|
||||
if (hasAnythingOtherThanExtractedMainDictionary(dir))
|
||||
continue
|
||||
dir.deleteRecursively()
|
||||
|
@ -89,6 +85,6 @@ fun cleanUnusedMainDicts(context: Context) {
|
|||
}
|
||||
|
||||
private fun hasAnythingOtherThanExtractedMainDictionary(dir: File) =
|
||||
dir.listFiles()?.any { it.name != DictionaryInfoUtils.getMainDictFilename(dir.name) } != false
|
||||
dir.listFiles()?.any { it.name != DictionaryInfoUtils.getExtractedMainDictFilename() } != false
|
||||
|
||||
const val DICTIONARY_URL = "https://codeberg.org/Helium314/aosp-dictionaries"
|
||||
|
|
|
@ -49,7 +49,7 @@ public final class LanguageOnSpacebarUtils {
|
|||
final String keyboardLayout = subtype.getKeyboardLayoutSetName();
|
||||
int sameLanguageAndLayoutCount = 0;
|
||||
for (final InputMethodSubtype ims : sEnabledSubtypes) {
|
||||
final String language = SubtypeLocaleUtils.getSubtypeLocale(ims).getLanguage();
|
||||
final String language = SubtypeUtilsKt.locale(ims).getLanguage();
|
||||
if (keyboardLanguage.equals(language) && keyboardLayout.equals(
|
||||
SubtypeLocaleUtils.getKeyboardLayoutSetName(ims))) {
|
||||
sameLanguageAndLayoutCount++;
|
||||
|
|
|
@ -6,13 +6,17 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import org.dslul.openboard.inputmethod.compat.locale
|
||||
import org.dslul.openboard.inputmethod.dictionarypack.DictionaryPackConstants
|
||||
import org.dslul.openboard.inputmethod.latin.Dictionary
|
||||
import org.dslul.openboard.inputmethod.latin.R
|
||||
import org.dslul.openboard.inputmethod.latin.ReadOnlyBinaryDictionary
|
||||
import org.dslul.openboard.inputmethod.latin.common.FileUtils
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils.constructLocale
|
||||
import org.dslul.openboard.inputmethod.latin.makedict.DictionaryHeader
|
||||
import org.dslul.openboard.inputmethod.latin.settings.*
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils.script
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
@ -34,7 +38,7 @@ class NewDictionaryAdder(private val context: Context, private val onAdded: ((Bo
|
|||
|
||||
val newHeader = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(cachedDictionaryFile, 0, cachedDictionaryFile.length())
|
||||
?: return onDictionaryLoadingError(R.string.dictionary_file_error)
|
||||
val locale = newHeader.mLocaleString.toLocale()
|
||||
val locale = newHeader.mLocaleString.constructLocale()
|
||||
|
||||
val dict = ReadOnlyBinaryDictionary(cachedDictionaryFile.absolutePath, 0, cachedDictionaryFile.length(), false, locale, "test")
|
||||
if (!dict.isValidDictionary) {
|
||||
|
@ -53,7 +57,7 @@ class NewDictionaryAdder(private val context: Context, private val onAdded: ((Bo
|
|||
.setMessage(message)
|
||||
.setNeutralButton(android.R.string.cancel) { _, _ -> cachedDictionaryFile.delete() }
|
||||
.setNegativeButton(R.string.button_select_language) { _, _ -> selectLocaleForDictionary(newHeader, locale) }
|
||||
if (hasMatchingSubtypeForLocaleString(locale.toString())) {
|
||||
if (hasMatchingSubtypeForLocale(locale)) {
|
||||
val buttonText = context.getString(R.string.button_add_to_language, localeName)
|
||||
b.setPositiveButton(buttonText) { _, _ ->
|
||||
addDictAndAskToReplace(newHeader, locale)
|
||||
|
@ -65,7 +69,7 @@ class NewDictionaryAdder(private val context: Context, private val onAdded: ((Bo
|
|||
|
||||
// ScriptUtils.getScriptFromSpellCheckerLocale may return latin when it should not,
|
||||
// e.g. for Persian or Chinese. But at least fail when dictionary certainly is incompatible
|
||||
if (ScriptUtils.getScriptFromSpellCheckerLocale(locale) != ScriptUtils.getScriptFromSpellCheckerLocale(mainLocale))
|
||||
if (locale.script() != mainLocale.script())
|
||||
return onDictionaryLoadingError(R.string.dictionary_file_wrong_script)
|
||||
|
||||
if (locale != mainLocale) {
|
||||
|
@ -87,13 +91,7 @@ class NewDictionaryAdder(private val context: Context, private val onAdded: ((Bo
|
|||
}
|
||||
|
||||
private fun selectLocaleForDictionary(newHeader: DictionaryHeader, dictLocale: Locale) {
|
||||
val localeStrings = getAvailableSubtypeLocaleStrings()
|
||||
val locales = linkedSetOf<Locale>()
|
||||
localeStrings.forEach {
|
||||
if (it.substringBefore("_") == dictLocale.language)
|
||||
locales.add(it.toLocale())
|
||||
}
|
||||
localeStrings.forEach { locales.add(it.toLocale()) }
|
||||
val locales = getAvailableSubtypeLocales().sortedBy { it.language != dictLocale.language } // matching languages should show first
|
||||
val displayNamesArray = locales.map { LocaleUtils.getLocaleDisplayNameInSystemLocale(it, context) }.toTypedArray()
|
||||
AlertDialog.Builder(context)
|
||||
.setTitle(R.string.button_select_language)
|
||||
|
@ -110,20 +108,17 @@ class NewDictionaryAdder(private val context: Context, private val onAdded: ((Bo
|
|||
|
||||
private fun addDictAndAskToReplace(header: DictionaryHeader, mainLocale: Locale) {
|
||||
val dictionaryType = header.mIdString.substringBefore(":")
|
||||
val dictFilename = DictionaryInfoUtils.getCacheDirectoryForLocale(mainLocale.toString(), context) +
|
||||
File.separator + dictionaryType + "_" + USER_DICTIONARY_SUFFIX
|
||||
val dictFile = File(dictFilename)
|
||||
val cacheDir = DictionaryInfoUtils.getCacheDirectoryForLocale(mainLocale, context)
|
||||
val dictFile = File(cacheDir, dictionaryType + "_" + USER_DICTIONARY_SUFFIX)
|
||||
|
||||
fun moveDict(replaced: Boolean) {
|
||||
if (!cachedDictionaryFile.renameTo(dictFile)) {
|
||||
return onDictionaryLoadingError(R.string.dictionary_load_error)
|
||||
}
|
||||
if (dictionaryType == DictionaryInfoUtils.DEFAULT_MAIN_DICT) {
|
||||
if (dictionaryType == Dictionary.TYPE_MAIN) {
|
||||
// replaced main dict, remove the one created from internal data
|
||||
// todo: currently not, see also BinaryDictionaryGetter.getDictionaryFiles
|
||||
// val internalMainDictFilename = DictionaryInfoUtils.getCacheDirectoryForLocale(mainLocaleString, context) +
|
||||
// File.separator + DictionaryInfoUtils.getMainDictFilename(mainLocaleString)
|
||||
// File(internalMainDictFilename).delete()
|
||||
val internalMainDictFile = File(cacheDir, DictionaryInfoUtils.getExtractedMainDictFilename())
|
||||
internalMainDictFile.delete()
|
||||
}
|
||||
val newDictBroadcast = Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION)
|
||||
context.sendBroadcast(newDictBroadcast)
|
||||
|
@ -134,7 +129,7 @@ class NewDictionaryAdder(private val context: Context, private val onAdded: ((Bo
|
|||
return moveDict(false)
|
||||
}
|
||||
|
||||
val systemLocale = context.resources.configuration.locale
|
||||
val systemLocale = context.resources.configuration.locale()
|
||||
val newInfo = header.info(systemLocale)
|
||||
val oldInfo = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(dictFile, 0, dictFile.length())?.info(systemLocale)
|
||||
confirmDialog(context, context.getString(R.string.replace_dictionary_message, dictionaryType, newInfo, oldInfo), context.getString(
|
||||
|
@ -148,5 +143,3 @@ class NewDictionaryAdder(private val context: Context, private val onAdded: ((Bo
|
|||
infoDialog(context, messageId)
|
||||
}
|
||||
}
|
||||
|
||||
fun String.toLocale() = LocaleUtils.constructLocaleFromString(this)
|
||||
|
|
|
@ -1,236 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
* modified
|
||||
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.dslul.openboard.inputmethod.latin.utils;
|
||||
|
||||
|
||||
import androidx.collection.ArraySet;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* A class to help with handling different writing scripts.
|
||||
*/
|
||||
public class ScriptUtils {
|
||||
|
||||
// Used for hardware keyboards
|
||||
public static final int SCRIPT_UNKNOWN = -1;
|
||||
|
||||
// These should be aligned with KeyboardLayoutSet_Feature values in attrs.xml.
|
||||
public static final int SCRIPT_ARABIC = 0;
|
||||
public static final int SCRIPT_ARMENIAN = 1;
|
||||
public static final int SCRIPT_BENGALI = 2;
|
||||
public static final int SCRIPT_CYRILLIC = 3;
|
||||
public static final int SCRIPT_DEVANAGARI = 4;
|
||||
public static final int SCRIPT_GEORGIAN = 5;
|
||||
public static final int SCRIPT_GREEK = 6;
|
||||
public static final int SCRIPT_HEBREW = 7;
|
||||
public static final int SCRIPT_KANNADA = 8;
|
||||
public static final int SCRIPT_KHMER = 9;
|
||||
public static final int SCRIPT_LAO = 10;
|
||||
public static final int SCRIPT_LATIN = 11;
|
||||
public static final int SCRIPT_MALAYALAM = 12;
|
||||
public static final int SCRIPT_MYANMAR = 13;
|
||||
public static final int SCRIPT_SINHALA = 14;
|
||||
public static final int SCRIPT_TAMIL = 15;
|
||||
public static final int SCRIPT_TELUGU = 16;
|
||||
public static final int SCRIPT_THAI = 17;
|
||||
public static final int SCRIPT_BULGARIAN = 18; // todo: why is bulgarian a separate script?
|
||||
public static final int SCRIPT_HANGUL = 19;
|
||||
public static final int SCRIPT_GUJARATI = 20;
|
||||
|
||||
private static final TreeMap<String, Integer> mLanguageCodeToScriptCode;
|
||||
private final static ArraySet<Integer> UPPERCASE_SCRIPTS = new ArraySet<>();
|
||||
|
||||
|
||||
static {
|
||||
mLanguageCodeToScriptCode = new TreeMap<>();
|
||||
mLanguageCodeToScriptCode.put("", SCRIPT_LATIN); // default
|
||||
mLanguageCodeToScriptCode.put("ar", SCRIPT_ARABIC);
|
||||
mLanguageCodeToScriptCode.put("ur", SCRIPT_ARABIC);
|
||||
mLanguageCodeToScriptCode.put("fa", SCRIPT_ARABIC);
|
||||
mLanguageCodeToScriptCode.put("hy", SCRIPT_ARMENIAN);
|
||||
mLanguageCodeToScriptCode.put("bg", SCRIPT_BULGARIAN);
|
||||
mLanguageCodeToScriptCode.put("bn", SCRIPT_BENGALI);
|
||||
mLanguageCodeToScriptCode.put("sr", SCRIPT_CYRILLIC);
|
||||
mLanguageCodeToScriptCode.put("mk", SCRIPT_CYRILLIC);
|
||||
mLanguageCodeToScriptCode.put("ru", SCRIPT_CYRILLIC);
|
||||
mLanguageCodeToScriptCode.put("ka", SCRIPT_GEORGIAN);
|
||||
mLanguageCodeToScriptCode.put("el", SCRIPT_GREEK);
|
||||
mLanguageCodeToScriptCode.put("iw", SCRIPT_HEBREW);
|
||||
mLanguageCodeToScriptCode.put("km", SCRIPT_KHMER);
|
||||
mLanguageCodeToScriptCode.put("lo", SCRIPT_LAO);
|
||||
mLanguageCodeToScriptCode.put("ml", SCRIPT_MALAYALAM);
|
||||
mLanguageCodeToScriptCode.put("my", SCRIPT_MYANMAR);
|
||||
mLanguageCodeToScriptCode.put("si", SCRIPT_SINHALA);
|
||||
mLanguageCodeToScriptCode.put("ta", SCRIPT_TAMIL);
|
||||
mLanguageCodeToScriptCode.put("te", SCRIPT_TELUGU);
|
||||
mLanguageCodeToScriptCode.put("th", SCRIPT_THAI);
|
||||
mLanguageCodeToScriptCode.put("uk", SCRIPT_CYRILLIC);
|
||||
mLanguageCodeToScriptCode.put("ko", SCRIPT_HANGUL);
|
||||
mLanguageCodeToScriptCode.put("hi", SCRIPT_DEVANAGARI);
|
||||
mLanguageCodeToScriptCode.put("kn", SCRIPT_KANNADA);
|
||||
mLanguageCodeToScriptCode.put("mr", SCRIPT_DEVANAGARI);
|
||||
mLanguageCodeToScriptCode.put("mn", SCRIPT_CYRILLIC);
|
||||
mLanguageCodeToScriptCode.put("be", SCRIPT_CYRILLIC);
|
||||
mLanguageCodeToScriptCode.put("kk", SCRIPT_CYRILLIC);
|
||||
mLanguageCodeToScriptCode.put("ky", SCRIPT_CYRILLIC);
|
||||
mLanguageCodeToScriptCode.put("ne", SCRIPT_DEVANAGARI);
|
||||
mLanguageCodeToScriptCode.put("gu", SCRIPT_GUJARATI);
|
||||
|
||||
// only Latin, Cyrillic, Greek and Armenian have upper/lower case
|
||||
// https://unicode.org/faq/casemap_charprop.html#3
|
||||
// adding Bulgarian because internally it uses a separate script value
|
||||
UPPERCASE_SCRIPTS.add(SCRIPT_LATIN);
|
||||
UPPERCASE_SCRIPTS.add(SCRIPT_CYRILLIC);
|
||||
UPPERCASE_SCRIPTS.add(SCRIPT_BULGARIAN);
|
||||
UPPERCASE_SCRIPTS.add(SCRIPT_GREEK);
|
||||
UPPERCASE_SCRIPTS.add(SCRIPT_ARMENIAN);
|
||||
}
|
||||
|
||||
|
||||
public static boolean scriptSupportsUppercase(Locale locale) {
|
||||
return UPPERCASE_SCRIPTS.contains(getScriptFromSpellCheckerLocale(locale));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the code point is a letter that makes sense for the specified
|
||||
* locale for this spell checker.
|
||||
* The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml
|
||||
* and is limited to EFIGS languages and Russian.
|
||||
* Hence at the moment this explicitly tests for Cyrillic characters or Latin characters
|
||||
* as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters.
|
||||
*/
|
||||
public static boolean isLetterPartOfScript(final int codePoint, final int scriptId) {
|
||||
switch (scriptId) {
|
||||
case SCRIPT_ARABIC:
|
||||
// Arabic letters can be in any of the following blocks:
|
||||
// Arabic U+0600..U+06FF
|
||||
// Arabic Supplement, Thaana U+0750..U+077F, U+0780..U+07BF
|
||||
// Arabic Extended-A U+08A0..U+08FF
|
||||
// Arabic Presentation Forms-A U+FB50..U+FDFF
|
||||
// Arabic Presentation Forms-B U+FE70..U+FEFF
|
||||
return (codePoint >= 0x600 && codePoint <= 0x6FF)
|
||||
|| (codePoint >= 0x750 && codePoint <= 0x7BF)
|
||||
|| (codePoint >= 0x8A0 && codePoint <= 0x8FF)
|
||||
|| (codePoint >= 0xFB50 && codePoint <= 0xFDFF)
|
||||
|| (codePoint >= 0xFE70 && codePoint <= 0xFEFF);
|
||||
case SCRIPT_ARMENIAN:
|
||||
// Armenian letters are in the Armenian unicode block, U+0530..U+058F and
|
||||
// Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the Armenian part
|
||||
// of that block, which is U+FB13..U+FB17.
|
||||
return (codePoint >= 0x530 && codePoint <= 0x58F
|
||||
|| codePoint >= 0xFB13 && codePoint <= 0xFB17);
|
||||
case SCRIPT_BENGALI:
|
||||
// Bengali unicode block is U+0980..U+09FF
|
||||
return (codePoint >= 0x980 && codePoint <= 0x9FF);
|
||||
case SCRIPT_BULGARIAN:
|
||||
case SCRIPT_CYRILLIC:
|
||||
// All Cyrillic characters are in the 400~52F block. There are some in the upper
|
||||
// Unicode range, but they are archaic characters that are not used in modern
|
||||
// Russian and are not used by our dictionary.
|
||||
return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint);
|
||||
case SCRIPT_DEVANAGARI:
|
||||
// Devanagari unicode block is +0900..U+097F
|
||||
return (codePoint >= 0x900 && codePoint <= 0x97F);
|
||||
case SCRIPT_GEORGIAN:
|
||||
// Georgian letters are in the Georgian unicode block, U+10A0..U+10FF,
|
||||
// or Georgian supplement block, U+2D00..U+2D2F
|
||||
return (codePoint >= 0x10A0 && codePoint <= 0x10FF
|
||||
|| codePoint >= 0x2D00 && codePoint <= 0x2D2F);
|
||||
case SCRIPT_GREEK:
|
||||
// Greek letters are either in the 370~3FF range (Greek & Coptic), or in the
|
||||
// 1F00~1FFF range (Greek extended). Our dictionary contains both sort of characters.
|
||||
// Our dictionary also contains a few words with 0xF2; it would be best to check
|
||||
// if that's correct, but a web search does return results for these words so
|
||||
// they are probably okay.
|
||||
return (codePoint >= 0x370 && codePoint <= 0x3FF)
|
||||
|| (codePoint >= 0x1F00 && codePoint <= 0x1FFF)
|
||||
|| codePoint == 0xF2;
|
||||
case SCRIPT_HEBREW:
|
||||
// Hebrew letters are in the Hebrew unicode block, which spans from U+0590 to U+05FF,
|
||||
// or in the Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the
|
||||
// Hebrew part of that block, which is U+FB1D..U+FB4F.
|
||||
return (codePoint >= 0x590 && codePoint <= 0x5FF
|
||||
|| codePoint >= 0xFB1D && codePoint <= 0xFB4F);
|
||||
case SCRIPT_KANNADA:
|
||||
// Kannada unicode block is U+0C80..U+0CFF
|
||||
return (codePoint >= 0xC80 && codePoint <= 0xCFF);
|
||||
case SCRIPT_KHMER:
|
||||
// Khmer letters are in unicode block U+1780..U+17FF, and the Khmer symbols block
|
||||
// is U+19E0..U+19FF
|
||||
return (codePoint >= 0x1780 && codePoint <= 0x17FF
|
||||
|| codePoint >= 0x19E0 && codePoint <= 0x19FF);
|
||||
case SCRIPT_LAO:
|
||||
// The Lao block is U+0E80..U+0EFF
|
||||
return (codePoint >= 0xE80 && codePoint <= 0xEFF);
|
||||
case SCRIPT_LATIN:
|
||||
// Our supported latin script dictionaries (EFIGS) at the moment only include
|
||||
// characters in the C0, C1, Latin Extended A and B, IPA extensions unicode
|
||||
// blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF,
|
||||
// so the below is a very efficient way to test for it. As for the 0-0x3F, it's
|
||||
// excluded from isLetter anyway.
|
||||
return codePoint <= 0x2AF && Character.isLetter(codePoint);
|
||||
case SCRIPT_MALAYALAM:
|
||||
// Malayalam unicode block is U+0D00..U+0D7F
|
||||
return (codePoint >= 0xD00 && codePoint <= 0xD7F);
|
||||
case SCRIPT_MYANMAR:
|
||||
// Myanmar has three unicode blocks :
|
||||
// Myanmar U+1000..U+109F
|
||||
// Myanmar extended-A U+AA60..U+AA7F
|
||||
// Myanmar extended-B U+A9E0..U+A9FF
|
||||
return (codePoint >= 0x1000 && codePoint <= 0x109F
|
||||
|| codePoint >= 0xAA60 && codePoint <= 0xAA7F
|
||||
|| codePoint >= 0xA9E0 && codePoint <= 0xA9FF);
|
||||
case SCRIPT_SINHALA:
|
||||
// Sinhala unicode block is U+0D80..U+0DFF
|
||||
return (codePoint >= 0xD80 && codePoint <= 0xDFF);
|
||||
case SCRIPT_TAMIL:
|
||||
// Tamil unicode block is U+0B80..U+0BFF
|
||||
return (codePoint >= 0xB80 && codePoint <= 0xBFF);
|
||||
case SCRIPT_TELUGU:
|
||||
// Telugu unicode block is U+0C00..U+0C7F
|
||||
return (codePoint >= 0xC00 && codePoint <= 0xC7F);
|
||||
case SCRIPT_THAI:
|
||||
// Thai unicode block is U+0E00..U+0E7F
|
||||
return (codePoint >= 0xE00 && codePoint <= 0xE7F);
|
||||
case SCRIPT_HANGUL:
|
||||
return (codePoint >= 0xAC00 && codePoint <= 0xD7A3
|
||||
|| codePoint >= 0x3131 && codePoint <= 0x318E
|
||||
|| codePoint >= 0x1100 && codePoint <= 0x11FF
|
||||
|| codePoint >= 0xA960 && codePoint <= 0xA97C
|
||||
|| codePoint >= 0xD7B0 && codePoint <= 0xD7C6
|
||||
|| codePoint >= 0xD7CB && codePoint <= 0xD7FB);
|
||||
case SCRIPT_GUJARATI:
|
||||
// Gujarati unicode block is U+0A80..U+0AFF
|
||||
return (codePoint >= 0xA80 && codePoint <= 0xAFF);
|
||||
case SCRIPT_UNKNOWN:
|
||||
return true;
|
||||
default:
|
||||
// Should never come here
|
||||
throw new RuntimeException("Impossible value of script: " + scriptId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param locale spell checker locale
|
||||
* @return internal Latin IME script code that maps to a language code
|
||||
* {@see http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes}
|
||||
*/
|
||||
public static int getScriptFromSpellCheckerLocale(final Locale locale) {
|
||||
// need special treatment of serbian latin and hinglish, which would get detected as cyrillic /
|
||||
if (locale.toString().toLowerCase(Locale.ENGLISH).endsWith("_zz"))
|
||||
return ScriptUtils.SCRIPT_LATIN;
|
||||
String language = locale.getLanguage();
|
||||
Integer script = mLanguageCodeToScriptCode.get(language);
|
||||
if (script == null) {
|
||||
// Default to Latin.
|
||||
script = mLanguageCodeToScriptCode.get("");
|
||||
}
|
||||
return script;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
* modified
|
||||
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||
*/
|
||||
package org.dslul.openboard.inputmethod.latin.utils
|
||||
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* A class to help with handling different writing scripts.
|
||||
*/
|
||||
object ScriptUtils {
|
||||
// Unicode scripts (ISO 15924), incomplete
|
||||
const val SCRIPT_UNKNOWN = "" // Used for hardware keyboards
|
||||
const val SCRIPT_ARABIC = "Arab"
|
||||
const val SCRIPT_ARMENIAN = "Armn"
|
||||
const val SCRIPT_BENGALI = "Beng"
|
||||
const val SCRIPT_CYRILLIC = "Cyrl"
|
||||
const val SCRIPT_DEVANAGARI = "Deva"
|
||||
const val SCRIPT_GEORGIAN = "Geor"
|
||||
const val SCRIPT_GREEK = "Grek"
|
||||
const val SCRIPT_HEBREW = "Hebr"
|
||||
const val SCRIPT_KANNADA = "Knda"
|
||||
const val SCRIPT_KHMER = "Khmr"
|
||||
const val SCRIPT_LAO = "Laoo"
|
||||
const val SCRIPT_LATIN = "Latn"
|
||||
const val SCRIPT_MALAYALAM = "Mlym"
|
||||
const val SCRIPT_MYANMAR = "Mymr"
|
||||
const val SCRIPT_SINHALA = "Sinh"
|
||||
const val SCRIPT_TAMIL = "Taml"
|
||||
const val SCRIPT_TELUGU = "Telu"
|
||||
const val SCRIPT_THAI = "Thai"
|
||||
const val SCRIPT_HANGUL = "Hang"
|
||||
const val SCRIPT_GUJARATI = "Gujr"
|
||||
|
||||
@JvmStatic
|
||||
fun scriptSupportsUppercase(locale: Locale): Boolean {
|
||||
// only Latin, Cyrillic, Greek and Armenian have upper/lower case
|
||||
// https://unicode.org/faq/casemap_charprop.html#3
|
||||
return when (locale.script()) {
|
||||
SCRIPT_LATIN, SCRIPT_CYRILLIC, SCRIPT_GREEK, SCRIPT_ARMENIAN -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the code point is a letter that makes sense for the specified
|
||||
* locale for this spell checker.
|
||||
* The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml
|
||||
* and is limited to EFIGS languages and Russian.
|
||||
* Hence at the moment this explicitly tests for Cyrillic characters or Latin characters
|
||||
* as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isLetterPartOfScript(codePoint: Int, script: String): Boolean {
|
||||
return when (script) {
|
||||
SCRIPT_ARABIC ->
|
||||
// Arabic letters can be in any of the following blocks:
|
||||
// Arabic U+0600..U+06FF
|
||||
// Arabic Supplement, Thaana U+0750..U+077F, U+0780..U+07BF
|
||||
// Arabic Extended-A U+08A0..U+08FF
|
||||
// Arabic Presentation Forms-A U+FB50..U+FDFF
|
||||
// Arabic Presentation Forms-B U+FE70..U+FEFF
|
||||
codePoint in 0x600..0x6FF
|
||||
|| codePoint in 0x750..0x7BF
|
||||
|| codePoint in 0x8A0..0x8FF
|
||||
|| codePoint in 0xFB50..0xFDFF
|
||||
|| codePoint in 0xFE70..0xFEFF
|
||||
SCRIPT_ARMENIAN ->
|
||||
// Armenian letters are in the Armenian unicode block, U+0530..U+058F and
|
||||
// Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the Armenian part
|
||||
// of that block, which is U+FB13..U+FB17.
|
||||
codePoint in 0x530..0x58F || codePoint in 0xFB13..0xFB17
|
||||
SCRIPT_BENGALI ->
|
||||
// Bengali unicode block is U+0980..U+09FF
|
||||
codePoint in 0x980..0x9FF
|
||||
SCRIPT_CYRILLIC ->
|
||||
// All Cyrillic characters are in the 400~52F block. There are some in the upper
|
||||
// Unicode range, but they are archaic characters that are not used in modern
|
||||
// Russian and are not used by our dictionary.
|
||||
codePoint in 0x400..0x52F && Character.isLetter(codePoint)
|
||||
SCRIPT_DEVANAGARI ->
|
||||
// Devanagari unicode block is +0900..U+097F
|
||||
codePoint in 0x900..0x97F
|
||||
SCRIPT_GEORGIAN ->
|
||||
// Georgian letters are in the Georgian unicode block, U+10A0..U+10FF,
|
||||
// or Georgian supplement block, U+2D00..U+2D2F
|
||||
codePoint in 0x10A0..0x10FF || codePoint in 0x2D00..0x2D2F
|
||||
SCRIPT_GREEK ->
|
||||
// Greek letters are either in the 370~3FF range (Greek & Coptic), or in the
|
||||
// 1F00~1FFF range (Greek extended). Our dictionary contains both sort of characters.
|
||||
// Our dictionary also contains a few words with 0xF2; it would be best to check
|
||||
// if that's correct, but a web search does return results for these words so
|
||||
// they are probably okay.
|
||||
codePoint in 0x370..0x3FF || codePoint in 0x1F00..0x1FFF || codePoint == 0xF2
|
||||
SCRIPT_HEBREW ->
|
||||
// Hebrew letters are in the Hebrew unicode block, which spans from U+0590 to U+05FF,
|
||||
// or in the Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the
|
||||
// Hebrew part of that block, which is U+FB1D..U+FB4F.
|
||||
codePoint in 0x590..0x5FF || codePoint in 0xFB1D..0xFB4F
|
||||
SCRIPT_KANNADA ->
|
||||
// Kannada unicode block is U+0C80..U+0CFF
|
||||
codePoint in 0xC80..0xCFF
|
||||
SCRIPT_KHMER ->
|
||||
// Khmer letters are in unicode block U+1780..U+17FF, and the Khmer symbols block
|
||||
// is U+19E0..U+19FF
|
||||
codePoint in 0x1780..0x17FF || codePoint in 0x19E0..0x19FF
|
||||
SCRIPT_LAO ->
|
||||
// The Lao block is U+0E80..U+0EFF
|
||||
codePoint in 0xE80..0xEFF
|
||||
SCRIPT_LATIN ->
|
||||
// Our supported latin script dictionaries (EFIGS) at the moment only include
|
||||
// characters in the C0, C1, Latin Extended A and B, IPA extensions unicode
|
||||
// blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF,
|
||||
// so the below is a very efficient way to test for it. As for the 0-0x3F, it's
|
||||
// excluded from isLetter anyway.
|
||||
codePoint <= 0x2AF && Character.isLetter(codePoint)
|
||||
SCRIPT_MALAYALAM ->
|
||||
// Malayalam unicode block is U+0D00..U+0D7F
|
||||
codePoint in 0xD00..0xD7F
|
||||
SCRIPT_MYANMAR ->
|
||||
// Myanmar has three unicode blocks :
|
||||
// Myanmar U+1000..U+109F
|
||||
// Myanmar extended-A U+AA60..U+AA7F
|
||||
// Myanmar extended-B U+A9E0..U+A9FF
|
||||
codePoint in 0x1000..0x109F || codePoint in 0xAA60..0xAA7F || codePoint in 0xA9E0..0xA9FF
|
||||
SCRIPT_SINHALA ->
|
||||
// Sinhala unicode block is U+0D80..U+0DFF
|
||||
codePoint in 0xD80..0xDFF
|
||||
SCRIPT_TAMIL ->
|
||||
// Tamil unicode block is U+0B80..U+0BFF
|
||||
codePoint in 0xB80..0xBFF
|
||||
SCRIPT_TELUGU ->
|
||||
// Telugu unicode block is U+0C00..U+0C7F
|
||||
codePoint in 0xC00..0xC7F
|
||||
SCRIPT_THAI ->
|
||||
// Thai unicode block is U+0E00..U+0E7F
|
||||
codePoint in 0xE00..0xE7F
|
||||
SCRIPT_HANGUL -> codePoint in 0xAC00..0xD7A3
|
||||
|| codePoint in 0x3131..0x318E
|
||||
|| codePoint in 0x1100..0x11FF
|
||||
|| codePoint in 0xA960..0xA97C
|
||||
|| codePoint in 0xD7B0..0xD7C6
|
||||
|| codePoint in 0xD7CB..0xD7FB
|
||||
SCRIPT_GUJARATI ->
|
||||
// Gujarati unicode block is U+0A80..U+0AFF
|
||||
codePoint in 0xA80..0xAFF
|
||||
SCRIPT_UNKNOWN -> true
|
||||
else -> throw RuntimeException("Unknown value of script: $script")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the locale script with fallback to default scripts
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Locale.script(): String {
|
||||
if (script.isNotEmpty()) return script
|
||||
if (country.equals("ZZ", true)) {
|
||||
Log.w("ScriptUtils", "old _ZZ locale found: $this")
|
||||
return SCRIPT_LATIN
|
||||
}
|
||||
return when (language) {
|
||||
"ar", "ur", "fa" -> SCRIPT_ARABIC
|
||||
"hy" -> SCRIPT_ARMENIAN
|
||||
"bn" -> SCRIPT_BENGALI
|
||||
"sr", "mk", "ru", "uk", "mn", "be", "kk", "ky", "bg" -> SCRIPT_CYRILLIC
|
||||
"ka" -> SCRIPT_GEORGIAN
|
||||
"el" -> SCRIPT_GREEK
|
||||
"iw" -> SCRIPT_HEBREW
|
||||
"km" -> SCRIPT_KHMER
|
||||
"lo" -> SCRIPT_LAO
|
||||
"ml" -> SCRIPT_MALAYALAM
|
||||
"my" -> SCRIPT_MYANMAR
|
||||
"si" -> SCRIPT_SINHALA
|
||||
"ta" -> SCRIPT_TAMIL
|
||||
"te" -> SCRIPT_TELUGU
|
||||
"th" -> SCRIPT_THAI
|
||||
"ko" -> SCRIPT_HANGUL
|
||||
"hi", "mr", "ne" -> SCRIPT_DEVANAGARI
|
||||
"kn" -> SCRIPT_KANNADA
|
||||
"gu" -> SCRIPT_GUJARATI
|
||||
else -> SCRIPT_LATIN // use as fallback
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import android.content.Context;
|
|||
import android.content.res.Resources;
|
||||
import android.view.inputmethod.InputMethodSubtype;
|
||||
|
||||
import org.dslul.openboard.inputmethod.compat.ConfigurationCompatKt;
|
||||
import org.dslul.openboard.inputmethod.latin.R;
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.common.StringUtils;
|
||||
|
@ -96,36 +97,37 @@ public final class SubtypeLocaleUtils {
|
|||
}
|
||||
|
||||
final String[] exceptionalLocaleInRootLocale = res.getStringArray(R.array.subtype_locale_displayed_in_root_locale);
|
||||
for (final String localeString : exceptionalLocaleInRootLocale) {
|
||||
final String resourceName = SUBTYPE_NAME_RESOURCE_IN_ROOT_LOCALE_PREFIX + localeString;
|
||||
for (final String languageTag : exceptionalLocaleInRootLocale) {
|
||||
final String resourceName = SUBTYPE_NAME_RESOURCE_IN_ROOT_LOCALE_PREFIX + languageTag.replace('-', '_');
|
||||
final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
|
||||
sExceptionalLocaleDisplayedInRootLocale.put(localeString, resId);
|
||||
sExceptionalLocaleDisplayedInRootLocale.put(languageTag, resId);
|
||||
}
|
||||
|
||||
final String[] exceptionalLocales = res.getStringArray(R.array.subtype_locale_exception_keys);
|
||||
for (final String localeString : exceptionalLocales) {
|
||||
final String resourceName = SUBTYPE_NAME_RESOURCE_PREFIX + localeString;
|
||||
for (final String languageTag : exceptionalLocales) {
|
||||
final String resourceName = SUBTYPE_NAME_RESOURCE_PREFIX + languageTag.replace('-', '_');
|
||||
final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
|
||||
sExceptionalLocaleToNameIdsMap.put(localeString, resId);
|
||||
final String resourceNameWithLayout = SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX + localeString;
|
||||
sExceptionalLocaleToNameIdsMap.put(languageTag, resId);
|
||||
final String resourceNameWithLayout = SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX + languageTag.replace('-', '_');
|
||||
final int resIdWithLayout = res.getIdentifier(resourceNameWithLayout, null, RESOURCE_PACKAGE_NAME);
|
||||
sExceptionalLocaleToWithLayoutNameIdsMap.put(localeString, resIdWithLayout);
|
||||
sExceptionalLocaleToWithLayoutNameIdsMap.put(languageTag, resIdWithLayout);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isExceptionalLocale(final String localeString) {
|
||||
return sExceptionalLocaleToNameIdsMap.containsKey(localeString);
|
||||
public static boolean isExceptionalLocale(final Locale locale) {
|
||||
return sExceptionalLocaleToNameIdsMap.containsKey(locale.toLanguageTag());
|
||||
}
|
||||
|
||||
private static final String getNoLanguageLayoutKey(final String keyboardLayoutName) {
|
||||
private static String getNoLanguageLayoutKey(final String keyboardLayoutName) {
|
||||
return NO_LANGUAGE + "_" + keyboardLayoutName;
|
||||
}
|
||||
|
||||
public static int getSubtypeNameId(final String localeString, final String keyboardLayoutName) {
|
||||
if (isExceptionalLocale(localeString)) {
|
||||
return sExceptionalLocaleToWithLayoutNameIdsMap.get(localeString);
|
||||
public static int getSubtypeNameId(final Locale locale, final String keyboardLayoutName) {
|
||||
final String languageTag = locale.toLanguageTag();
|
||||
if (isExceptionalLocale(locale)) {
|
||||
return sExceptionalLocaleToWithLayoutNameIdsMap.get(languageTag);
|
||||
}
|
||||
final String key = NO_LANGUAGE.equals(localeString)
|
||||
final String key = NO_LANGUAGE.equals(languageTag)
|
||||
? getNoLanguageLayoutKey(keyboardLayoutName)
|
||||
: keyboardLayoutName;
|
||||
final Integer nameId = sKeyboardLayoutToNameIdsMap.get(key);
|
||||
|
@ -133,53 +135,54 @@ public final class SubtypeLocaleUtils {
|
|||
}
|
||||
|
||||
@NonNull
|
||||
public static Locale getDisplayLocaleOfSubtypeLocale(@NonNull final String localeString) {
|
||||
if (NO_LANGUAGE.equals(localeString)) {
|
||||
return sResources.getConfiguration().locale;
|
||||
public static Locale getDisplayLocaleOfSubtypeLocale(@NonNull final Locale locale) {
|
||||
final String languageTag = locale.toLanguageTag();
|
||||
if (NO_LANGUAGE.equals(languageTag)) {
|
||||
return ConfigurationCompatKt.locale(sResources.getConfiguration());
|
||||
}
|
||||
if (sExceptionalLocaleDisplayedInRootLocale.containsKey(localeString)) {
|
||||
if (sExceptionalLocaleDisplayedInRootLocale.containsKey(languageTag)) {
|
||||
return Locale.ROOT;
|
||||
}
|
||||
return LocaleUtils.constructLocaleFromString(localeString);
|
||||
return locale;
|
||||
}
|
||||
|
||||
public static String getSubtypeLocaleDisplayNameInSystemLocale(
|
||||
@NonNull final String localeString) {
|
||||
final Locale displayLocale = sResources.getConfiguration().locale;
|
||||
return getSubtypeLocaleDisplayNameInternal(localeString, displayLocale);
|
||||
public static String getSubtypeLocaleDisplayNameInSystemLocale(@NonNull final Locale locale) {
|
||||
final Locale displayLocale = ConfigurationCompatKt.locale(sResources.getConfiguration());
|
||||
return getSubtypeLocaleDisplayNameInternal(locale, displayLocale);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getSubtypeLocaleDisplayName(@NonNull final String localeString) {
|
||||
final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(localeString);
|
||||
return getSubtypeLocaleDisplayNameInternal(localeString, displayLocale);
|
||||
public static String getSubtypeLocaleDisplayName(@NonNull final Locale locale) {
|
||||
final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(locale);
|
||||
return getSubtypeLocaleDisplayNameInternal(locale, displayLocale);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getSubtypeLanguageDisplayName(@NonNull final String localeString) {
|
||||
final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(localeString);
|
||||
final String languageString;
|
||||
if (sExceptionalLocaleDisplayedInRootLocale.containsKey(localeString)) {
|
||||
languageString = localeString;
|
||||
public static String getSubtypeLanguageDisplayName(@NonNull final Locale locale) {
|
||||
final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(locale);
|
||||
final Locale languageLocale;
|
||||
if (sExceptionalLocaleDisplayedInRootLocale.containsKey(locale.toLanguageTag())) {
|
||||
languageLocale = locale;
|
||||
} else {
|
||||
languageString = LocaleUtils.constructLocaleFromString(localeString).getLanguage();
|
||||
languageLocale = LocaleUtils.constructLocale(locale.getLanguage());
|
||||
}
|
||||
return getSubtypeLocaleDisplayNameInternal(languageString, displayLocale);
|
||||
return getSubtypeLocaleDisplayNameInternal(languageLocale, displayLocale);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String getSubtypeLocaleDisplayNameInternal(@NonNull final String localeString,
|
||||
private static String getSubtypeLocaleDisplayNameInternal(@NonNull final Locale locale,
|
||||
@NonNull final Locale displayLocale) {
|
||||
if (NO_LANGUAGE.equals(localeString)) {
|
||||
final String languageTag = locale.toLanguageTag();
|
||||
if (NO_LANGUAGE.equals(locale.toLanguageTag())) {
|
||||
// No language subtype should be displayed in system locale.
|
||||
return sResources.getString(R.string.subtype_no_language);
|
||||
}
|
||||
final Integer exceptionalNameResId;
|
||||
if (displayLocale.equals(Locale.ROOT)
|
||||
&& sExceptionalLocaleDisplayedInRootLocale.containsKey(localeString)) {
|
||||
exceptionalNameResId = sExceptionalLocaleDisplayedInRootLocale.get(localeString);
|
||||
} else if (sExceptionalLocaleToNameIdsMap.containsKey(localeString)) {
|
||||
exceptionalNameResId = sExceptionalLocaleToNameIdsMap.get(localeString);
|
||||
&& sExceptionalLocaleDisplayedInRootLocale.containsKey(languageTag)) {
|
||||
exceptionalNameResId = sExceptionalLocaleDisplayedInRootLocale.get(languageTag);
|
||||
} else if (sExceptionalLocaleToNameIdsMap.containsKey(languageTag)) {
|
||||
exceptionalNameResId = sExceptionalLocaleToNameIdsMap.get(languageTag);
|
||||
} else {
|
||||
exceptionalNameResId = null;
|
||||
}
|
||||
|
@ -188,8 +191,7 @@ public final class SubtypeLocaleUtils {
|
|||
if (exceptionalNameResId != null) {
|
||||
displayName = RunInLocaleKt.runInLocale(sResources, displayLocale, res -> res.getString(exceptionalNameResId));
|
||||
} else {
|
||||
displayName = LocaleUtils.constructLocaleFromString(localeString)
|
||||
.getDisplayName(displayLocale);
|
||||
displayName = locale.getDisplayName(displayLocale);
|
||||
}
|
||||
return StringUtils.capitalizeFirstCodePoint(displayName, displayLocale);
|
||||
}
|
||||
|
@ -218,13 +220,13 @@ public final class SubtypeLocaleUtils {
|
|||
if (subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)) {
|
||||
return subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME);
|
||||
}
|
||||
return getSubtypeLocaleDisplayNameInternal(subtype.getLocale(), displayLocale);
|
||||
return getSubtypeLocaleDisplayNameInternal(SubtypeUtilsKt.locale(subtype), displayLocale);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getSubtypeDisplayNameInSystemLocale(
|
||||
@NonNull final InputMethodSubtype subtype) {
|
||||
final Locale displayLocale = sResources.getConfiguration().locale;
|
||||
final Locale displayLocale = ConfigurationCompatKt.locale(sResources.getConfiguration());
|
||||
return getSubtypeDisplayNameInternal(subtype, displayLocale);
|
||||
}
|
||||
|
||||
|
@ -233,7 +235,7 @@ public final class SubtypeLocaleUtils {
|
|||
if (subtype == null) {
|
||||
return "<null subtype>";
|
||||
}
|
||||
return getSubtypeLocale(subtype) + "/" + getKeyboardLayoutSetName(subtype);
|
||||
return SubtypeUtilsKt.locale(subtype) + "/" + getKeyboardLayoutSetName(subtype);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -259,12 +261,6 @@ public final class SubtypeLocaleUtils {
|
|||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Locale getSubtypeLocale(@NonNull final InputMethodSubtype subtype) {
|
||||
final String localeString = subtype.getLocale();
|
||||
return LocaleUtils.constructLocaleFromString(localeString);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getKeyboardLayoutSetDisplayName(@NonNull final InputMethodSubtype subtype) {
|
||||
final String layoutName = getKeyboardLayoutSetName(subtype);
|
||||
|
|
|
@ -13,16 +13,18 @@ import androidx.core.content.edit
|
|||
import org.dslul.openboard.inputmethod.keyboard.KeyboardSwitcher
|
||||
import org.dslul.openboard.inputmethod.latin.R
|
||||
import org.dslul.openboard.inputmethod.latin.RichInputMethodManager
|
||||
import org.dslul.openboard.inputmethod.latin.common.Constants
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils.constructLocale
|
||||
import org.dslul.openboard.inputmethod.latin.define.DebugFlags
|
||||
import org.dslul.openboard.inputmethod.latin.settings.Settings
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils.script
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.LinkedHashMap
|
||||
|
||||
/** @return enabled subtypes. If no subtypes are enabled, but a contextForFallback is provided,
|
||||
* subtypes for system locales will be returned, or en_US if none found. */
|
||||
* subtypes for system locales will be returned, or en-US if none found. */
|
||||
fun getEnabledSubtypes(prefs: SharedPreferences, fallback: Boolean = false): List<InputMethodSubtype> {
|
||||
require(initialized)
|
||||
if (prefs.getBoolean(Settings.PREF_USE_SYSTEM_LOCALES, true))
|
||||
|
@ -37,6 +39,22 @@ fun getAllAvailableSubtypes(): List<InputMethodSubtype> {
|
|||
return resourceSubtypesByLocale.values.flatten() + additionalSubtypes
|
||||
}
|
||||
|
||||
fun getMatchingLayoutSetNameForLocale(locale: Locale): String {
|
||||
val subtypes = resourceSubtypesByLocale.values.flatten()
|
||||
val name = LocaleUtils.getBestMatch(locale, subtypes) { it.locale() }?.getExtraValueOf(Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET)
|
||||
if (name != null) return name
|
||||
return when (locale.script()) {
|
||||
ScriptUtils.SCRIPT_LATIN -> "qwerty"
|
||||
ScriptUtils.SCRIPT_ARMENIAN -> "armenian_phonetic"
|
||||
ScriptUtils.SCRIPT_CYRILLIC -> "ru"
|
||||
ScriptUtils.SCRIPT_GREEK -> "greek"
|
||||
ScriptUtils.SCRIPT_HEBREW -> "hebrew"
|
||||
ScriptUtils.SCRIPT_GEORGIAN -> "georgian"
|
||||
ScriptUtils.SCRIPT_BENGALI -> "bengali_unijoy"
|
||||
else -> throw RuntimeException("Wrong script supplied: ${locale.script()}")
|
||||
};
|
||||
}
|
||||
|
||||
fun addEnabledSubtype(prefs: SharedPreferences, newSubtype: InputMethodSubtype) {
|
||||
require(initialized)
|
||||
val subtypeString = newSubtype.prefString()
|
||||
|
@ -46,7 +64,7 @@ fun addEnabledSubtype(prefs: SharedPreferences, newSubtype: InputMethodSubtype)
|
|||
|
||||
if (newSubtype !in enabledSubtypes) {
|
||||
enabledSubtypes.add(newSubtype)
|
||||
enabledSubtypes.sortBy { it.locale() } // for consistent order
|
||||
enabledSubtypes.sortBy { it.locale().toLanguageTag() } // for consistent order
|
||||
RichInputMethodManager.getInstance().refreshSubtypeCaches()
|
||||
}
|
||||
}
|
||||
|
@ -77,15 +95,15 @@ fun removeAdditionalSubtype(prefs: SharedPreferences, resources: Resources, subt
|
|||
|
||||
fun getSelectedSubtype(prefs: SharedPreferences): InputMethodSubtype {
|
||||
require(initialized)
|
||||
val subtypeString = prefs.getString(Settings.PREF_SELECTED_INPUT_STYLE, "")!!.split(LOCALE_LAYOUT_SEPARATOR)
|
||||
val localeAndLayout = prefs.getString(Settings.PREF_SELECTED_INPUT_STYLE, "")!!.toLocaleAndLayout()
|
||||
val subtypes = if (prefs.getBoolean(Settings.PREF_USE_SYSTEM_LOCALES, true)) getDefaultEnabledSubtypes()
|
||||
else enabledSubtypes
|
||||
val subtype = subtypes.firstOrNull { subtypeString.first() == it.locale() && subtypeString.last() == SubtypeLocaleUtils.getKeyboardLayoutSetName(it) }
|
||||
val subtype = subtypes.firstOrNull { localeAndLayout.first == it.locale() && localeAndLayout.second == SubtypeLocaleUtils.getKeyboardLayoutSetName(it) }
|
||||
?: subtypes.firstOrNull()
|
||||
if (subtype == null) {
|
||||
val defaultSubtypes = getDefaultEnabledSubtypes()
|
||||
return defaultSubtypes.firstOrNull { subtypeString.first() == it.locale() && subtypeString.last() == SubtypeLocaleUtils.getKeyboardLayoutSetName(it) }
|
||||
?: defaultSubtypes.firstOrNull { subtypeString.first().substringBefore("_") == it.locale().substringBefore("_") && subtypeString.last() == SubtypeLocaleUtils.getKeyboardLayoutSetName(it) }
|
||||
return defaultSubtypes.firstOrNull { localeAndLayout.first == it.locale() && localeAndLayout.second == SubtypeLocaleUtils.getKeyboardLayoutSetName(it) }
|
||||
?: defaultSubtypes.firstOrNull { localeAndLayout.first.language == it.locale().language && localeAndLayout.second == SubtypeLocaleUtils.getKeyboardLayoutSetName(it) }
|
||||
?: defaultSubtypes.first()
|
||||
}
|
||||
return subtype
|
||||
|
@ -93,7 +111,7 @@ fun getSelectedSubtype(prefs: SharedPreferences): InputMethodSubtype {
|
|||
|
||||
fun setSelectedSubtype(prefs: SharedPreferences, subtype: InputMethodSubtype) {
|
||||
val subtypeString = subtype.prefString()
|
||||
if (subtype.locale().isEmpty() || prefs.getString(Settings.PREF_SELECTED_INPUT_STYLE, "") == subtypeString)
|
||||
if (subtype.locale().toLanguageTag().isEmpty() || prefs.getString(Settings.PREF_SELECTED_INPUT_STYLE, "") == subtypeString)
|
||||
return
|
||||
prefs.edit { putString(Settings.PREF_SELECTED_INPUT_STYLE, subtypeString) }
|
||||
}
|
||||
|
@ -123,12 +141,12 @@ fun getSystemLocales(): List<Locale> {
|
|||
return systemLocales
|
||||
}
|
||||
|
||||
fun hasMatchingSubtypeForLocaleString(localeString: String): Boolean {
|
||||
fun hasMatchingSubtypeForLocale(locale: Locale): Boolean {
|
||||
require(initialized)
|
||||
return !resourceSubtypesByLocale[localeString].isNullOrEmpty()
|
||||
return !resourceSubtypesByLocale[locale].isNullOrEmpty()
|
||||
}
|
||||
|
||||
fun getAvailableSubtypeLocaleStrings(): Collection<String> {
|
||||
fun getAvailableSubtypeLocales(): Collection<Locale> {
|
||||
require(initialized)
|
||||
return resourceSubtypesByLocale.keys
|
||||
}
|
||||
|
@ -151,29 +169,29 @@ fun init(context: Context) {
|
|||
private fun getDefaultEnabledSubtypes(): List<InputMethodSubtype> {
|
||||
if (systemSubtypes.isNotEmpty()) return systemSubtypes
|
||||
val subtypes = systemLocales.mapNotNull { locale ->
|
||||
val localeString = locale.toString()
|
||||
val subtypesOfLocale = resourceSubtypesByLocale[localeString]
|
||||
?: resourceSubtypesByLocale[localeString.substringBefore("_")] // fall back to language matching the subtype
|
||||
?: localeString.substringBefore("_").let { language -> // fall back to languages matching subtype language
|
||||
resourceSubtypesByLocale.firstNotNullOfOrNull {
|
||||
if (it.key.substringBefore("_") == language)
|
||||
it.value
|
||||
else null
|
||||
}
|
||||
}
|
||||
val subtypesOfLocale = resourceSubtypesByLocale[locale]
|
||||
// get best match
|
||||
?: LocaleUtils.getBestMatch(locale, resourceSubtypesByLocale.keys) {it}?.let { resourceSubtypesByLocale[it] }
|
||||
subtypesOfLocale?.firstOrNull()
|
||||
}
|
||||
if (subtypes.isEmpty()) {
|
||||
// hardcoded fallback for weird cases
|
||||
systemSubtypes.add(resourceSubtypesByLocale["en_US"]!!.first())
|
||||
// hardcoded fallback to en-US for weird cases
|
||||
systemSubtypes.add(resourceSubtypesByLocale[Locale.US]!!.first())
|
||||
} else {
|
||||
systemSubtypes.addAll(subtypes)
|
||||
}
|
||||
return systemSubtypes
|
||||
}
|
||||
|
||||
/** string for for identifying a subtype, does not contain all necessary information to actually create it */
|
||||
private fun InputMethodSubtype.prefString() =
|
||||
locale() + LOCALE_LAYOUT_SEPARATOR + SubtypeLocaleUtils.getKeyboardLayoutSetName(this)
|
||||
locale().toLanguageTag() + LOCALE_LAYOUT_SEPARATOR + SubtypeLocaleUtils.getKeyboardLayoutSetName(this)
|
||||
|
||||
private fun String.toLocaleAndLayout(): Pair<Locale, String> =
|
||||
substringBefore(LOCALE_LAYOUT_SEPARATOR).constructLocale() to substringAfter(LOCALE_LAYOUT_SEPARATOR)
|
||||
|
||||
private fun Pair<Locale, String>.prefString() =
|
||||
first.toLanguageTag() + LOCALE_LAYOUT_SEPARATOR + second
|
||||
|
||||
private fun loadResourceSubtypes(resources: Resources) {
|
||||
val xml = resources.getXml(R.xml.method)
|
||||
|
@ -185,8 +203,8 @@ private fun loadResourceSubtypes(resources: Resources) {
|
|||
val icon = xml.getAttributeResourceValue(namespace, "icon", 0)
|
||||
val label = xml.getAttributeResourceValue(namespace, "label", 0)
|
||||
val subtypeId = xml.getAttributeIntValue(namespace, "subtypeId", 0)
|
||||
val locale = xml.getAttributeValue(namespace, "imeSubtypeLocale").intern()
|
||||
val languageTag = xml.getAttributeValue(namespace, "languageTag")
|
||||
val localeString = xml.getAttributeValue(namespace, "imeSubtypeLocale").intern()
|
||||
val languageTag = xml.getAttributeValue(namespace, "languageTag").intern()
|
||||
val imeSubtypeMode = xml.getAttributeValue(namespace, "imeSubtypeMode")
|
||||
val imeSubtypeExtraValue = xml.getAttributeValue(namespace, "imeSubtypeExtraValue").intern()
|
||||
val isAsciiCapable = xml.getAttributeBooleanValue(namespace, "isAsciiCapable", false)
|
||||
|
@ -195,12 +213,14 @@ private fun loadResourceSubtypes(resources: Resources) {
|
|||
b.setSubtypeNameResId(label)
|
||||
if (subtypeId != 0)
|
||||
b.setSubtypeId(subtypeId)
|
||||
b.setSubtypeLocale(locale)
|
||||
b.setSubtypeLocale(localeString)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && languageTag != null)
|
||||
b.setLanguageTag(languageTag)
|
||||
b.setSubtypeMode(imeSubtypeMode)
|
||||
b.setSubtypeExtraValue(imeSubtypeExtraValue)
|
||||
b.setIsAsciiCapable(isAsciiCapable)
|
||||
val locale = if (languageTag.isEmpty()) localeString.constructLocale()
|
||||
else languageTag.constructLocale()
|
||||
resourceSubtypesByLocale.getOrPut(locale) { ArrayList(2) }.add(b.build())
|
||||
}
|
||||
eventType = xml.next()
|
||||
|
@ -223,7 +243,7 @@ private fun removeInvalidCustomSubtypes(context: Context) {
|
|||
additionalSubtypes.forEach {
|
||||
val name = it.substringAfter(":").substringBefore(":")
|
||||
if (!name.startsWith(CUSTOM_LAYOUT_PREFIX)) return@forEach
|
||||
if (name !in customSubtypeFiles)
|
||||
if (customSubtypeFiles?.contains(name) != true)
|
||||
subtypesToRemove.add(it)
|
||||
}
|
||||
if (subtypesToRemove.isEmpty()) return
|
||||
|
@ -235,30 +255,29 @@ private fun removeInvalidCustomSubtypes(context: Context) {
|
|||
private fun loadEnabledSubtypes(context: Context) {
|
||||
val prefs = DeviceProtectedUtils.getSharedPreferences(context)
|
||||
val subtypeStrings = prefs.getString(Settings.PREF_ENABLED_INPUT_STYLES, "")!!
|
||||
.split(SUBTYPE_SEPARATOR).filter { it.isNotEmpty() }.map { it.split(LOCALE_LAYOUT_SEPARATOR) }
|
||||
.split(SUBTYPE_SEPARATOR).filter { it.isNotEmpty() }.map { it.toLocaleAndLayout() }
|
||||
|
||||
for (localeAndLayout in subtypeStrings) {
|
||||
require(localeAndLayout.size == 2)
|
||||
val subtypesForLocale = resourceSubtypesByLocale[localeAndLayout.first()]
|
||||
val subtypesForLocale = resourceSubtypesByLocale[localeAndLayout.first]
|
||||
if (subtypesForLocale == null) {
|
||||
val message = "no resource subtype for $localeAndLayout"
|
||||
Log.w(TAG, message)
|
||||
if (DebugFlags.DEBUG_ENABLED)
|
||||
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
|
||||
else // don't remove in debug mode
|
||||
removeEnabledSubtype(prefs, localeAndLayout.joinToString(LOCALE_LAYOUT_SEPARATOR))
|
||||
removeEnabledSubtype(prefs, localeAndLayout.prefString())
|
||||
continue
|
||||
}
|
||||
|
||||
val subtype = subtypesForLocale.firstOrNull { SubtypeLocaleUtils.getKeyboardLayoutSetName(it) == localeAndLayout.last() }
|
||||
?: additionalSubtypes.firstOrNull { it.locale() == localeAndLayout.first() && SubtypeLocaleUtils.getKeyboardLayoutSetName(it) == localeAndLayout.last() }
|
||||
val subtype = subtypesForLocale.firstOrNull { SubtypeLocaleUtils.getKeyboardLayoutSetName(it) == localeAndLayout.second }
|
||||
?: additionalSubtypes.firstOrNull { it.locale() == localeAndLayout.first && SubtypeLocaleUtils.getKeyboardLayoutSetName(it) == localeAndLayout.second }
|
||||
if (subtype == null) {
|
||||
val message = "subtype $localeAndLayout could not be loaded"
|
||||
Log.w(TAG, message)
|
||||
if (DebugFlags.DEBUG_ENABLED)
|
||||
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
|
||||
else // don't remove in debug mode
|
||||
removeEnabledSubtype(prefs, localeAndLayout.joinToString(LOCALE_LAYOUT_SEPARATOR))
|
||||
removeEnabledSubtype(prefs, localeAndLayout.prefString())
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -287,7 +306,7 @@ private fun removeEnabledSubtype(prefs: SharedPreferences, subtypeString: String
|
|||
var initialized = false
|
||||
private set
|
||||
private val enabledSubtypes = mutableListOf<InputMethodSubtype>()
|
||||
private val resourceSubtypesByLocale = LinkedHashMap<String, MutableList<InputMethodSubtype>>(100)
|
||||
private val resourceSubtypesByLocale = LinkedHashMap<Locale, MutableList<InputMethodSubtype>>(100)
|
||||
private val additionalSubtypes = mutableListOf<InputMethodSubtype>()
|
||||
private val systemLocales = mutableListOf<Locale>()
|
||||
private val systemSubtypes = mutableListOf<InputMethodSubtype>()
|
||||
|
@ -295,11 +314,3 @@ private val systemSubtypes = mutableListOf<InputMethodSubtype>()
|
|||
private const val SUBTYPE_SEPARATOR = ";"
|
||||
private const val LOCALE_LAYOUT_SEPARATOR = ":"
|
||||
private const val TAG = "SubtypeSettings"
|
||||
|
||||
@Suppress("deprecation") // it's deprecated, but no replacement for API < 24
|
||||
// todo: subtypes should now have language tags -> use them for api >= 24
|
||||
// but only replace subtype-related usage, otherwise the api mess will be horrible
|
||||
// maybe rather return a locale instead of a string...
|
||||
// is this acceptable for performance? any place where there are many call to locale()?
|
||||
// see also InputMethodSubtypeCompatUtils
|
||||
fun InputMethodSubtype.locale() = locale
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package org.dslul.openboard.inputmethod.latin.utils
|
||||
|
||||
import android.os.Build
|
||||
import android.view.inputmethod.InputMethodSubtype
|
||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils.constructLocale
|
||||
import java.util.Locale
|
||||
|
||||
fun InputMethodSubtype.locale(): Locale {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
if (languageTag.isNotEmpty())
|
||||
return languageTag.constructLocale()
|
||||
}
|
||||
@Suppress("deprecation") return locale.constructLocale()
|
||||
}
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Engels (VK)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Engels (VS)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Spaans (VS)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serwies (Latyns)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serwies (Latyns)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">Engels (VK) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">Engels (VS) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">Spaans (VS) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">Hinglish (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">Serwies (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">Hinglish (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">Serwies (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (Tradisioneel)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (Kompak)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Geen taal nie (alfabet)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"እንግሊዘኛ (የታላቋ ብሪታንያ)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"እንግሊዘኛ (ዩ.ኤስ)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"ስፓኒሽኛ (ዩኤስ)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"ሂንግሊሽ"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"ሰርብያኛ (ላቲን)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"ሂንግሊሽ"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"ሰርብያኛ (ላቲን)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"እንግሊዝኛ (ዩኬ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"እንግሊዝኛ (አሜሪካ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"ስፓኒሽ (አሜሪካ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ሂንግሊሽ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"ሰርቢያኛ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"ሂንግሊሽ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"ሰርቢያኛ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ተለምዷዊ)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (እስግ)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"ምንም ቋንቋ (ፊደላት)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"الإنجليزية (المملكة المتحدة)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"الإنجليزية (الولايات المتحدة)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"الإسبانية (الأميركية)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"هنجليزية"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"الصربية (اللاتينية)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"هنجليزية"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"الصربية (اللاتينية)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">الإنجليزية (المملكة المتحدة) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">الإنجليزية (الولايات المتحدة) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">الإسبانية (الولايات المتحدة) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">الهنجليزية (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">الصربية (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">الهنجليزية (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">الصربية (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (التقليدية)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (مضغوط)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"بدون لغة (أبجدية)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"İngilis (BK)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"İngilis (ABŞ)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"İspan (ABŞ)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hingilis"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serb (Latın)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hingilis"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serb (Latın)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"İngilis (Britaniya) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"İngilis (Amerika) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"İspan (Amerika) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hingilis (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serb (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hingilis (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Serb (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Ənənəvi)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Kompakt)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Dil yoxdur (Əlifba)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"engleski (UK)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"engleski (SAD)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"španski (SAD)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"hengleski"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"srpski (latinica)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"hengleski"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"srpski (latinica)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"engleski (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"engleski (SAD) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"španski (SAD) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"hengleski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"srpski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"hengleski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"srpski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicionalni)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktna)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Nema jezika (abeceda)"</string>
|
||||
|
|
|
@ -53,13 +53,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Англійская (Вялікабрытанія)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Англійская (ЗША)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Іспанская (ЗША)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Хінгліш"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Сербская (Лацініца)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Хінгліш"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Сербская (Лацініца)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">Англійская (Вялікабрытанія) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">Англійская (ЗША) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">Іспанская (ЗША) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">Хінгліш (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">Сербская (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">Хінгліш (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">Сербская (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (Традыцыйная)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (Кампактная)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Стандартная (Лацініца)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"английски (Великобритания)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"английски (САЩ)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"испански (САЩ)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Хинглиш"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Сръбска (латиница)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Хинглиш"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Сръбска (латиница)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"английски (Великобр.) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"английски (САЩ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"испански (САЩ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Сръбска (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Сръбска (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиционна клавиатура)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (компактна)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Без език (латиница)"</string>
|
||||
|
|
|
@ -102,7 +102,7 @@
|
|||
<string name="subtype_en_GB">ইংরেজি (ইউকে)</string>
|
||||
<string name="subtype_en_US">ইংরেজি (ইউএস)</string>
|
||||
<string name="subtype_es_US">স্প্যানিশ (ইউএস)</string>
|
||||
<string name="subtype_hi_ZZ">হিংলিশ</string>
|
||||
<string name="subtype_hi_Latn">হিংলিশ</string>
|
||||
<string name="hidden_features_title">প্রচ্ছন্ন বৈশিষ্ট্য</string>
|
||||
<string name="hidden_features_summary">অপরিদৃষ্ট হতে পারে এমন বৈশিষ্ট্যের বর্ণনা</string>
|
||||
<string name="hidden_features_text">ডিভাইস সুরক্ষিত স্টোরেজ</string>
|
||||
|
@ -126,13 +126,12 @@
|
|||
► রুট উপলব্ধতাসহ ম্যানুয়াল ব্যাকআপ করা ব্যবহারকারীদের জন্য: Android 7 থেকে শেয়ারড্ প্রেফারেন্স ফাইল ডিফল্ট জায়গা নয়। কারণ অ্যাপ %s ব্যবহার করছে। <br>
|
||||
ডিভাইস আনলকের আগে সেটিংস খোলার জন্য এটি প্রয়োজনীয়, যেমন: বুট করার সময়। <br>
|
||||
ফাইলটি /data/user_de/0/package_id/shared_prefs/ থাকে। যদিও এটা ডিভাইস এবং অ্যান্ড্রয়েড সংস্করণের উপরে নির্ভর করে।</string>
|
||||
<string name="subtype_hu_ZZ">হাঙ্গেরীয় (QWERTY)</string>
|
||||
<string name="subtype_sr_ZZ">সার্বীয় (ল্যাটিন)</string>
|
||||
<string name="subtype_sr_Latn">সার্বীয় (ল্যাটিন)</string>
|
||||
<string name="subtype_with_layout_en_GB">ইংরেজি (ইউকে) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US">ইংরেজি (ইউএস) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US">স্প্যানিশ (ইউএস) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ">হিংলিশ (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ">সার্বিয়ান (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn">হিংলিশ (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn">সার্বিয়ান (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (প্রথাগত)</string>
|
||||
<string name="subtype_with_layout_bn_BD"><xliff:g id="LANGUAGE_NAME" example="Bangla">%s</xliff:g> (অক্ষর)</string>
|
||||
<string name="subtype_generic_compact"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (সংক্ষিপ্ত)</string>
|
||||
|
|
|
@ -53,13 +53,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"engleski (UK)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"engleski (SAD)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"španski (SAD)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hindu engleski"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Srpski (latinica)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hindu engleski"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Srpski (latinica)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Engleski (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Engleski (SAD) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"Španski (SAD) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindu engleski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Srpski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hindu engleski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Srpski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicionalan)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktan)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Nema jezika (abeceda)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"anglès (Regne Unit)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"anglès (EUA)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Espanyol (EUA)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">Hinglès</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbi (llatí)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">Hinglès</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbi (llatí)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">Anglès (Regne Unit) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">Angles (EUA) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">Espanyol (EUA) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">Hinglès (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">Serbi (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">Hinglès (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">Serbi (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (Tradicional)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (Compacte)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"cap idioma (alfabet)"</string>
|
||||
|
|
|
@ -53,13 +53,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Angličtina (Velká Británie)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Angličtina (USA)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Španělština (USA)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Srbština (Latinka)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Srbština (Latinka)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Angličtina (Velká Británie) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Angličtina (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"Španělština (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Srbština (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Srbština (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradiční)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Kompaktní)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Standardní (Latinka)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"engelsk (Storbritannien)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"engelsk (USA)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Spansk (USA)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbisk (latinsk)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbisk (latinsk)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">Engelsk (Storbritannien) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">Engelsk (USA) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">Spansk (USA) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">Hinglish (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">Serbisk (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">Hinglish (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">Serbisk (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (traditionelt)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (kompakt)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Intet sprog (Alfabet)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Englisch (UK)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Englisch (USA)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Spanisch (USA)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">Hinglisch</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbisch (Lateinisch)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">Hinglisch</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbisch (Lateinisch)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">Englisch (GB) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">Englisch (US) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">Spanisch (USA) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">Hinglisch (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">Serbisch (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">Hinglisch (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">Serbisch (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (Traditionell)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (Kompakt)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Keine Sprache (lat. Alphabet)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Αγγλικά (ΗΒ)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Αγγλικά (ΗΠΑ)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Ισπανικά (ΗΠΑ)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Σερβικά (Λατινικά)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Σερβικά (Λατινικά)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Αγγλικά (Ηνωμένο Βασίλειο) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Αγγλικά (ΗΠΑ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"Ισπανικά (ΗΠΑ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Σερβικά (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Σερβικά (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Παραδοσιακά)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Συμπαγές)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Καμία γλώσσα (Αλφάβητο)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"English (UK)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"English (US)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Spanish (US)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbian (Latin)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbian (Latin)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"English (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"English (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spanish (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Compact)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"English (UK)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"English (US)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Spanish (US)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbian (Latin)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbian (Latin)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"English (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"English (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spanish (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Compact)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"English (UK)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"English (US)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Spanish (US)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbian (Latin)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbian (Latin)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">English (UK) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">English (US) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">Spanish (US) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">Hinglish (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">Serbian (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">Hinglish (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">Serbian (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (Traditional)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (Compact)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"English (UK)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"English (US)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Spanish (US)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbian (Latin)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbian (Latin)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"English (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"English (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spanish (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Compact)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"English (UK)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"English (US)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Spanish (US)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbian (Latin)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbian (Latin)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"English (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"English (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spanish (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Compact)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Inglés (Reino Unido)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Inglés (EE.UU.)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Español (EE.UU.)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbio (latino)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbio (latino)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Inglés, Reino Unido (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Inglés, EE. UU. (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"Español, EE. UU. (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Serbio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compacto)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">Inglés (Reino Unido)</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">Inglés (EE.UU.)</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Español (EE.UU.)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">inglés indio</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbio (latino)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">inglés indio</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbio (latino)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">Inglés (Reino Unido) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">Inglés (EE.UU.) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">Español (EE.UU.)) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">inglés indio (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">Serbio (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">inglés indio (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">Serbio (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (Tradicional)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (Compacto)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Inglise (UK)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Inglise (USA)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"hispaania (USA)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hindi-inglise"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbia (ladina)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hindi-inglise"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbia (ladina)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Inglise (Ühendk.) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Inglise (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"Hispaania (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi-inglise (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hindi-inglise (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditsiooniline)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktne)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Keel puudub (tähestik)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Ingelesa (Erresuma Batua)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Ingelesa (AEB)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Gaztelania (AEB)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglisha"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbiarra (latindarra)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglisha"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbiarra (latindarra)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">Ingelesa (Erresuma Batua) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">Ingelesa (Estatu Batuak) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">Gaztelania (AEB) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">Hinglisha (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">Serbiarra (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">Hinglisha (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">Serbiarra (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (Tradizionala)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (Trinkoa)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Ez dago hizkuntzarik (alfabetoa)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"انگلیسی (بریتانیا)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"انگلیسی (امریکا)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"اسپانیایی (امریکا)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"هندی انگلیسی"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"صربی (لاتین)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"هندی انگلیسی"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"صربی (لاتین)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">انگلیسی (بریتانیا) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">انگلیسی (امریکا) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">اسپانیایی (امریکا) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">هندی انگلیسی (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">صربی (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">هندی انگلیسی (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">صربی (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (سنتی)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (فشرده)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"بدون زبان (حروف الفبا)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"englanti (Iso-Britannia)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"englanti (Yhdysvallat)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"espanja (Yhdysvallat)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hindienglanti"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"serbialainen (latinal.)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hindienglanti"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"serbialainen (latinal.)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"englanti (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"englanti (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"espanja (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindienglanti (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"serbialainen (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hindienglanti (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"serbialainen (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (perinteinen)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tiivis)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Ei kieltä (aakkoset)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Anglais (britannique)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Anglais (États-Unis)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Espagnol (États-Unis)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbe (latin)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbe (latin)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"anglais (Royaume-Uni) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"anglais (États-Unis) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"espagnol (États-Unis) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbe (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Serbe (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionnel)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compact)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Aucune langue (alphabet)"</string>
|
||||
|
|
|
@ -58,13 +58,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Anglais (Royaume-Uni)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Anglais (États-Unis)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Espagnol (États-Unis)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hindi/Anglais"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbe (latin)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hindi/Anglais"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbe (latin)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">Anglais (Royaume-Uni) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">Anglais (États-Unis) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">Espagnol (États-Unis) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">Hindi/Anglais (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">Serbe (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">Hindi/Anglais (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">Serbe (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (Traditionnel)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (Compact)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Aucune langue (latin)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Inglés (Reino Unido)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Inglés (EUA)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Español (EUA)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbio (alfabeto latino)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbio (alfabeto latino)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Inglés (Reino Unido) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Inglés (EUA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"Español (EUA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Serbio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compacto)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"અંગ્રેજી (યુકે)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"અંગ્રેજી (યુ એસ)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"સ્પેનિશ (US)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"હિંગ્લિશ"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"સર્બિયન (લેટિન)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"હિંગ્લિશ"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"સર્બિયન (લેટિન)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"અંગ્રેજી (યુકે) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"અંગ્રેજી (યુએસ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"સ્પેનિશ (યુએસ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"હિંગ્લિશ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"સર્બિયન (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"હિંગ્લિશ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"સર્બિયન (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (પરંપરાગત)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (કોમ્પેક્ટ)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"ભાષા નથી (આલ્ફાબેટ)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"अंग्रेज़ी (यूके)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"अंग्रेज़ी (यूएस)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"स्पेनिश (यूएस)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"हिंग्लिश"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"सर्बियाई (लैटिन)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"हिंग्लिश"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"सर्बियाई (लैटिन)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"अंग्रेज़ी (यूके) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"अंग्रेज़ी (यूएस) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"स्पेनिश (यूएस) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"हिंग्लिश (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"सर्बियाई (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"हिंग्लिश (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"सर्बियाई (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (पारंपरिक)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (संक्षिप्त)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"भाषा उपलब्ध नहीं है (लैटिन वर्णाक्षर)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Engleski (UK)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Engleski (SAD)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">Španjolski (SAD)</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Srpski (latinica)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Srpski (latinica)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">Engleski (UK) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">Engleski (US) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">Španjolski (US) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">Hinglish (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">Srpski (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">Hinglish (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">Srpski (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (traditionalno)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (kompaktno)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">Neodređen jezik (abeceda)</string>
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--Generated by crowdin.com-->
|
||||
<!--
|
||||
Copyright (C) 2015 The Android Open Source Project
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<!-- This string is displayed in the description for a keyboard type. It refers specifically to
|
||||
the Latin alphabet, as opposed to Cyrillic, Arabic, Hebrew or other scripts.
|
||||
When the device is configured to use a language using a script other than the Latin alphabet, the
|
||||
user still needs a keyboard that can input Latin characters for passwords or login names for
|
||||
example, and a way to switch to this Latin alphabet keyboard. This string is the description for
|
||||
this keyboard, so users of other scripts should understand when they read this that it represents a
|
||||
keyboard that is meant for them to be able to enter Latin characters as opposed to the script they
|
||||
are used to. This keyboard does not provide a dictionary, and it is not tied to any specific
|
||||
language among those that use the Latin alphabet. This keyboard is laid out in the Bépo
|
||||
disposition rather than other common dispositions for Latin languages. [CHAR LIMIT=25] -->
|
||||
<string name="subtype_no_language_bepo">Ábécé (Bépo)</string>
|
||||
</resources>
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"angol (brit)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"angol (amerikai)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"spanyol (USA)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish (hindi-angol)"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Szerb (latin)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish (hindi-angol)"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Szerb (latin)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">Angol (UK) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">Angol (USA) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">Spanyol (USA) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (hindi-angol, <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">Szerb (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hinglish (hindi-angol, <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">Szerb (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (hagyományos)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompakt)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Nincs nyelv (ábécé)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"angol (brit)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"angol (amerikai)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"spanyol (USA)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish (hindi-angol)"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Szerb (latin)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish (hindi-angol)"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Szerb (latin)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"angol (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"angol (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"spanyol (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (hindi-angol, <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Szerb (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hinglish (hindi-angol, <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Szerb (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (hagyományos)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompakt)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Nincs nyelv (ábécé)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Անգլերեն (ՄԹ)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Անգլերեն (ԱՄՆ)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Իսպաներեն (ԱՄՆ)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Հինգլիշ"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Սերբերեն (Լատինական)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Հինգլիշ"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Սերբերեն (Լատինական)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Անգլերեն (ՄԹ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Անգլերեն (ԱՄՆ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"Իսպաներեն (ԱՄՆ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Հինգլիշ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Սերբերեն (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Հինգլիշ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Սերբերեն (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ավանդական)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (սեղմ)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Ոչ մի լեզվով (Այբուբեն)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Inggris (Inggris)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Inggris (AS)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Spanyol (AS)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbia (Latin)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbia (Latin)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">Inggris (UK) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">Inggris (US) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">Spanyol (US) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">Hinglish (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">Serbia (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">Hinglish (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">Serbia (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (Tradisional)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (Ringkas)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Tidak ada bahasa (Abjad)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"Enskt (Bretland)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"Enskt (Bandaríkin)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Spænskt (US)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbneskt (latneskt)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Serbneskt (latneskt)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Enskt (Bretland) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Enskt (Bandaríkin) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spænskt (Bandaríkin) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbneskt (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Serbneskt (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (hefðbundið)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (lítið)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Ekkert tungumál (stafróf)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">Inglese (Regno Unito)</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">Inglese (Stati Uniti)</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">Spagnolo (Stati Uniti)</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">Serbo (Latino)</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">Serbo (Latino)</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">Inglese (Regno Unito) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">Inglese (Stati Uniti) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">Spagnolo (Stati Uniti) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">Hinglish (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">Serbo (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">Hinglish (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">Serbo (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (tradizionale)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (compatto)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Nessuna lingua (alfabeto)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"אנגלית (בריטניה)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"אנגלית (ארה\"ב)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"ספרדית (ארצות הברית)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"אנגלית הודית"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"סרבית (באותיות לטיניות)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"אנגלית הודית"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"סרבית (באותיות לטיניות)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">אנגלית (בריטניה) <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">אנגלית (ארה\"ב) <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">ספרדית (ארה\"ב) <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">אנגלית הודית <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">סרבית ) <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">אנגלית הודית <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">סרבית ) <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (מסורתית)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (קומפקטית)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"ללא שפה (אלף-בית)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"英語 (英国)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"英語 (米国)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"スペイン語 (米国)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"ヒングリッシュ"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"セルビア語(ラテン文字)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"ヒングリッシュ"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"セルビア語(ラテン文字)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"英語(英国)(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"英語(米国)(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"スペイン語(米国)(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ヒングリッシュ(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"セルビア語(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"ヒングリッシュ(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"セルビア語(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(伝統言語)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(コンパクト)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"言語なし(アルファベット)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"ინგლისური (გართ. სამ.)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"ინგლისური (აშშ)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"ესპანური (აშშ)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"ჰინგლისური"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"სერბული (ლათინური)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"ჰინგლისური"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"სერბული (ლათინური)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">ინგლისური (UK) <xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g></string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">ინგლისური (აშშ) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">ესპანური (აშშ) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">ჰინგლისური (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">სერბული (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">ჰინგლისური (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">სერბული (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (ტრადიციული)</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (კომპაქტური)</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"ენის გარეშე (ლათინური ანბანი)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"ағылшын (ҰБ)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"ағылшын (АҚШ)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"Испан (АҚШ)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Хинглиш"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"Серб (латын жазуы)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Хинглиш"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"Серб (латын жазуы)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Ағылшын (Құрама Корольдік) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Ағылшын (АҚШ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"Испан (АҚШ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Серб (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"Серб (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (дәстүрлі)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (шағын)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"Тіл жоқ (әліпби)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"អង់គ្លេស (អង់គ្លេស)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"អង់គ្លេស (សហរដ្ឋអាមេរិក)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"អេស្ប៉ាញ (សហរដ្ឋអាមេរិក)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"សើប (ឡាតាំង)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"Hinglish"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"សើប (ឡាតាំង)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"អង់គ្លេស (ចក្រភពអង់គ្លេស) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"អង់គ្លេស (អាមេរិក) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"អេស្ប៉ាញ (អាមេរិក) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"សើប (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"សើប (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (អក្សរពេញ)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (បង្រួម)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"គ្មានភាសា (អក្សរក្រម)"</string>
|
||||
|
|
|
@ -52,13 +52,13 @@
|
|||
<string name="subtype_en_GB" msgid="88170601942311355">"ಇಂಗ್ಲಿಷ್ (ಯುಕೆ)"</string>
|
||||
<string name="subtype_en_US" msgid="6160452336634534239">"ಇಂಗ್ಲಿಷ್ (US)"</string>
|
||||
<string name="subtype_es_US" msgid="5583145191430180200">"ಸ್ಪ್ಯಾನಿಷ್ (US)"</string>
|
||||
<string name="subtype_hi_ZZ" msgid="8860448146262798623">"ಹಿಂಗ್ಲಿಷ್"</string>
|
||||
<string name="subtype_sr_ZZ" msgid="9059219552986034343">"ಸರ್ಬಿಯನ್ (ಲ್ಯಾಟಿನ್)"</string>
|
||||
<string name="subtype_hi_Latn" msgid="8860448146262798623">"ಹಿಂಗ್ಲಿಷ್"</string>
|
||||
<string name="subtype_sr_Latn" msgid="9059219552986034343">"ಸರ್ಬಿಯನ್ (ಲ್ಯಾಟಿನ್)"</string>
|
||||
<string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"ಇಂಗ್ಲಿಷ್ (ಯುಕೆ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_en_US" msgid="8809311287529805422">"ಇಂಗ್ಲಿಷ್ (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_es_US" msgid="510930471167541338">"ಸ್ಪ್ಯಾನಿಷ್ (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ಹಿಂಗ್ಲಿಷ್ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"ಸರ್ಬಿಯನ್ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_hi_Latn" msgid="6827402953860547044">"ಹಿಂಗ್ಲಿಷ್ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_with_layout_sr_Latn" msgid="2859024772719772407">"ಸರ್ಬಿಯನ್ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
|
||||
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ಸಾಂಪ್ರದಾಯಿಕ)"</string>
|
||||
<string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ಕಾಂಪ್ಯಾಕ್ಟ್)"</string>
|
||||
<string name="subtype_no_language" msgid="7137390094240139495">"ಯಾವುದೇ ಭಾಷೆಯಿಲ್ಲ (ವರ್ಣಮಾಲೆ)"</string>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue