workaround for getTextBeforeCursor() taking too much time and causing unresponsive UI
This commit is contained in:
parent
f1b063c84b
commit
2b2b8e9a1c
19 changed files with 156 additions and 14 deletions
|
|
@ -9,6 +9,7 @@ import android.view.inputmethod.InputConnection;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import io.github.sspanak.tt9.R;
|
||||||
import io.github.sspanak.tt9.db.DataStore;
|
import io.github.sspanak.tt9.db.DataStore;
|
||||||
import io.github.sspanak.tt9.db.words.DictionaryLoader;
|
import io.github.sspanak.tt9.db.words.DictionaryLoader;
|
||||||
import io.github.sspanak.tt9.hacks.InputType;
|
import io.github.sspanak.tt9.hacks.InputType;
|
||||||
|
|
@ -237,21 +238,35 @@ public class TraditionalT9 extends MainViewHandler {
|
||||||
Logger.d(LOG_TAG, "===> Shutdown completed");
|
Logger.d(LOG_TAG, "===> Shutdown completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTimeout(int startId) {
|
public void onTimeout(int startId) {
|
||||||
onZombie();
|
onZombie();
|
||||||
super.onTimeout(startId);
|
super.onTimeout(startId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean onNumber(int key, boolean hold, int repeat) {
|
protected boolean onNumber(int key, boolean hold, int repeat) {
|
||||||
if (InputModeKind.isPredictive(mInputMode) && DictionaryLoader.autoLoad(this, mLanguage)) {
|
if (InputModeKind.isPredictive(mInputMode) && DictionaryLoader.autoLoad(this, mLanguage)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (textField.shouldReportConnectionErrors()) {
|
||||||
|
UI.toastLongSingle(getApplicationContext(), R.string.error_unstable_input_connection);
|
||||||
|
}
|
||||||
return super.onNumber(key, hold, repeat);
|
return super.onNumber(key, hold, repeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onBackspace(int repeat) {
|
||||||
|
if (textField.shouldReportConnectionErrors()) {
|
||||||
|
UI.toastLongSingle(getApplicationContext(), R.string.error_unstable_input_connection);
|
||||||
|
}
|
||||||
|
return super.onBackspace(repeat);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TraditionalT9 getFinalContext() {
|
protected TraditionalT9 getFinalContext() {
|
||||||
return this;
|
return this;
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@ import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||||
public class InputField {
|
public class InputField {
|
||||||
public static final int IME_ACTION_ENTER = EditorInfo.IME_MASK_ACTION + 1;
|
public static final int IME_ACTION_ENTER = EditorInfo.IME_MASK_ACTION + 1;
|
||||||
|
|
||||||
protected final InputConnection connection;
|
@Nullable protected final InputConnection connection;
|
||||||
protected final EditorInfo field;
|
@Nullable protected final EditorInfo field;
|
||||||
|
|
||||||
|
|
||||||
protected InputField(InputConnection inputConnection, EditorInfo inputField) {
|
protected InputField(@Nullable InputConnection inputConnection, @Nullable EditorInfo inputField) {
|
||||||
connection = inputConnection;
|
connection = inputConnection;
|
||||||
field = inputField;
|
field = inputField;
|
||||||
}
|
}
|
||||||
|
|
@ -85,11 +85,11 @@ public class InputField {
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public Language getLanguage(ArrayList<Integer> allowedLanguageIds) {
|
public Language getLanguage(ArrayList<Integer> allowedLanguageIds) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N || field == null || field.hintLocales == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; field.hintLocales != null && i < field.hintLocales.size(); i++) {
|
for (int i = 0; i < field.hintLocales.size(); i++) {
|
||||||
Language lang = LanguageCollection.getByLanguageCode(field.hintLocales.get(i).getLanguage());
|
Language lang = LanguageCollection.getByLanguageCode(field.hintLocales.get(i).getLanguage());
|
||||||
if (lang != null && allowedLanguageIds.contains(lang.getId())) {
|
if (lang != null && allowedLanguageIds.contains(lang.getId())) {
|
||||||
return lang;
|
return lang;
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,13 @@ import io.github.sspanak.tt9.ime.modes.InputMode;
|
||||||
import io.github.sspanak.tt9.languages.Language;
|
import io.github.sspanak.tt9.languages.Language;
|
||||||
import io.github.sspanak.tt9.languages.LanguageKind;
|
import io.github.sspanak.tt9.languages.LanguageKind;
|
||||||
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
|
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
|
||||||
|
import io.github.sspanak.tt9.util.InputConnectionTools;
|
||||||
import io.github.sspanak.tt9.util.Logger;
|
import io.github.sspanak.tt9.util.Logger;
|
||||||
import io.github.sspanak.tt9.util.Text;
|
import io.github.sspanak.tt9.util.Text;
|
||||||
|
|
||||||
public class TextField extends InputField {
|
public class TextField extends InputField {
|
||||||
|
@NonNull private final InputConnectionTools connectionTools;
|
||||||
|
|
||||||
private CharSequence composingText = "";
|
private CharSequence composingText = "";
|
||||||
private final boolean isComposingSupported;
|
private final boolean isComposingSupported;
|
||||||
private final boolean isNonText;
|
private final boolean isNonText;
|
||||||
|
|
@ -30,6 +33,8 @@ public class TextField extends InputField {
|
||||||
public TextField(InputConnection inputConnection, EditorInfo inputField) {
|
public TextField(InputConnection inputConnection, EditorInfo inputField) {
|
||||||
super(inputConnection, inputField);
|
super(inputConnection, inputField);
|
||||||
|
|
||||||
|
connectionTools = new InputConnectionTools(inputConnection);
|
||||||
|
|
||||||
InputType inputType = new InputType(inputConnection, inputField);
|
InputType inputType = new InputType(inputConnection, inputField);
|
||||||
isComposingSupported = !inputType.isNumeric() && !inputType.isLimited() && !inputType.isRustDesk() && !inputType.isDeezerSearchBar();
|
isComposingSupported = !inputType.isNumeric() && !inputType.isLimited() && !inputType.isRustDesk() && !inputType.isDeezerSearchBar();
|
||||||
isNonText = !inputType.isText();
|
isNonText = !inputType.isText();
|
||||||
|
|
@ -37,14 +42,14 @@ public class TextField extends InputField {
|
||||||
|
|
||||||
|
|
||||||
public String getStringAfterCursor(int numberOfChars) {
|
public String getStringAfterCursor(int numberOfChars) {
|
||||||
CharSequence character = connection != null ? connection.getTextAfterCursor(numberOfChars, 0) : null;
|
CharSequence chars = connectionTools.getTextAfterCursor(numberOfChars, 0);
|
||||||
return character != null ? character.toString() : "";
|
return chars != null ? chars.toString() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getStringBeforeCursor(int numberOfChars) {
|
public String getStringBeforeCursor(int numberOfChars) {
|
||||||
CharSequence character = connection != null ? connection.getTextBeforeCursor(numberOfChars, 0) : null;
|
CharSequence chars = connectionTools.getTextBeforeCursor(numberOfChars, 0);
|
||||||
return character != null ? character.toString() : "";
|
return chars != null ? chars.toString() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -206,7 +211,7 @@ public class TextField extends InputField {
|
||||||
String searchText = " " + word;
|
String searchText = " " + word;
|
||||||
|
|
||||||
connection.beginBatchEdit();
|
connection.beginBatchEdit();
|
||||||
CharSequence beforeText = connection.getTextBeforeCursor(searchText.length(), 0);
|
String beforeText = getStringBeforeCursor(searchText.length());
|
||||||
if (beforeText == null || !beforeText.equals(searchText)) {
|
if (beforeText == null || !beforeText.equals(searchText)) {
|
||||||
connection.endBatchEdit();
|
connection.endBatchEdit();
|
||||||
return;
|
return;
|
||||||
|
|
@ -229,7 +234,7 @@ public class TextField extends InputField {
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.beginBatchEdit();
|
connection.beginBatchEdit();
|
||||||
CharSequence beforeText = connection.getTextBeforeCursor(word.length(), 0);
|
String beforeText = getStringBeforeCursor(word.length());
|
||||||
if (beforeText == null || !beforeText.equals(word)) {
|
if (beforeText == null || !beforeText.equals(word)) {
|
||||||
connection.endBatchEdit();
|
connection.endBatchEdit();
|
||||||
return;
|
return;
|
||||||
|
|
@ -391,4 +396,9 @@ public class TextField extends InputField {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean shouldReportConnectionErrors() {
|
||||||
|
return connectionTools.shouldReportTimeout();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ public class SettingsStore extends SettingsUI {
|
||||||
public final static int DICTIONARY_DOWNLOAD_READ_TIMEOUT = 10000; // ms
|
public final static int DICTIONARY_DOWNLOAD_READ_TIMEOUT = 10000; // ms
|
||||||
public final static int DICTIONARY_IMPORT_BATCH_SIZE = 5000; // words
|
public final static int DICTIONARY_IMPORT_BATCH_SIZE = 5000; // words
|
||||||
public final static int DICTIONARY_IMPORT_PROGRESS_UPDATE_TIME = 250; // ms
|
public final static int DICTIONARY_IMPORT_PROGRESS_UPDATE_TIME = 250; // ms
|
||||||
|
public final static int INPUT_CONNECTION_ERRORS_MAX = 3;
|
||||||
|
public final static int INPUT_CONNECTION_OPERATIONS_TIMEOUT = 150; // ms
|
||||||
public final static int RESIZE_THROTTLING_TIME = 60; // ms
|
public final static int RESIZE_THROTTLING_TIME = 60; // ms
|
||||||
public final static byte SLOW_QUERY_TIME = 50; // ms
|
public final static byte SLOW_QUERY_TIME = 50; // ms
|
||||||
public final static int SLOW_QUERY_TIMEOUT = 3000; // ms
|
public final static int SLOW_QUERY_TIMEOUT = 3000; // ms
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ public class UI {
|
||||||
toastLong(context, msg);
|
toastLong(context, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void toastShortSingle(@NonNull Context context, @NonNull String uniqueId, @NonNull String message) {
|
public static void toastSingle(@NonNull Context context, @NonNull String uniqueId, @NonNull String message, boolean isShort) {
|
||||||
Toast toast = singleToasts.get(uniqueId);
|
Toast toast = singleToasts.get(uniqueId);
|
||||||
|
|
||||||
if (toast != null) {
|
if (toast != null) {
|
||||||
|
|
@ -105,14 +105,21 @@ public class UI {
|
||||||
}
|
}
|
||||||
|
|
||||||
// we recreate the toast, because if set new text, when it is fading out, it is ignored
|
// we recreate the toast, because if set new text, when it is fading out, it is ignored
|
||||||
toast = Toast.makeText(context, message, Toast.LENGTH_SHORT);
|
toast = Toast.makeText(context, message, isShort ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG);
|
||||||
toast.show();
|
toast.show();
|
||||||
|
|
||||||
singleToasts.put(uniqueId, toast);
|
singleToasts.put(uniqueId, toast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void toastShortSingle(@NonNull Context context, @NonNull String uniqueId, @NonNull String message) {
|
||||||
|
toastSingle(context, uniqueId, message, true);
|
||||||
|
}
|
||||||
|
|
||||||
public static void toastShortSingle(@NonNull Context context, int resourceId) {
|
public static void toastShortSingle(@NonNull Context context, int resourceId) {
|
||||||
toastShortSingle(context, String.valueOf(resourceId), context.getString(resourceId));
|
toastSingle(context, String.valueOf(resourceId), context.getString(resourceId), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void toastLongSingle(@NonNull Context context, int resourceId) {
|
||||||
|
toastSingle(context, String.valueOf(resourceId), context.getString(resourceId), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
package io.github.sspanak.tt9.util;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
import android.view.inputmethod.InputConnection;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
|
||||||
|
|
||||||
|
public class InputConnectionTools {
|
||||||
|
@Nullable private final InputConnection connection;
|
||||||
|
private int connectionErrors;
|
||||||
|
private boolean isErrorReported;
|
||||||
|
|
||||||
|
|
||||||
|
public InputConnectionTools(@Nullable InputConnection connection) {
|
||||||
|
this.connection = connection;
|
||||||
|
connectionErrors = 0;
|
||||||
|
isErrorReported = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public CharSequence getTextAfterCursor(int i, int ii) {
|
||||||
|
return getTextNextToCursor(i, ii, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public CharSequence getTextBeforeCursor(int i, int ii) {
|
||||||
|
return getTextNextToCursor(i, ii, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private CharSequence getTextNextToCursor(int i, int ii, boolean after) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
return getTextNextToCursorModern(i, ii, after);
|
||||||
|
} else {
|
||||||
|
return getTextNextToCursorClassic(i, ii, after);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private CharSequence getTextNextToCursorClassic(int i, int ii, boolean after) {
|
||||||
|
if (connection == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return after ? connection.getTextAfterCursor(i, ii) : connection.getTextBeforeCursor(i, ii);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getTextNextToCursorModern
|
||||||
|
* On some devices with Android >= 11, getTextBeforeCursor() sometimes takes too long to execute,
|
||||||
|
* blocking the UI thread and ultimately causing ANR. This method is a wrapper around
|
||||||
|
* getTextBeforeCursor() that terminates the operations after a certain timeout. Just in case we
|
||||||
|
* handle getTextAfterCursor() the same way.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||||
|
private CharSequence getTextNextToCursorModern(int i, int ii, boolean after) {
|
||||||
|
CompletableFuture<CharSequence> future = CompletableFuture.supplyAsync(
|
||||||
|
() -> getTextNextToCursorClassic(i, ii, after)
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return future.get(SettingsStore.INPUT_CONNECTION_OPERATIONS_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
connectionErrors++;
|
||||||
|
Logger.e(
|
||||||
|
getClass().getSimpleName(),
|
||||||
|
"getStringBeforeCursor() timed out after " + SettingsStore.INPUT_CONNECTION_OPERATIONS_TIMEOUT + "ms. " + connectionErrors + " errors so far."
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
connectionErrors++;
|
||||||
|
Logger.e(getClass().getSimpleName(), "getStringBeforeCursor() failed " + connectionErrors + " times so far. Current error: " + e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldReportTimeout() {
|
||||||
|
if (!isErrorReported && connectionErrors > SettingsStore.INPUT_CONNECTION_ERRORS_MAX) {
|
||||||
|
isErrorReported = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
<string name="completed">Завършено</string>
|
<string name="completed">Завършено</string>
|
||||||
<string name="loading">Зареждане…</string>
|
<string name="loading">Зареждане…</string>
|
||||||
<string name="error_unexpected">Възникна неочаквана грешка.</string>
|
<string name="error_unexpected">Възникна неочаквана грешка.</string>
|
||||||
|
<string name="error_unstable_input_connection">Нестабилна връзка с приложението!</string>
|
||||||
<string name="add_word_add">Добави</string>
|
<string name="add_word_add">Добави</string>
|
||||||
<string name="add_word_no_selection">Преместете показалеца върху дума, за да я добавите към речника.</string>
|
<string name="add_word_no_selection">Преместете показалеца върху дума, за да я добавите към речника.</string>
|
||||||
<string name="add_word_blank">Не може да се въведе празна дума.</string>
|
<string name="add_word_blank">Не може да се въведе празна дума.</string>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
<string name="loading">Laden…</string>
|
<string name="loading">Laden…</string>
|
||||||
<string name="error_unexpected">Unerwarteter Fehler aufgetreten.</string>
|
<string name="error_unexpected">Unerwarteter Fehler aufgetreten.</string>
|
||||||
|
|
||||||
|
<string name="error_unstable_input_connection">Instabile Anwendungsverbindung!</string>
|
||||||
<string name="add_word_add">Hinzufügen</string>
|
<string name="add_word_add">Hinzufügen</string>
|
||||||
<string name="add_word_confirm">Wort \"%1$s\" zu %2$s hinzufügen?</string>
|
<string name="add_word_confirm">Wort \"%1$s\" zu %2$s hinzufügen?</string>
|
||||||
<string name="add_word_no_selection">Bewegen Sie den Cursor innerhalb eines Wortes, um es hinzuzufügen.</string>
|
<string name="add_word_no_selection">Bewegen Sie den Cursor innerhalb eines Wortes, um es hinzuzufügen.</string>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_settings">Configuración de Traditional T9</string>
|
<string name="app_settings">Configuración de Traditional T9</string>
|
||||||
|
<string name="error_unstable_input_connection">¡Conexión inestable con la aplicación!</string>
|
||||||
<string name="add_word_add">Agregar</string>
|
<string name="add_word_add">Agregar</string>
|
||||||
<string name="add_word_confirm">¿Agregar la palabra \"%1$s\" a %2$s?</string>
|
<string name="add_word_confirm">¿Agregar la palabra \"%1$s\" a %2$s?</string>
|
||||||
<string name="add_word_no_selection">Mueve el cursor dentro de una palabra para añadirla.</string>
|
<string name="add_word_no_selection">Mueve el cursor dentro de una palabra para añadirla.</string>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
<string name="completed">Fini</string>
|
<string name="completed">Fini</string>
|
||||||
<string name="loading">Chargement…</string>
|
<string name="loading">Chargement…</string>
|
||||||
<string name="error_unexpected">Une erreur inattendue s\'est produite.</string>
|
<string name="error_unexpected">Une erreur inattendue s\'est produite.</string>
|
||||||
|
<string name="error_unstable_input_connection">Connexion instable à l\'application !</string>
|
||||||
<string name="add_word_add">Ajouter</string>
|
<string name="add_word_add">Ajouter</string>
|
||||||
<string name="add_word_no_selection">Déplacez le curseur dans un mot pour l\'ajouter.</string>
|
<string name="add_word_no_selection">Déplacez le curseur dans un mot pour l\'ajouter.</string>
|
||||||
<string name="add_word_blank">Mot vide non ajouté.</string>
|
<string name="add_word_blank">Mot vide non ajouté.</string>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
<string name="loading">Caricamento…</string>
|
<string name="loading">Caricamento…</string>
|
||||||
<string name="error_unexpected">Si è verificato un errore imprevisto.</string>
|
<string name="error_unexpected">Si è verificato un errore imprevisto.</string>
|
||||||
|
|
||||||
|
<string name="error_unstable_input_connection">Connessione instabile all\'applicazione!</string>
|
||||||
<string name="add_word_add">Aggiungere</string>
|
<string name="add_word_add">Aggiungere</string>
|
||||||
<string name="add_word_confirm">Aggiungi la parola \"%1$s\" a %2$s?</string>
|
<string name="add_word_confirm">Aggiungi la parola \"%1$s\" a %2$s?</string>
|
||||||
<string name="add_word_no_selection">Sposta il cursore dentro un parola per aggiungerla.</string>
|
<string name="add_word_no_selection">Sposta il cursore dentro un parola per aggiungerla.</string>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
<string name="loading">טוען…</string>
|
<string name="loading">טוען…</string>
|
||||||
<string name="error_unexpected">אירעה שגיאה לא צפויה.</string>
|
<string name="error_unexpected">אירעה שגיאה לא צפויה.</string>
|
||||||
|
|
||||||
|
<string name="error_unstable_input_connection">חיבור לא יציב לאפליקציה!</string>
|
||||||
<string name="add_word_add">הוסף</string>
|
<string name="add_word_add">הוסף</string>
|
||||||
<string name="add_word_confirm">האם להוסיף את המילה \"%1$s\" ל-%2$s?</string>
|
<string name="add_word_confirm">האם להוסיף את המילה \"%1$s\" ל-%2$s?</string>
|
||||||
<string name="add_word_no_selection">הזיזו את הסמן בתוך מילה כדי להוסיף אותה.</string>
|
<string name="add_word_no_selection">הזיזו את הסמן בתוך מילה כדי להוסיף אותה.</string>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
<string name="error_unexpected">Įvyko netikėta klaida.</string>
|
<string name="error_unexpected">Įvyko netikėta klaida.</string>
|
||||||
|
|
||||||
|
<string name="error_unstable_input_connection">Nestabilus programėlės ryšys!</string>
|
||||||
<string name="add_word_add">Pridėti</string>
|
<string name="add_word_add">Pridėti</string>
|
||||||
<string name="add_word_confirm">Pridėti žodį \"%1$s\" į \"%2$s\" žodyną?</string>
|
<string name="add_word_confirm">Pridėti žodį \"%1$s\" į \"%2$s\" žodyną?</string>
|
||||||
<string name="add_word_no_selection">Perkelkite žymeklį prie žodžio kurį norite pridėti.</string>
|
<string name="add_word_no_selection">Perkelkite žymeklį prie žodžio kurį norite pridėti.</string>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
<string name="loading">Laden…</string>
|
<string name="loading">Laden…</string>
|
||||||
<string name="error_unexpected">Er is een onverwachte fout opgetreden.</string>
|
<string name="error_unexpected">Er is een onverwachte fout opgetreden.</string>
|
||||||
|
|
||||||
|
<string name="error_unstable_input_connection">Onstabiele applicatieverbinding!</string>
|
||||||
<string name="add_word_add">Toevoegen</string>
|
<string name="add_word_add">Toevoegen</string>
|
||||||
<string name="add_word_confirm">Voeg woord \"%1$s\" toe aan %2$s?</string>
|
<string name="add_word_confirm">Voeg woord \"%1$s\" toe aan %2$s?</string>
|
||||||
<string name="add_word_no_selection">Verplaats de cursor binnen een woord om het toe te voegen.</string>
|
<string name="add_word_no_selection">Verplaats de cursor binnen een woord om het toe te voegen.</string>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
<string name="loading">Carregando…</string>
|
<string name="loading">Carregando…</string>
|
||||||
<string name="error_unexpected">Um erro inesperado aconteceu.</string>
|
<string name="error_unexpected">Um erro inesperado aconteceu.</string>
|
||||||
|
|
||||||
|
<string name="error_unstable_input_connection">Conexão instável com o aplicativo!</string>
|
||||||
<string name="add_word_add">Adicionar</string>
|
<string name="add_word_add">Adicionar</string>
|
||||||
<string name="add_word_confirm">Adicionar a palavra \"%1$s\" a %2$s?</string>
|
<string name="add_word_confirm">Adicionar a palavra \"%1$s\" a %2$s?</string>
|
||||||
<string name="add_word_no_selection">Mova o cursor dentro de uma palavra para adicioná-la.</string>
|
<string name="add_word_no_selection">Mova o cursor dentro de uma palavra para adicioná-la.</string>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
<string name="completed">Выполнено</string>
|
<string name="completed">Выполнено</string>
|
||||||
<string name="loading">Загрузка…</string>
|
<string name="loading">Загрузка…</string>
|
||||||
<string name="error_unexpected">Произошла непредвиденная ошибка.</string>
|
<string name="error_unexpected">Произошла непредвиденная ошибка.</string>
|
||||||
|
<string name="error_unstable_input_connection">Нестабильное соединение с приложением!</string>
|
||||||
<string name="add_word_add">Добавить</string>
|
<string name="add_word_add">Добавить</string>
|
||||||
<string name="add_word_no_selection">Переместите курсор внутрь слова, чтобы добавить его.</string>
|
<string name="add_word_no_selection">Переместите курсор внутрь слова, чтобы добавить его.</string>
|
||||||
<string name="add_word_blank">Невозможно добавить слово.</string>
|
<string name="add_word_blank">Невозможно добавить слово.</string>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
<string name="loading">Yükleniyor…</string>
|
<string name="loading">Yükleniyor…</string>
|
||||||
<string name="error_unexpected">Beklenmeyen bir hata ile karşılaşıldı.</string>
|
<string name="error_unexpected">Beklenmeyen bir hata ile karşılaşıldı.</string>
|
||||||
|
|
||||||
|
<string name="error_unstable_input_connection">Uygulama bağlantısı kararsız!</string>
|
||||||
<string name="add_word_add">Ekle</string>
|
<string name="add_word_add">Ekle</string>
|
||||||
<string name="add_word_confirm">\"%1$s\" kelimesi %2$s sözlüğe eklensin mi?</string>
|
<string name="add_word_confirm">\"%1$s\" kelimesi %2$s sözlüğe eklensin mi?</string>
|
||||||
<string name="add_word_no_selection">Ekleme yapmak için imleci eklemek istediğiniz kelimeye götürün.</string>
|
<string name="add_word_no_selection">Ekleme yapmak için imleci eklemek istediğiniz kelimeye götürün.</string>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
<string name="error_unexpected">Сталася неочікувана помилка.</string>
|
<string name="error_unexpected">Сталася неочікувана помилка.</string>
|
||||||
|
|
||||||
|
<string name="error_unstable_input_connection">Нестабільне з\'єднання з додатком!</string>
|
||||||
<string name="add_word_add">Додати</string>
|
<string name="add_word_add">Додати</string>
|
||||||
<string name="add_word_confirm">Додати слово \"%1$s\" в %2$s?</string>
|
<string name="add_word_confirm">Додати слово \"%1$s\" в %2$s?</string>
|
||||||
<string name="add_word_no_selection">Перемістіть курсор у слово, щоб додати його.</string>
|
<string name="add_word_no_selection">Перемістіть курсор у слово, щоб додати його.</string>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
<string name="search_results_void">No results.</string>
|
<string name="search_results_void">No results.</string>
|
||||||
|
|
||||||
<string name="error_unexpected">Unexpected error occurred.</string>
|
<string name="error_unexpected">Unexpected error occurred.</string>
|
||||||
|
<string name="error_unstable_input_connection">Unstable application connection!</string>
|
||||||
|
|
||||||
<string name="add_word_add">Add</string>
|
<string name="add_word_add">Add</string>
|
||||||
<string name="add_word_confirm">Add word \"%1$s\" to %2$s?</string>
|
<string name="add_word_confirm">Add word \"%1$s\" to %2$s?</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue