I need to return the short version of the weekdays names using getShortWeekdays() in Android. I tested with different device language settings. Although it seems to be working with various language settings, unfortunately when the selected language is Portuguese (Portugal), it returns the full day names. Any advice?
DateFormatSymbols symbols = new DateFormatSymbols(Locale.getDefault());
String[] dayNames = symbols.getShortWeekdays();
Output if language is English (United States):
["", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
Output if language is Portuguese (Brasil):
["", "dom.", "seg.", "ter.", "qua.", "qui.", "sex.", "sáb."]
Output if language is Portuguese (Portugal):
["", "domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado"]
At first, I wondered if using a different package for the method's class would produce different results, but it didn't.
final java.util.Locale pt_BR = new java.util.Locale("pt", "BR");
android.icu.text.DateFormatSymbols symbolsPtBr1 = new android.icu.text.DateFormatSymbols(pt_BR);
String[] dayNamesBtBr1 = symbolsPtBr1.getShortWeekdays();
Log.e("LOCALE_ANDROID", String.format("%s(%s): (android.icu.text): %s", pt_BR.getDisplayVariant(), pt_BR.getDisplayName(), Arrays.toString(dayNamesBtBr1)));
java.text.DateFormatSymbols symbolsPtBr2 = new java.text.DateFormatSymbols(pt_BR);
String[] dayNamesBtBr2 = symbolsPtBr2.getShortWeekdays();
Log.e("LOCALE_ANDROID", String.format("%s(%s): (java.text): %s", pt_BR.getDisplayVariant(), pt_BR.getDisplayName(), Arrays.toString(dayNamesBtBr2)));
final java.util.Locale pt_PT = new java.util.Locale("pt", "PT");
android.icu.text.DateFormatSymbols symbolsPtPt1 = new android.icu.text.DateFormatSymbols(pt_PT);
String[] dayNamesBtPt1 = symbolsPtPt1.getShortWeekdays();
Log.e("LOCALE_ANDROID", String.format("%s(%s): (android.icu.text): %s", pt_PT.getDisplayVariant(), pt_PT.getDisplayName(), Arrays.toString(dayNamesBtPt1)));
java.text.DateFormatSymbols symbolsPtPt2 = new java.text.DateFormatSymbols(pt_PT);
String[] dayNamesBtPt2 = symbolsPtPt2.getShortWeekdays();
Log.e("LOCALE_ANDROID", String.format("%s(%s): (java.text): %s", pt_PT.getDisplayVariant(), pt_PT.getDisplayName(), Arrays.toString(dayNamesBtPt2)));
The log output (tested on Android 7.0):
LOCALE_ANDROID: (Portuguese (Brazil)): (android.icu.text): [, Dom, Seg, Ter, Qua, Qui, Sex, Sáb]
LOCALE_ANDROID: (Portuguese (Brazil)): (java.text): [, Dom, Seg, Ter, Qua, Qui, Sex, Sáb]
LOCALE_ANDROID: (Portuguese (Portugal)): (android.icu.text): [, domingo, segunda, terça, quarta, quinta, sexta, sábado]
LOCALE_ANDROID: (Portuguese (Portugal)): (java.text): [, domingo, segunda, terça, quarta, quinta, sexta, sábado]
Therefore, you can just truncate each weekday to a two- or three-letter symbol length with substring(0, len) and capitalize it with StringUtils.capitalize() method as this is the approach used for the issue in OsmAnd https://github.com/osmandapp/Osmand/issues/5115
Related
I am trying to implement behavior similar to spell check, however not exactly same. By static, I mean only on certain words.
So let's say I have an array of 10 strings.
String[] originalWords = new String[] {
"Apple", "Banana", "Orange", "Pear", "Grape",
"Pineapple", "Lemon", "Mango", "Cherry", "Peach"
};
Now, I have an EditText where user can type in something. After user finishes typing (they press next, etc.), I want to perform a check on what they typed in.
String userInput = String.valueOf(editText.getText()).trim();
Let's say userInput value is appla. That means what user typed in is only one letter off the world Apple (which is in the array).
That's the check I want to implement. Whatever the user types in, if one SIMILAR (1 or 2 letters off) word exists in the array, I want to get that word. How would I go about implementing that?
Result examples:
aoole ==> Apple
orenge ==> Orange
Cheery ==> Cherry
I was able to figure this out. It doesn't go exactly the way I wanted (1 or 2 letters off) but it does the job because it gives the of percentage of similarity.
I used this library.
// array of original words to compare against
String[] originalWords = new String[] {
"Apple", "Banana", "Orange", "Pear", "Grape",
"Pineapple", "Lemon", "Mango", "Cherry", "Peach"
};
// convert String array to ArrayList
ArrayList<String> originals = (ArrayList<String>) Arrays.asList(originalWords);
ExtractedResult extractedResult = FuzzySearch.extractOne("aoole", originals);
if(extractedResult.getScore() >= 75) {
// the word provided (aoole) is at least 75% similar to one of the original words.
// To get the original word:
String result = extractedResult.getString();
}
I want to read data from a raw file and replace the format in the text.
For example... In a raw file like this:
hello {0}, my name id {1}, my age is {2}....
When I use String.format, as shown below, the text loses its indentation.
String data = readTextFile(this, R.raw.input);
data = String.format(data, "world", "josh", "3");
Does anyone know how to do this without losing indentation?
Code that you provided looks more like String.format e.g from C#. String.format in Java does not work this way, it's more like printf.
You can manipulate your input to looks like this.
String input = "hello %s, my name id %s, my age is %s";
String.format(input, "world", "josh", "3");
output:
hello world, my name id josh, my age is 3
indentation should be the same
EDIT
If you want to use brackets you can use MessageFormat.format instead of String.format.
String messageInput = "hello {0}, my name id {1}, my age is {2}";
MessageFormat.format(messageInput,"world", "josh", "3");
You can use Regular Explessions with pattern like that: "{/d++}":
String format (String input, String... args) {
Pattern p = Pattern.compile("{/d++}");
String[] parts = p.split(input);
StringBuilder builder = new StringBuilder("");
int limit = Math.min(args.length, parts.length);
for(int i = 0; i < limit; i++){
builder.append(parts[i]).append(args[i]);
}
return builder.toString();
}
I found the solution for my problem.
there is a needed in one more variable, it's impossible to assignment into same variable
String data = readTextFile(this, R.raw.input);
String output = String.format(data, "world", "josh", "3");
How can I get simplified chinese description (简体)? From the available locale Locale.SIMPLIFIED_CHINESE, no method seems to return this description:
getDisplayLanguage() returns the correct language name, but without the variant.
getDisplayName() returns the correct language name and country, but also without the variant.
getDisplayVariant() returns an empty string.
I've also tried to build a new Locale using the different constructors, also to no avail.
new Locale("zh", "CN");
new Locale("zh", "CN", "Hans");
I've checked the Android source code for LocalePicker and I've concluded that it is loaded from the resources (special_locale_codes and special_locale_names).
Any solutions besides having to hardcode/include this string in my resources?
Let me explain my process on how I tackled this. First, I found this block of code in LocalePicker.java
private static String getDisplayName(Locale l, String[] specialLocaleCodes, String[] specialLocaleNames) {
String code = l.toString();
for (int i = 0; i < specialLocaleCodes.length; i++) {
if (specialLocaleCodes[i].equals(code)) {
return specialLocaleNames[i];
}
}
return l.getDisplayName(l);
}
which takes in a Locale as you already know. Then it tries to find the locale code in the specialLocaleCodes string array. The specialLocaleNames you are seeking are obtained from arrays.xml as you've helpfully stated:
<string-array translatable="false" name="special_locale_codes">
<item>ar_EG</item>
<item>zh_CN</item>
<item>zh_TW</item>
</string-array>
and the corresponding languages
<string-array translatable="false" name="special_locale_names">
<item>العربية</item>
<item>中文 (简体)</item>
<item>中文 (繁體)</item>
</string-array>
Notice the code with the simplified Chinese is zh_CN and the last two characters are capitalized.
However,
Locale locale = new Locale("zh_CN");
System.out.println("Locale: " + locale);
prints
Locale: zh_cn
Notice the lower case. So there is no way specialLocaleCodes[i].equals(code) will return true. So then I poked around Locale.java and, long story short, we can bypass that case-changing jumble by doing this (and you MUST keep the 3rd parameter as an empty string for this to work):
Locale locale = new Locale("zh", "CN", "");
System.out.println("Locale: " + locale);
Prints
Locale: zh_CN
With this you should be able to do this:
Locale locale = new Locale("zh", "CN", "");
System.out.println("Name:" + locale.getDisplayName(locale));
Upon further inspection on Kitkat using this (thank you Andrew!)
int specialLocaleNamesId = Resources.getSystem().getIdentifier("special_locale_names", "array", "android");
String[] specialLocaleNames = Resources.getSystem().getStringArray(specialLocaleNamesId);
it was possible to print out
العربية,中文 (简体),中文 (繁體)
as expected. However, something in Kitkat is still preventing the correct string to display. Frustrating.
However, in Lollipop 5.0+ and Java 1.7 this works using forLanguageTag() in Locale.
Locale locale = Locale.forLanguageTag("zh-Hans");
System.out.println("getDisplayName:" + locale.getDisplayName(locale));
System.out.println("getDisplayLanguage:" + locale.getDisplayLanguage(locale));
which prints
getDisplayName:中文 (简体中文)
getDisplayLanguage:中文
You could probably access the android internal resource: com.android.internal.R.array.special_locale_names the same way it's done in LocalePicker:
final Resources resources = context.getResources();
final String[] specialLocaleNames = resources.getStringArray(com.android.internal.R.array.special_locale_names);
But it's probably safer to use your own resource here (avoiding
the use of internals)
I've been looking for answer regarding my question but I cannot find one or maybe I'm not just using the right terms when searching.
My question is, how can I distribute string entered in an editText to an array?
This is about my porter2 stemming project. I need to distribute the strings entered in the EditText field to an array so i can scan whether x in array[x] is vowel or not.
Ex.
String in EditText field = "dog".
Array should be:
array[0] = d
array[1] = o
array[2] = g
I'm sorry that i cannot give a code because I really don't have any idea how to code this one. Thank you so much everyone. :)
You can use use .split() with an empty ("") string input:
String text = yourEditText.getText().toString();
String[] letters = text.split(""); // Split by empty string to be in an array
// letters == { "", "d", "o", "g" }
// ^ Note that this has an empty string element at the front; that's just a byproduct of how split() works.
If you want a char array instead, it's much easier to use .toCharArray():
String text = yourEditText.getText().toString();
char[] letters = text.toCharArray();
// letters == { 'd', 'o', 'g' };
Personally I'd use the second one; letter == 'a' is a much faster operation than letter.equals("a").
Is it possible, at runtime, to know which resources languages are embedded in my app?
i.e the presence of this folders:
values-en
values-de
values-fr
...
It's complicated because even if you have a folder named values-de it doesn't mean you have any resources there. If you have string.xml in values-de it doesn't mean you have string value there.
values:
<resources>
<string name="app_name">LocTest</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
</resources>
values-de:
<resources>
<string name="hello_world">Hallo Welt!</string>
</resources>
You can test if a resource for a specific locale is different than the default:
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
Resources r = getResources();
Configuration c = r.getConfiguration();
String[] loc = r.getAssets().getLocales();
for (int i = 0; i < loc.length; i++) {
Log.d("LOCALE", i + ": " + loc[i]);
c.locale = new Locale(loc[i]);
Resources res = new Resources(getAssets(), metrics, c);
String s1 = res.getString(R.string.hello_world);
c.locale = new Locale("");
Resources res2 = new Resources(getAssets(), metrics, c);
String s2 = res2.getString(R.string.hello_world);
if(!s1.equals(s2)){
Log.d("DIFFERENT LOCALE", i + ": "+ s1+" "+s2 +" "+ loc[i]);
}
}
It has one fault - you can check one value whether it has translation.
The dirty code above will print something like:
LOCALE(5667): 51: en_NZ LOCALE(5667): 52: uk_UA LOCALE(5667): 53:
nl_BE LOCALE(5667): 54: de_DE DIFFERENT LOCALE(5667): 54: Hallo Welt!
Hello world! de_DE LOCALE(5667): 55: ka_GE LOCALE(5667): 56: sv_SE
LOCALE(5667): 57: bg_BG LOCALE(5667): 58: de_CH DIFFERENT
LOCALE(5667): 58: Hallo Welt! Hello world! de_CH LOCALE(5667): 59:
fr_CH LOCALE(5667): 60: fi_FI
AssetManager.getLocales() is actually the way to do this. However, from the public API every AssetManager you create also has the framework resources included in its search path... so when you call AssetManager.getLocales() you will also see any locales that are part of the framework resources. There is no way to get around this with the SDK, sorry.
For anyone using Gradle, I did it like so below it traverses all strings.xml, grabs the directory names and figures out the locales from there. It adds a String[] to BuildConfig which you can access as BuildConfig.TRANSLATION_ARRAY
task buildTranslationArray << {
def foundLocales = new StringBuilder()
foundLocales.append("new String[]{")
fileTree("src/main/res").visit { FileVisitDetails details ->
if(details.file.path.endsWith("strings.xml")){
def languageCode = details.file.parent.tokenize('/').last().replaceAll('values-','').replaceAll('-r','-')
languageCode = (languageCode == "values") ? "en" : languageCode;
foundLocales.append("\"").append(languageCode).append("\"").append(",")
}
}
foundLocales.append("}")
//Don't forget to remove the trailing comma
def foundLocalesString = foundLocales.toString().replaceAll(',}','}')
android.defaultConfig.buildConfigField "String[]", "TRANSLATION_ARRAY", foundLocalesString
}
preBuild.dependsOn buildTranslationArray
So after the above task occurs (on prebuild) the BuildConfig.TRANSLATION_ARRAY has your list of locales.
I'm not a Gradle/Groovy expert so this could definitely be a bit neater.
Reasoning - I ran into too many issues implementing pawelzieba's solution, I had no reliable strings to 'compare' as the translations were crowdsourced. The easiest way then was to actually look at the values-blah folders available.
Inspired by Mendhak's solution I created something a bit cleaner:
defaultConfig {
....
def locales = ["en", "it", "pl", "fr", "es", "de", "ru"]
buildConfigField "String[]", "TRANSLATION_ARRAY", "new String[]{\""+locales.join("\",\"")+"\"}"
resConfigs locales
}
Then in Java use:
BuildConfig.TRANSLATION_ARRAY
Advatages of this method:
Smaller apk - resConfigs will cut out resources from libraries which you don't need (some have hundreds)
Fast - No need to parse resource configurations
Inspired by the answers above I created a simple method to get all app's languages based on provided translations:
public static Set<String> getAppLanguages( Context ctx, int id ) {
DisplayMetrics dm = ctx.getResources().getDisplayMetrics();
Configuration conf = ctx.getResources().getConfiguration();
Locale originalLocale = conf.locale;
conf.locale = Locale.ENGLISH;
final String reference = new Resources( ctx.getAssets(), dm, conf ).getString( id );
Set<String> result = new HashSet<>();
result.add( Locale.ENGLISH.getLanguage() );
for( String loc : ctx.getAssets().getLocales() ){
if( loc.isEmpty() ) continue;
Locale l = Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT_WATCH ? new Locale( loc.substring( 0, 2 ) ) : Locale.forLanguageTag( loc );
conf.locale = l;
if( !reference.equals( new Resources( ctx.getAssets(), dm, conf ).getString( id ) ) ) result.add( l.getLanguage() );
}
conf.locale = originalLocale;
return result;
}
where as id arg should be used a R.string.some_message which is provided in all translations and contains clearly distinguishable text, like "Do you really want to delete the object?"
Maybe it would help someone...
LocaleList or LocaleListCompat are one of the ways you could get the languages supported by your application.
LocaleList was introduced in API 24.
The thing to consider when using LocaleListCompat is that for API < 24 only the first language tag will be used.
these res-lang actually depends on Locales, so you need to get locale from device and you can get which language is getting shown from locale..
Locale myPhoneLocale = Locale.getDefault();
You can then call getDisplayLanguage() to know which language is getting shown.
Reference : Locale
Are you talking about this?
String language = Locale.getDefault().getDisplayLanguage();
Try calling AssetManager.getLocales():
Get the locales that this asset manager contains data for.
Or you can try if you can get a list using list().
Return a String array of all the assets at the given path.
Inspired with the code by#Injecteer
I have done the following:
for the list of languages that the app supports , it is necessary to pass the default language , since it is not possible to detect
public static Map<String,String> getAppLanguages(Context context, String appDefaultLang) {
Map<String, String> listAppLocales = new LinkedHashMap<>();
listAppLocales.put("default","Auto");
DisplayMetrics metrics = new DisplayMetrics();
Resources res = context.getResources();
Configuration conf = res.getConfiguration();
String[] listLocates = res.getAssets().getLocales();
for (String locate : listLocates) {
conf.locale = new Locale(locate);
Resources res1 = new Resources(context.getAssets(), metrics, conf);
String s1 = res1.getString(R.string.title_itinerary);
String value = ucfirst(conf.locale.getDisplayName());
conf.locale = new Locale("");
Resources res2 = new Resources(context.getAssets(), metrics, conf);
String s2 = res2.getString(R.string.title_itinerary);
if (!s1.equals(s2)) {
listAppLocales.put(locate, value);
} else if (locate.equals(appDefaultLang)) {
listAppLocales.put(locate, value);
}
}
return listAppLocales;
}
The result is a list map<key,value>of languages supported by the application, the first thing is for if you want to use to populate a listPreference
Do you mean:
String[] locales = getAssets().getLocales();
This would let you get the language that your device have.