How can I create a Locale with a specific script code? - java

I'm trying to convert this String az_AZ_#Latn, found here, to a Locale but I'm unable to parse the #Latn part.
If I do new Locale("az_AZ_#Latn") I lose the #Latn part (the Script code).
I've tried as well using the LocaleUtils from commons-lang but I get an error saying that it's an invalid format.

As written in the docs:
It is not possible to set a script code on a Locale object in a
release earlier than JDK 7.
But you can use the Locale builder to make it like this:
Locale locale = new Locale.Builder().setLanguage("az").setRegion("AZ").setScript("Latn").build();
You can get the Script it by calling locale.getScript()
Edit:
Here's a method I made for converting a string into a locale (doesn't work for extensions):
public static Locale stringToLocale(String locale){
if(locale == null || locale.isEmpty()) return null;
String[] parts = locale.split("_");
if(parts.length == 1) return new Locale(parts[0]);
if(parts.length == 2) return new Locale(parts[0],parts[1]);
if(parts.length == 3)
if(parts[2].charAt(0) != '#') return new Locale(parts[0],parts[1],parts[2]);
else return new Locale.Builder().setLanguage(parts[0]).setRegion(parts[1]).setScript(parts[2].substring(1)).build();
if(parts.length == 4) return new Locale.Builder().setLanguage(parts[0]).setRegion(parts[1]).setVariant(parts[2]).setScript(parts[3].charAt(0)=='#'? parts[3].substring(1):null).build();
return null;
}
//works for the toString output expect for extensions. test: for(Locale l: Locale.getAvailableLocales()) System.out.println(l.equals(stringToLocale(l.toString())));
// output : true true true...
usage:
Locale l = stringToLocale("az_AZ_#Latn");

Locale.Builder is able to handle script information for locales. The documentation of the Builder class also includes this example code:
Locale aLocale = new Locale.Builder().setLanguage("sr")
.setScript("Latn")
.setRegion("RS")
.build();
By using the builder you would have to do the splitting of the string yourself and also removal of any unsupported characters like #.
Using the 3-arg contructor java.util.Locale.Locale(String, String, String) is not correct since you probably don't intend to specify a variant using Latn but a script instead.

If the format is consistent (your input looks always the same) you can split by # and by _ and get the parts.
See the following example:
var input = "az_AZ#Latn"
var lns = input.split("#")
var l = lns[0].split("_")
var locale = new Locale.Builder()
.setLanguage(l[0])
.setRegion(l[1])
.setScript(lns[1])
.build()
locale.getLanguage() // ==> "az"
locale.getCountry() // ==> "AZ"
locale.getScript() //==> "Latn"

That #Latn refers to the script which in this case is Latin.
From java documentation:
script
ISO 15924 alpha-4 script code. You can find a full list of valid script codes in the IANA Language Subtag Registry (search for "Type: script"). The script field is case insensitive, but Locale always canonicalizes to title case (the first letter is upper case and the rest of the letters are lower case).
Well-formed script values have the form [a-zA-Z]{4}
Example: "Latn" (Latin), "Cyrl" (Cyrillic)
If you want to create a Locale using the script you can use its builder.
For instance:
Locale locale = new Locale.Builder()
.setLanguage("az")
.setRegion("AZ")
.setScript("Latn")
.build();

Related

Cannot get '#' symbol in Controller using Spring #RequestParam

