Add Thai language support (#629)
* added Thai language * the SoftKeyNumber now displays abbreviated letter list when there are too many letters on a single key * updated the language validation rules to detect single letters in Asian languages * added a 'no space between words' language YAML option --------- Co-authored-by: sspanak <doftor.livain@gmail.com>
This commit is contained in:
parent
e5b9beb84e
commit
d5fc1fe4b1
16 changed files with 19709 additions and 97 deletions
17
app/languages/definitions/Thai.yml
Normal file
17
app/languages/definitions/Thai.yml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
locale: th-TH
|
||||
dictionaryFile: th-utf8.csv
|
||||
abcString: กขค
|
||||
hasSpaceBetweenWords: no
|
||||
hasUpperCase: no
|
||||
name: ภาษาไทย
|
||||
layout:
|
||||
- [SPECIAL] # 0
|
||||
- [PUNCTUATION] # 1
|
||||
- [ก, ข, ฃ, ค, ฅ, ฆ, ง, จ, ฉ] # 2
|
||||
- [ช, ซ, ฌ, ญ, ฎ, ฏ, ฐ, ฑ, ฒ, ณ] # 3
|
||||
- [ด, ต, ถ, ท, ธ, น] # 4
|
||||
- [บ, ป, ผ, ฝ, พ, ฟ] # 5
|
||||
- [ภ, ม, ย, ร, ล, ว] # 6
|
||||
- [ศ, ษ, ส, ห, ฬ, อ, ฮ] # 7
|
||||
- [ิ, ี, ึ, ื, ุ, ู, ั, ่, ้, ๊, ๋, ็, ์] # 8
|
||||
- [ะ, า, โ, ไ, ใ, เ, แ, ำ, ๅ, ๆ, ฯ, ฤ, ฦ] # 9
|
||||
19476
app/languages/dictionaries/th-utf8.csv
Normal file
19476
app/languages/dictionaries/th-utf8.csv
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -46,21 +46,19 @@ public class ModePredictive extends InputMode {
|
|||
|
||||
|
||||
ModePredictive(SettingsStore settings, InputType inputType, TextField textField, Language lang) {
|
||||
changeLanguage(lang);
|
||||
defaultTextCase();
|
||||
|
||||
autoSpace = new AutoSpace(settings);
|
||||
autoSpace = new AutoSpace(settings, lang).setLanguage(lang);
|
||||
autoTextCase = new AutoTextCase(settings);
|
||||
predictions = new Predictions(settings, textField);
|
||||
|
||||
this.settings = settings;
|
||||
|
||||
digitSequence = "";
|
||||
predictions = new Predictions(settings, textField);
|
||||
this.settings = settings;
|
||||
|
||||
if (inputType.isEmail()) {
|
||||
KEY_CHARACTERS.add(new ArrayList<>(Characters.Email.get(0)));
|
||||
KEY_CHARACTERS.add(new ArrayList<>(Characters.Email.get(1)));
|
||||
}
|
||||
|
||||
changeLanguage(lang);
|
||||
defaultTextCase();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -113,6 +111,8 @@ public class ModePredictive extends InputMode {
|
|||
public void changeLanguage(Language language) {
|
||||
super.changeLanguage(language);
|
||||
|
||||
autoSpace.setLanguage(language);
|
||||
|
||||
allowedTextCases.clear();
|
||||
allowedTextCases.add(CASE_LOWER);
|
||||
if (language.hasUpperCase()) {
|
||||
|
|
@ -124,6 +124,10 @@ public class ModePredictive extends InputMode {
|
|||
|
||||
@Override
|
||||
public boolean recompose(String word) {
|
||||
if (!language.hasSpaceBetweenWords()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (word == null || word.length() < 2 || word.contains(" ")) {
|
||||
Logger.d(LOG_TAG, "Not recomposing invalid word: '" + word + "'");
|
||||
textCase = CASE_CAPITALIZE;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package io.github.sspanak.tt9.ime.modes.helpers;
|
|||
|
||||
import io.github.sspanak.tt9.hacks.InputType;
|
||||
import io.github.sspanak.tt9.ime.helpers.TextField;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
|
||||
import io.github.sspanak.tt9.util.Characters;
|
||||
import io.github.sspanak.tt9.util.Text;
|
||||
|
|
@ -12,9 +13,11 @@ public class AutoSpace {
|
|||
private InputType inputType;
|
||||
private TextField textField;
|
||||
private String lastWord;
|
||||
private boolean isLanguageWithSpaceBetweenWords;
|
||||
|
||||
public AutoSpace(SettingsStore settingsStore) {
|
||||
public AutoSpace(SettingsStore settingsStore, Language language) {
|
||||
settings = settingsStore;
|
||||
isLanguageWithSpaceBetweenWords = true;
|
||||
}
|
||||
|
||||
public AutoSpace setInputType(InputType inputType) {
|
||||
|
|
@ -32,6 +35,11 @@ public class AutoSpace {
|
|||
return this;
|
||||
}
|
||||
|
||||
public AutoSpace setLanguage(Language language) {
|
||||
isLanguageWithSpaceBetweenWords = language != null && language.hasSpaceBetweenWords();
|
||||
return this;
|
||||
}
|
||||
|
||||
public AutoSpace setLastSequence() {
|
||||
return this;
|
||||
}
|
||||
|
|
@ -40,11 +48,13 @@ public class AutoSpace {
|
|||
* shouldAddAutoSpace
|
||||
* When the "auto-space" settings is enabled, this determines whether to automatically add a space
|
||||
* at the end of a sentence or after accepting a suggestion. This allows faster typing, without
|
||||
* pressing space.
|
||||
*
|
||||
* See the helper functions for the list of rules.
|
||||
* pressing space. See the helper functions for the list of rules.
|
||||
*/
|
||||
public boolean shouldAddAutoSpace(boolean isWordAcceptedManually, int nextKey) {
|
||||
if (!isLanguageWithSpaceBetweenWords) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String previousChars = textField.getStringBeforeCursor(2);
|
||||
Text nextChars = textField.getTextAfterCursor(2);
|
||||
|
||||
|
|
@ -114,7 +124,8 @@ public class AutoSpace {
|
|||
*/
|
||||
public boolean shouldDeletePrecedingSpace() {
|
||||
return
|
||||
settings.getAutoSpace()
|
||||
isLanguageWithSpaceBetweenWords
|
||||
&& settings.getAutoSpace()
|
||||
&& (
|
||||
lastWord.equals(".")
|
||||
|| lastWord.equals(",")
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ abstract public class Language {
|
|||
protected String dictionaryFile;
|
||||
protected Locale locale = Locale.ROOT;
|
||||
protected String name;
|
||||
protected boolean hasSpaceBetweenWords = true;
|
||||
protected boolean hasUpperCase = true;
|
||||
|
||||
|
||||
|
|
@ -51,6 +52,10 @@ abstract public class Language {
|
|||
return name;
|
||||
}
|
||||
|
||||
final public boolean hasSpaceBetweenWords() {
|
||||
return hasSpaceBetweenWords;
|
||||
}
|
||||
|
||||
final public boolean hasUpperCase() {
|
||||
return hasUpperCase;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ public class LanguageDefinition {
|
|||
|
||||
public String abcString = "";
|
||||
public String dictionaryFile = "";
|
||||
public boolean hasSpaceBetweenWords = true;
|
||||
public boolean hasUpperCase = true;
|
||||
public ArrayList<ArrayList<String>> layout = new ArrayList<>();
|
||||
public String locale = "";
|
||||
|
|
@ -83,27 +84,14 @@ public class LanguageDefinition {
|
|||
@NonNull
|
||||
private static LanguageDefinition parse(ArrayList<String> yaml) {
|
||||
LanguageDefinition definition = new LanguageDefinition();
|
||||
String value;
|
||||
|
||||
value = getPropertyFromYaml(yaml, "abcString");
|
||||
definition.abcString = value != null ? value : definition.abcString;
|
||||
|
||||
value = getPropertyFromYaml(yaml, "dictionaryFile");
|
||||
definition.dictionaryFile = value != null ? value : definition.dictionaryFile;
|
||||
|
||||
value = getPropertyFromYaml(yaml, "locale");
|
||||
definition.locale = value != null ? value : definition.locale;
|
||||
|
||||
value = getPropertyFromYaml(yaml, "name");
|
||||
definition.name = value != null ? value : definition.name;
|
||||
|
||||
definition.abcString = getPropertyFromYaml(yaml, "abcString", definition.abcString);
|
||||
definition.dictionaryFile = getPropertyFromYaml(yaml, "dictionaryFile", definition.dictionaryFile);
|
||||
definition.hasSpaceBetweenWords = getPropertyFromYaml(yaml, "hasSpaceBetweenWords", definition.hasSpaceBetweenWords);
|
||||
definition.hasUpperCase = getPropertyFromYaml(yaml, "hasUpperCase", definition.hasUpperCase);
|
||||
definition.layout = getLayoutFromYaml(yaml);
|
||||
|
||||
value = getPropertyFromYaml(yaml, "hasUpperCase");
|
||||
if (value != null) {
|
||||
value = value.toLowerCase();
|
||||
definition.hasUpperCase = value.equals("true") || value.equals("on") || value.equals("yes") || value.equals("y");
|
||||
}
|
||||
definition.locale = getPropertyFromYaml(yaml, "locale", definition.locale);
|
||||
definition.name = getPropertyFromYaml(yaml, "name", definition.name);
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
|
@ -112,10 +100,10 @@ public class LanguageDefinition {
|
|||
/**
|
||||
* getPropertyFromYaml
|
||||
* Finds "property" in the "yaml" and returns its value.
|
||||
* Optional properties are allowed. NULL will be returned when they are missing.
|
||||
* Optional properties are allowed. If the property is not found, "defaultValue" will be returned.
|
||||
*/
|
||||
@Nullable
|
||||
private static String getPropertyFromYaml(ArrayList<String> yaml, String property) {
|
||||
private static String getPropertyFromYaml(ArrayList<String> yaml, String property, String defaultValue) {
|
||||
for (String line : yaml) {
|
||||
line = line.replaceAll("#.+$", "").trim();
|
||||
String[] parts = line.split(":");
|
||||
|
|
@ -128,7 +116,22 @@ public class LanguageDefinition {
|
|||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The boolean variant of getPropertyFromYaml. It returns true if the property is found and is:
|
||||
* "true", "on", "yes" or "y".
|
||||
*/
|
||||
private static boolean getPropertyFromYaml(ArrayList<String> yaml, String property, boolean defaultValue) {
|
||||
String value = getPropertyFromYaml(yaml, property, null);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
value = value.toLowerCase();
|
||||
return value.equals("true") || value.equals("on") || value.equals("yes") || value.equals("y");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package io.github.sspanak.tt9.languages;
|
|||
|
||||
public class LanguageKind {
|
||||
public static boolean isArabic(Language language) { return language != null && language.getKeyCharacters(3).contains("ا"); }
|
||||
public static boolean isBulgarian(Language language) { return language != null && language.getKeyCharacters(4).contains("ѝ"); }
|
||||
public static boolean isCyrillic(Language language) { return language != null && language.getKeyCharacters(2).contains("а"); }
|
||||
public static boolean isHebrew(Language language) { return language != null && language.getKeyCharacters(3).contains("א"); }
|
||||
public static boolean isGreek(Language language) { return language != null && language.getKeyCharacters(2).contains("α"); }
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public class NaturalLanguage extends Language implements Comparable<NaturalLangu
|
|||
NaturalLanguage lang = new NaturalLanguage();
|
||||
lang.abcString = definition.abcString.isEmpty() ? null : definition.abcString;
|
||||
lang.dictionaryFile = definition.getDictionaryFile();
|
||||
lang.hasSpaceBetweenWords = definition.hasSpaceBetweenWords;
|
||||
lang.hasUpperCase = definition.hasUpperCase;
|
||||
lang.name = definition.name.isEmpty() ? lang.name : definition.name;
|
||||
lang.setLocale(definition);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ public class SettingsStore extends SettingsUI {
|
|||
public final static byte SLOW_QUERY_TIME = 50; // ms
|
||||
public final static int SOFT_KEY_DOUBLE_CLICK_DELAY = 500; // ms
|
||||
public final static int SOFT_KEY_REPEAT_DELAY = 40; // ms
|
||||
public final static int SOFT_KEY_TITLE_MAX_CHARS = 5;
|
||||
public final static int SOFT_KEY_TITLE_SIZE = 18; // sp
|
||||
public final static float SOFT_KEY_COMPLEX_LABEL_TITLE_RELATIVE_SIZE = 0.55f;
|
||||
public final static float SOFT_KEY_COMPLEX_LABEL_ARABIC_TITLE_RELATIVE_SIZE = 0.72f;
|
||||
|
|
|
|||
|
|
@ -2,11 +2,16 @@ package io.github.sspanak.tt9.ui.main.keys;
|
|||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseArray;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
import io.github.sspanak.tt9.ime.helpers.Key;
|
||||
import io.github.sspanak.tt9.ime.modes.InputMode;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
|
|
@ -14,8 +19,34 @@ import io.github.sspanak.tt9.languages.LanguageKind;
|
|||
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
|
||||
import io.github.sspanak.tt9.ui.Vibration;
|
||||
import io.github.sspanak.tt9.util.Logger;
|
||||
import io.github.sspanak.tt9.util.TextTools;
|
||||
|
||||
public class SoftKeyNumber extends SoftKey {
|
||||
private final static SparseArray<Integer> NUMBERS = new SparseArray<Integer>() {{
|
||||
put(R.id.soft_key_0, 0);
|
||||
put(R.id.soft_key_1, 1);
|
||||
put(R.id.soft_key_2, 2);
|
||||
put(R.id.soft_key_3, 3);
|
||||
put(R.id.soft_key_4, 4);
|
||||
put(R.id.soft_key_5, 5);
|
||||
put(R.id.soft_key_6, 6);
|
||||
put(R.id.soft_key_7, 7);
|
||||
put(R.id.soft_key_8, 8);
|
||||
put(R.id.soft_key_9, 9);
|
||||
}};
|
||||
|
||||
private final static SparseArray<Integer> UPSIDE_DOWN_NUMBERS = new SparseArray<Integer>() {{
|
||||
put(1, 7);
|
||||
put(2, 8);
|
||||
put(3, 9);
|
||||
put(7, 1);
|
||||
put(8, 2);
|
||||
put(9, 3);
|
||||
}};
|
||||
|
||||
private static final String PUNCTUATION_LABEL = ",:-)";
|
||||
|
||||
|
||||
public SoftKeyNumber(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
|
@ -28,6 +59,7 @@ public class SoftKeyNumber extends SoftKey {
|
|||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void handleHold() {
|
||||
preventRepeat();
|
||||
|
|
@ -42,6 +74,7 @@ public class SoftKeyNumber extends SoftKey {
|
|||
tt9.onKeyUp(keyCode, new KeyEvent(KeyEvent.ACTION_UP, keyCode));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean handleRelease() {
|
||||
int keyCode = Key.numberToCode(getUpsideDownNumber(getId()));
|
||||
|
|
@ -55,6 +88,23 @@ public class SoftKeyNumber extends SoftKey {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected int getNumber(int keyId) {
|
||||
return NUMBERS.get(keyId, -1);
|
||||
}
|
||||
|
||||
|
||||
protected int getUpsideDownNumber(int keyId) {
|
||||
int number = getNumber(keyId);
|
||||
|
||||
if (tt9 == null || !tt9.getSettings().getUpsideDownKeys()) {
|
||||
return number;
|
||||
}
|
||||
|
||||
return UPSIDE_DOWN_NUMBERS.get(number, number);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
int number = getNumber(getId());
|
||||
|
|
@ -68,6 +118,7 @@ public class SoftKeyNumber extends SoftKey {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String getSubTitle() {
|
||||
if (tt9 == null) {
|
||||
|
|
@ -76,88 +127,105 @@ public class SoftKeyNumber extends SoftKey {
|
|||
|
||||
int number = getNumber(getId());
|
||||
|
||||
// 0
|
||||
if (number == 0) {
|
||||
if (tt9.isNumericModeSigned()) {
|
||||
return "+/-";
|
||||
} else if (tt9.isNumericModeStrict()) {
|
||||
return null;
|
||||
} else if (tt9.isInputModeNumeric()) {
|
||||
return "+";
|
||||
} else {
|
||||
complexLabelSubTitleSize = 1;
|
||||
return "␣";
|
||||
}
|
||||
switch (number) {
|
||||
case 0:
|
||||
return getSpecialCharList(tt9);
|
||||
case 1:
|
||||
return tt9.isNumericModeStrict() ? null : PUNCTUATION_LABEL;
|
||||
default:
|
||||
return getKeyCharList(tt9, number);
|
||||
}
|
||||
}
|
||||
|
||||
// 1
|
||||
if (number == 1) {
|
||||
return tt9.isNumericModeStrict() ? null : ",:-)";
|
||||
}
|
||||
|
||||
// no other special labels in 123 mode
|
||||
if (tt9.isInputModeNumeric()) {
|
||||
private String getSpecialCharList(@NonNull TraditionalT9 tt9) {
|
||||
if (tt9.isNumericModeSigned()) {
|
||||
return "+/-";
|
||||
} else if (tt9.isNumericModeStrict()) {
|
||||
return null;
|
||||
} else if (tt9.isInputModeNumeric()) {
|
||||
return "+";
|
||||
} else {
|
||||
complexLabelSubTitleSize = 1;
|
||||
return "␣";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String getKeyCharList(@NonNull TraditionalT9 tt9, int number) {
|
||||
if (tt9.isInputModeNumeric()) {
|
||||
return null; // no special labels in 123 mode
|
||||
}
|
||||
|
||||
// 2-9
|
||||
Language language = tt9.getLanguage();
|
||||
if (language == null) {
|
||||
Logger.d("SoftKeyNumber.getLabel", "Cannot generate a label when the language is NULL.");
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean isLatinBased = LanguageKind.isLatinBased(language);
|
||||
boolean isGreekBased = LanguageKind.isGreek(language);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
ArrayList<String> chars = language.getKeyCharacters(number);
|
||||
for (int i = 0; sb.length() < 5 && i < chars.size(); i++) {
|
||||
String currentLetter = chars.get(i);
|
||||
if (
|
||||
(isLatinBased && currentLetter.charAt(0) > 'z')
|
||||
|| (isGreekBased && (currentLetter.charAt(0) < 'α' || currentLetter.charAt(0) > 'ω'))
|
||||
) {
|
||||
// As suggested by the community, there is no need to display the accented letters.
|
||||
// People are used to seeing just A-Z.
|
||||
boolean isBulgarian = LanguageKind.isBulgarian(language);
|
||||
boolean isGreek = LanguageKind.isGreek(language);
|
||||
boolean isLatinBased = LanguageKind.isLatinBased(language);
|
||||
boolean isUkrainian = LanguageKind.isUkrainian(language);
|
||||
boolean isUppercase = tt9.getTextCase() == InputMode.CASE_UPPER;
|
||||
|
||||
if (
|
||||
isBulgarian
|
||||
|| isGreek
|
||||
|| isLatinBased
|
||||
|| (isUkrainian && number == 2)
|
||||
|| chars.size() < SettingsStore.SOFT_KEY_TITLE_MAX_CHARS) {
|
||||
return getDefaultCharList(chars, language.getLocale(), isGreek, isLatinBased, isUppercase);
|
||||
} else {
|
||||
return abbreviateCharList(chars, language.getLocale(), isUppercase);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Joins the key characters into a single string, skipping accented characters
|
||||
* when neccessary
|
||||
*/
|
||||
private String getDefaultCharList(ArrayList<String> chars, Locale locale, boolean isGreek, boolean isLatinBased, boolean isUppercase) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String currentLetter : chars) {
|
||||
if (shouldSkipAccents(currentLetter.charAt(0), isGreek, isLatinBased)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sb.append(
|
||||
tt9.getTextCase() == InputMode.CASE_UPPER ? currentLetter.toUpperCase(language.getLocale()) : currentLetter
|
||||
isUppercase ? currentLetter.toUpperCase(locale) : currentLetter
|
||||
);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected int getNumber(int keyId) {
|
||||
if (keyId == R.id.soft_key_0) return 0;
|
||||
if (keyId == R.id.soft_key_1) return 1;
|
||||
if (keyId == R.id.soft_key_2) return 2;
|
||||
if (keyId == R.id.soft_key_3) return 3;
|
||||
if (keyId == R.id.soft_key_4) return 4;
|
||||
if (keyId == R.id.soft_key_5) return 5;
|
||||
if (keyId == R.id.soft_key_6) return 6;
|
||||
if (keyId == R.id.soft_key_7) return 7;
|
||||
if (keyId == R.id.soft_key_8) return 8;
|
||||
if (keyId == R.id.soft_key_9) return 9;
|
||||
|
||||
return -1;
|
||||
/**
|
||||
* In some languages there are many characters for a single key. Naturally, they can not all fit
|
||||
* on one key. As suggested by the community, we could display them as "A-Z".
|
||||
* @see <a href="https://github.com/sspanak/tt9/issues/628">Issue #628</a>
|
||||
*/
|
||||
private String abbreviateCharList(ArrayList<String> chars, Locale locale, boolean isUppercase) {
|
||||
boolean containsCombiningChars = TextTools.isCombining(chars.get(0)) || TextTools.isCombining(chars.get(chars.size() - 1));
|
||||
return
|
||||
(isUppercase ? chars.get(0).toUpperCase(locale) : chars.get(0))
|
||||
+ (containsCombiningChars ? "– " : "–")
|
||||
+ (isUppercase ? chars.get(chars.size() - 1).toUpperCase(locale) : chars.get(chars.size() - 1));
|
||||
}
|
||||
|
||||
protected int getUpsideDownNumber(int keyId) {
|
||||
int number = getNumber(keyId);
|
||||
|
||||
if (tt9 != null && tt9.getSettings().getUpsideDownKeys()) {
|
||||
if (number == 1) return 7;
|
||||
if (number == 2) return 8;
|
||||
if (number == 3) return 9;
|
||||
if (number == 7) return 1;
|
||||
if (number == 8) return 2;
|
||||
if (number == 9) return 3;
|
||||
}
|
||||
|
||||
return number;
|
||||
/**
|
||||
* As suggested by the community, there is no need to display the accented letters.
|
||||
* People are used to seeing just "ABC", "DEF", etc.
|
||||
*/
|
||||
private boolean shouldSkipAccents(char currentLetter, boolean isGreek, boolean isLatinBased) {
|
||||
return
|
||||
currentLetter == 'ѝ'
|
||||
|| currentLetter == 'ґ'
|
||||
|| (isLatinBased && currentLetter > 'z')
|
||||
|| (isGreek && (currentLetter < 'α' || currentLetter > 'ω'));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,22 @@ import java.util.regex.Pattern;
|
|||
|
||||
public class TextTools {
|
||||
private static final Pattern containsOtherThan1 = Pattern.compile("[02-9]");
|
||||
private static final Pattern combiningString = Pattern.compile("^\\p{M}+$");
|
||||
private static final Pattern nextIsPunctuation = Pattern.compile("^\\p{Punct}");
|
||||
private static final Pattern nextToWord = Pattern.compile("\\b$");
|
||||
private static final Pattern previousIsLetter = Pattern.compile("\\p{L}$");
|
||||
private static final Pattern startOfSentence = Pattern.compile("(?<!\\.)(^|[.?!؟¿¡])\\s+$");
|
||||
|
||||
|
||||
|
||||
public static boolean containsOtherThan1(String str) {
|
||||
return str != null && containsOtherThan1.matcher(str).find();
|
||||
}
|
||||
|
||||
public static boolean isCombining(String str) {
|
||||
return str != null && combiningString.matcher(str).find();
|
||||
}
|
||||
|
||||
|
||||
public static boolean isGraphic(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ static def validateDictionaryWord(String word, int lineNumber, String validChara
|
|||
errors += "${errorMsgPrefix}. Found a garbage word: '${word}' on line ${lineNumber}.\n"
|
||||
}
|
||||
|
||||
if (word.matches("^.\$")) {
|
||||
if (word.matches("^(.|\\p{L}\\p{M}?)\$")) {
|
||||
errorCount++
|
||||
errors += "${errorMsgPrefix}. Found a single letter: '${word}' on line ${lineNumber}. Only uppercase single letters are allowed. The rest of the alphabet will be added automatically.\n"
|
||||
}
|
||||
|
|
@ -65,6 +65,7 @@ static def parseLanguageFile(File languageFile, String dictionariesDir) {
|
|||
line.matches("^[a-zA-Z].*")
|
||||
&& !line.startsWith("abcString")
|
||||
&& !line.startsWith("dictionaryFile")
|
||||
&& !line.startsWith("hasSpaceBetweenWords")
|
||||
&& !line.startsWith("hasUpperCase")
|
||||
&& !line.startsWith("layout")
|
||||
&& !line.startsWith("locale")
|
||||
|
|
@ -77,10 +78,14 @@ static def parseLanguageFile(File languageFile, String dictionariesDir) {
|
|||
errorMsg += "Language '${languageFile.name}' is invalid. Found unknown property: '${property}'.\n"
|
||||
}
|
||||
|
||||
if (line.startsWith("hasUpperCase") && !line.endsWith("yes") && !line.endsWith("no")) {
|
||||
if (
|
||||
(line.startsWith("hasUpperCase") || line.startsWith("hasSpaceBetweenWords"))
|
||||
&& !line.endsWith("yes") && !line.endsWith("no")
|
||||
) {
|
||||
def property = line.replaceAll(":.*\$", "")
|
||||
def invalidVal = line.replace("hasUpperCase:", "").trim()
|
||||
errorCount++
|
||||
errorMsg += "Language '${languageFile.name}' is invalid. Unrecognized 'hasUpperCase' value: '${invalidVal}'. Only 'yes' and 'no' are allowed.\n"
|
||||
errorMsg += "Language '${languageFile.name}' is invalid. Unrecognized '${property}' value: '${invalidVal}'. Only 'yes' and 'no' are allowed.\n"
|
||||
}
|
||||
|
||||
if (line.startsWith("layout")) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue