11package com .box .l10n .mojito .okapi .filters ;
22
3+ import java .util .regex .Matcher ;
4+ import java .util .regex .Pattern ;
35import net .sf .okapi .common .encoder .EncoderContext ;
46import net .sf .okapi .filters .its .Parameters ;
57import org .apache .commons .lang .StringUtils ;
68
79/**
8- * This overrides the {@link net.sf.okapi.common.encoder.XMLEncoder}
9- * to not to escape supported HTML elements for Android strings.
10+ * This overrides the {@link net.sf.okapi.common.encoder.XMLEncoder} for Android
11+ * strings.
12+ *
13+ * It does not escape supported HTML elements for Android strings unless there
14+ * are variables within the HTML elements.
15+ * For example, <b>songs</b> vs. <b>%d songs</b>
16+ *
1017 * Also it overrides the default quotemode setting so the quotes do not get escaped.
11- * According to Android specification in http://developer.android.com/guide/topics/resources/string-resource.html,
12- * <b>bold</b>, <i>italian</i> and <u>underline</u> should be in localized file as-is.
13- *
18+ *
19+ * For detailed information, see to Android specification in
20+ * http://developer.android.com/guide/topics/resources/string-resource.html,
21+ *
1422 * @author jyi
1523 */
1624public class XMLEncoder extends net .sf .okapi .common .encoder .XMLEncoder {
17-
25+
26+ // trying to match variables between html tags, for example, <b>%d</b>, <i>%1$s</i>, <u>%2$s</u>
27+ private static final Pattern ANDROID_VARIABLE_WITHIN_HTML = Pattern .compile ("(<[b|i|u]>)((.*?)%(([-0+ #]?)[-0+ #]?)((\\ d\\ $)?)(([\\ d\\ *]*)(\\ .[\\ d\\ *]*)?)[dioxXucsfeEgGpn](.*?))+(</[b|i|u]>)" );
28+ private static final Pattern ANDROID_HTML = Pattern .compile ("(<)(/?)(b|i|u)(>)" );
29+ private static final Pattern UNESCAPED_DOUBLE_QUOTE = Pattern .compile ("([^\\ \\ ])(\" )" );
30+ private static final Pattern START_WITH_DOUBLE_QUOTE = Pattern .compile ("(^\" )" );
31+ private static final Pattern UNESCAPED_SINGLE_QUOTE = Pattern .compile ("([^\\ \\ ])(')" );
32+ private static final Pattern START_WITH_SINGLE_QUOTE = Pattern .compile ("(^')" );
33+
1834 @ Override
1935 public String encode (String text , EncoderContext context ) {
2036 String encoded = super .encode (text , context );
2137 if (isAndroidStrings ()) {
22- encoded = escape (encoded );
38+ encoded = escapeAndroid (encoded );
2339 }
2440 return encoded ;
2541 }
@@ -32,39 +48,44 @@ private boolean isAndroidStrings() {
3248 return false ;
3349 }
3450 }
35-
36- public String escape (String text ) {
51+
52+ public String escapeAndroid (String text ) {
3753 boolean enclosedInDoubleQuotes = StringUtils .startsWith (text , "\" " ) && StringUtils .endsWith (text , "\" " );
3854 if (enclosedInDoubleQuotes ) {
3955 text = text .substring (1 , text .length () - 1 );
4056 }
41-
42- String pattern = "(<)(/?)(b|i|u)(>)" ;
43- String replacement = "<$2$3>" ;
44- text = text .replaceAll (pattern , replacement );
57+
58+ String replacement ;
59+ if (needsAndroidEscapeHTML (text )) {
60+ replacement = "$1$2$3>" ;
61+ } else {
62+ replacement = "<$2$3>" ;
63+ }
64+ text = ANDROID_HTML .matcher (text ).replaceAll (replacement );
4565 text = text .replaceAll ("\n " , "\\ \\ n" );
4666 text = text .replaceAll ("\r " , "\\ \\ r" );
4767 text = escapeDoubleQuotes (text );
4868 if (!enclosedInDoubleQuotes ) {
4969 text = escapeSingleQuotes (text );
5070 }
5171 return enclosedInDoubleQuotes ? "\" " + text + "\" " : text ;
52-
72+
5373 }
54-
74+
75+ private boolean needsAndroidEscapeHTML (String text ) {
76+ Matcher matcher = ANDROID_VARIABLE_WITHIN_HTML .matcher (text );
77+ return matcher .find ();
78+ }
79+
5580 private String escapeDoubleQuotes (String text ) {
56- String pattern1 = "([^\\ \\ ])(\" )" ;
57- String pattern2 = "(^\" )" ;
58- String escaped = text .replaceAll (pattern1 , "$1\\ \\ $2" );
59- escaped = escaped .replaceFirst (pattern2 , "\\ \\ $1" );
81+ String escaped = UNESCAPED_DOUBLE_QUOTE .matcher (text ).replaceAll ("$1\\ \\ $2" );
82+ escaped = START_WITH_DOUBLE_QUOTE .matcher (escaped ).replaceFirst ("\\ \\ $1" );
6083 return escaped ;
6184 }
6285
6386 private String escapeSingleQuotes (String text ) {
64- String pattern1 = "([^\\ \\ ])(')" ;
65- String pattern2 = "(^')" ;
66- String escaped = text .replaceAll (pattern1 , "$1\\ \\ $2" );
67- escaped = escaped .replaceFirst (pattern2 , "\\ \\ $1" );
87+ String escaped = UNESCAPED_SINGLE_QUOTE .matcher (text ).replaceAll ("$1\\ \\ $2" );
88+ escaped = START_WITH_SINGLE_QUOTE .matcher (escaped ).replaceFirst ("\\ \\ $1" );
6889 return escaped ;
6990 }
7091}
0 commit comments