I have the following request Url /search?charset=UTF-8&q=C%23C%2B%2B.
My controller looks like
#RequestMapping(method = RequestMethod.GET, params = "q")
public String refineSearch(#RequestParam("q") final String searchQuery,....
and here i have searchQuery = 'CC++'.
'#' is encoded in '%23' and '+' is '%2B'.
Why searchQuery does not contain '#'?
searchQuery in debug
I resolved a similar problem by URL encoding the hash part. We have Spring web server and mix of JS and VueJS client. This fixed my problem:
const location = window.location;
const redirect = location.pathname + encodeURIComponent(location.hash);
The main cause is known as the "fragment identifier". You find more detail for Fragment Identifier right here. It says:
The fragment identifier introduced by a hash mark # is the optional last part of a URL for a document. It is typically used to identify a portion of that document.
When you write # sign, it contains info for clientbase. Put everything only the browser needs here. You can get this problem for all types of URI characters you can look Percent Encoding for this. In my opinion The simple solution is character replacing, you could try replace in serverbase.
Finally i found a problem.In filters chain ServletRequest is wrapped in XSSRequestWrapper with DefaultXSSValueTranslator and here is the method String stripXSS(String value) which iterates through pattern list,in case if value matches with pattern, method will delete it.
Pattern list contains "\u0023" pattern and '#' will be replaced with ""
DefaultXSSValueTranslator.
private String stripXSS(String value) {
Pattern scriptPattern;
if (value != null && value.length() > 0) {
for(Iterator var3 = this.patterns.iterator(); var3.hasNext(); value = scriptPattern.matcher(value).replaceAll("")) {
scriptPattern = (Pattern)var3.next();
}
}
return value;
}

Formatting string content xtext 2.14

Given a grammar (simplified version below) where I can enter arbitrary text in a section of the grammar, is it possible to format the content of the arbitrary text? I understand how to format the position of the arbitrary text in relation to the rest of the grammar, but not whether it is possible to format the content string itself?
Sample grammar
Model:
'content' content=RT
terminal RT: // (returns ecore::EString:)
'RT>>' -> '<<RT';
Sample content
content RT>>
# Some sample arbitrary text
which I would like to format
<<RT
you can add custom ITextReplacer to the region of the string.
assuming you have a grammar like
Model:
greetings+=Greeting*;
Greeting:
'Hello' name=STRING '!';
you can do something like the follow in the formatter
def dispatch void format(Greeting model, extension IFormattableDocument document) {
model.prepend[newLine]
val region = model.regionFor.feature(MyDslPackage.Literals.GREETING__NAME)
val r = new AbstractTextReplacer(document, region) {
override createReplacements(ITextReplacerContext it) {
val text = region.text
var int index = text.indexOf(SPACE);
val offset = region.offset
while (index >=0){
it.addReplacement(region.textRegionAccess.rewriter.createReplacement(offset+index, SPACE.length, "\n"))
index = text.indexOf(SPACE, index+SPACE.length()) ;
}
it
}
}
addReplacer(r)
}
this will turn this model
Hello "A B C"!
into
Hello "A
B
C"!
of course you need to come up with a more sophisticated formatter logic.
see How to define different indentation levels in the same document with Xtext formatter too

How to create Currency instance with non ISO 3166 country like en_UK?

In my app, I get the user's default locale using Locale.getDefault() and then pass that to Currency.getInstance(Locale). It mostly works, but I have started getting reports from users which show the following IllegalArgumentException in the stack trace:
Caused by: java.lang.IllegalArgumentException: Unsupported ISO 3166
country: en_UK at java.util.Currency.getInstance(Currency.java:81) at
org.
I expected Android to only return valid locales, but that is apparently not the case.
How do I handle such cases to make sure I only get valid ISO 3166 locales? The easy way will be to handle this special case, but I would rather use a generic solution if there is one.
Anyone have experience with this? Thanks.
The ISO 3166 two-letter abbreviation for the UK is not UK, the correct id is GB. UK is there for compatibility reasons (a mistake made in the past).
I did look for other exeptions but did not find any, so for now i would just handle the special case.
Locale loc = new Locale("en","UK"); // test code
if(loc.getCountry().equals("UK")){
loc = new Locale(loc.getLanguage(), "GB");
}
Currency cur = Currency.getInstance(loc);
Currency.getInstance(...) expects a locale of the form "en_US", however, Locale.getDefault() does not always return a locale of this form.
To prevent crashes in your app, you can use something like this:
public static String getCurrencySymbol(){
String cursym;
try {
cursym = Currency.getInstance(Locale.getDefault()).getSymbol();
} catch (IllegalArgumentException e) {
cursym = "?"; // default symbol
}
return cursym;
}
You can try to figure out a better way to get the most appropriate symbol, or just let the user choose one.
Your users (with dev settings enabled) or testers have manually set a weird and invalid country. This bug occurred to us, but it turned out our testers had configured Appium to set the device locale to en-UK :).
A regular user can not select en-UK or any other invalid locale.
String symbolLocale ="";
int localeLength = mLocale.split("-").length;
if (localeLength == 2) {
symbolLocale = new Locale(
mLocale.split("-", 2)[0],
mLocale.split("-", 2)[1]
);
} else if (localeLength == 3) {
symbolLocale = new Locale(
mLocale.split("-", 3)[0] + "-" +
mLocale.split("-", 3)[1],
mLocale.split("-", 3)[2]
);
}
symbolString = Currency.getInstance(symbolLocale).getSymbol(symbolLocale);

url harvester string manipulation

I'm doing a recursive url harvest.. when I find an link in the source that doesn't start with "http" then I append it to the current url. Problem is when I run into a dynamic site the link without an http is usually a new parameter for the current url. For example if the current url is something like http://www.somewebapp.com/default.aspx?pageid=4088 and in the source for that page there is a link which is default.aspx?pageid=2111. In this case I need do some string manipulation; this is where I need help.
pseudocode:
if part of the link found is a contains a substring of the current url
save the substring
save the unique part of the link found
replace whatever is after the substring in the current url with the unique saved part
What would this look like in java? Any ideas for doing this differently? Thanks.
As per comment, here's what I've tried:
if (!matched.startsWith("http")) {
String[] splitted = url.toString().split("/");
java.lang.String endOfURL = splitted[splitted.length-1];
boolean b = false;
while (!b && endOfURL.length() > 5) { // f.bar shortest val
endOfURL = endOfURL.substring(0, endOfURL.length()-2);
if (matched.contains(endOfURL)) {
matched = matched.substring(endOfURL.length()-1);
matched = url.toString().substring(url.toString().length() - matched.length()) + matched;
b = true;
}
}
it's not working well..
I think you are doing this the wrong way. Java has two classes URL and URI which are capable of parsing URL/URL strings much more accurately than a "string bashing" solution. For example the URL constructor URL(URL, String) will create a new URL object in the context of an existing one, without you needing to worry whether the String is an absolute URL or a relative one. You would use it something like this:
URL currentPageUrl = ...
String linkUrlString = ...
// (Exception handling not included ...)
URL linkUrl = new URL(currentPageUrl, linkUrlString);

How can I used named parameters in a messages.properties file?

Is there any way to have message.properties records as follows
message.myMessage=This message is for ${name} in ${location}
as opposed to
message.myMessage = This message is for {0} in {1}
When I am creating the messages, I don't neccessarily know the order / how many parameters are needed, but I am able just pass in several properties by name, and just the correct ones would be used.
After facing the very same question and poking in source code I found a "loop-hole" that makes it possible in a very easy way:
message.myMessage = This message is for {0,,name} in {1,,location}
This approach doesn't eliminate usage of numbers. The reason to use it is to give hints to translation folks.
I am afraid not, parameters are an Object array so there is no way to define names for them. If you always passes in the array of parameter in the same order though you could use them like this:
message.myMessage = This message is for {0} in {1}
message.myNameMessage = This message is for {0}
message.myLocationMessage = This message is for people in {1}
message.myAlternateMessage = The message params are location: {1}; name: {0}
Take a look at ICU4J
It allows for something like this:
message.myMessage=This message is for {name} in {location}.
And it is way more powerful than the simple replacements suggested, because can do locale aware formatting of the parameters (ie: "Subscription expires on: {expirationDate, date, long})
http://icu-project.org/apiref/icu4j/com/ibm/icu/text/MessageFormat.html
Unfortunately the MessageFormat API does not support named parameters, only argument-index:
Patterns and Their Interpretation
MessageFormat uses patterns of the following form:
MessageFormatPattern:
String
MessageFormatPattern FormatElement String
FormatElement:
{ ArgumentIndex }
{ ArgumentIndex , FormatType }
{ ArgumentIndex , FormatType , FormatStyle }
Everything is possible for those who try... I never heard about something like that for Java, but you can write it by yourself.
Please take a look at this example:
public String format(String message, String... arguments) {
for (String argument : arguments) {
String[] keyValue = argument.split("=");
if (keyValue.length != 2)
throw new IllegalArgumentException("Incorrect argument: " + argument);
String placeholder = "${" + keyValue[0] + "}";
if (!message.contains(placeholder))
throw new IllegalArgumentException(keyValue[0] + " does not exists.");
while (message.contains(placeholder))
message = message.replace(placeholder, keyValue[1]);
}
return message;
}
It is not ideal, as you actually would call it with hardcoded string (which is generally bad idea) and you would be forced to use Strings only, but it can be done. The only question is if it is practical.
It is possible using apache commons lang library.
https://commons.apache.org/proper/commons-lang/
Properties messages = ...
Map<String, String> m = new HashMap<>();
m.put("name", "Mithu");
m.put("location", "Dhaka");
StrSubstitutor sub = new StrSubstitutor(m);
String msg = sub.replace(messages.getProperty("message.myMessage"));
// msg = This message is for Mithu in Dhaka

Categories

Resources