Skip to content

Commit 3b128ce

Browse files
committed
document translation medium and other models
1 parent 5f6bbbd commit 3b128ce

File tree

6 files changed

+175
-46
lines changed

6 files changed

+175
-46
lines changed

app/src/main/java/com/example/phrase/MainActivity.kt

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,28 @@ class MainActivity : AppCompatActivity() {
2828

2929
val font = Typeface.createFromAsset(assets, "rb.ttf")
3030
// setting up phrase
31-
val p = phrase {
32-
mediums = listOf(GoogleTranslate(this@MainActivity, R.raw.credential))
33-
options = options {
34-
targeting = target.text.toString()
35-
preferredSources = listOf("es", "yo")
36-
sourceTranslation = listOf(SourceTranslationRule("fr", listOf("en")))
37-
behaviour = behaviour {
38-
flags = setOf(Behaviour.BEHAVIOR_TRANSLATE_PREFERRED_OPTION_ONLY)
39-
signatureTypeface = font
40-
signatureColor =
41-
ContextCompat.getColor(this@MainActivity, R.color.white)
42-
}
43-
actionLabel = { detected ->
44-
"Translate"
45-
}
31+
val pOptions = options {
32+
targeting = target.text.toString()
33+
preferredSources = listOf("es", "yo")
34+
sourceTranslation = listOf(SourceTranslationRule("fr", listOf("en")))
35+
behaviour = behaviour {
36+
flags = setOf(Behaviour.BEHAVIOR_TRANSLATE_PREFERRED_OPTION_ONLY)
37+
signatureTypeface = font
38+
signatureColor =
39+
ContextCompat.getColor(this@MainActivity, R.color.white)
40+
}
41+
actionLabel = { detected ->
42+
"Translate"
43+
}
4644

47-
resultActionLabel = { phraseTranslation ->
48-
"Translated from ${phraseTranslation.detectedSource?.languageName} by "
49-
}
45+
resultActionLabel = { phraseTranslation ->
46+
"Translated from ${phraseTranslation.detectedSource?.languageName} by "
5047
}
5148
}
52-
p.de
53-
49+
val phrase = phrase {
50+
mediums = listOf(GoogleTranslate(this@MainActivity, R.raw.credential))
51+
options = pOptions
52+
}
5453

5554
phraseSpannableBuilder =
5655
object : PhraseSpannableBuilder("", null) {
@@ -70,6 +69,7 @@ class MainActivity : AppCompatActivity() {
7069

7170

7271
update_source.setOnClickListener {
72+
pOptions.targetLanguageCode = target.text.toString()
7373
phraseSpannableBuilder.updateSource(source_edit.text.toString())
7474
}
7575
}

phrase/src/main/java/xyz/belvi/phrase/helpers/PhraseSpannableBuilder.kt

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,24 @@ abstract class PhraseSpannableBuilder constructor(
7979
*/
8080
private fun options() = phraseOptions ?: Phrase.instance().phraseImpl.phraseOptions
8181

82+
/**
83+
* build string to include original text and a translate actionLabel.
84+
* Phrase adds translate actionLabel based options provided via phraseOptions.
85+
* First, sourceLanguage is detected
86+
* then, sourceLanguage is unable to be detected, translation actionLabel is not appended to source (if user doesn't set BEHAVIOR_IGNORE_DETECTION)
87+
*/
8288
private fun buildTranslateActionSpan(source: CharSequence) {
8389
init()
8490
GlobalScope.launch(Dispatchers.Main) {
8591
val options = options() ?: return@launch
8692
val behaviors = options.behavioursOptions.behaviours
87-
val detectedMedium =
93+
94+
/*
95+
* detected source language. is sourceLanguage is provided, we want to skip language detection.
96+
* if sourceLanguage is not provided and BEHAVIOR_IGNORE_DETECTION is set, phraseDetected is null since we want to skip language detection.
97+
* Otherwise, Phrase.instance().detectLanguage(source.toString()) is executed.
98+
*/
99+
val phraseDetected =
88100
sourceLanguage?.let { sourceLanguage ->
89101
val languageName =
90102
Languages.values().find { it.code == sourceLanguage.toLowerCase() }?.name
@@ -104,18 +116,43 @@ abstract class PhraseSpannableBuilder constructor(
104116
}
105117
}
106118

107-
detectedMedium?.let { phraseDetected ->
119+
if (options.excludeSources.indexOfFirst {
120+
it.toLowerCase() == (phraseDetected?.languageCode ?: "").toLowerCase()
121+
} < 0) {
122+
123+
}
124+
/* with the detected sourceLanguage, check to see if there's any rule that check against translating from the source language at all
125+
*/
126+
127+
phraseDetected?.let { detected ->
128+
// halt this process if the sourceLangauge is part of the excluded list or it is same with the target langauge,
129+
if (detected.languageCode.equals(
130+
options.targetLanguageCode.toLowerCase(),
131+
true
132+
) || options.excludeSources.indexOfFirst {
133+
it.equals(
134+
detected.languageCode,
135+
true
136+
)
137+
} > 0
138+
) {
139+
actionStatus = ActionStatus.SHOWING_SOURCE
140+
onContentChanged(this@PhraseSpannableBuilder)
141+
return@launch
142+
}
108143

144+
// if options supports translation of only preferred sources, check if sourceLanguage is in preferred list.
109145
var allowTranslation =
110146
if (options.behavioursOptions.behaviours.translatePreferredSourceOnly()) {
111147
options.preferredSources.indexOfFirst {
112-
it.equals(phraseDetected.languageCode, true)
148+
it.equals(detected.languageCode, true)
113149
} >= 0
114150
} else {
115151
true
116152
}
153+
// check if source langauge is defined in languageTranslation preference
117154
allowTranslation =
118-
(options.sourcePreferredTranslation.sourceTranslateRule.filter { it.sourceLanguageCode.toLowerCase() == phraseDetected.languageCode.toLowerCase() }
155+
(options.sourcePreferredTranslation.sourceTranslateRule.filter { it.sourceLanguageCode.toLowerCase() == detected.languageCode.toLowerCase() }
119156
.let { sourceOptions ->
120157
sourceOptions.find { sourceTranslationOption ->
121158
sourceTranslationOption.targetLanguageCode.indexOfFirst {
@@ -129,36 +166,25 @@ abstract class PhraseSpannableBuilder constructor(
129166
}?.let { true }
130167
?: !options.behavioursOptions.behaviours.translatePreferredSourceOnly()
131168
}) || allowTranslation
169+
// if source language is neither in sourcePreferredTranslation nor preferredSources and BEHAVIOR_TRANSLATE_PREFERRED_OPTION_ONLY is set, actionLable for translation shouldn't be appended to this string.
132170
if (!allowTranslation) {
133171
actionStatus = ActionStatus.SHOWING_SOURCE
134172
onContentChanged(this@PhraseSpannableBuilder)
135173
return@launch
136174
}
137-
138-
if (phraseDetected.languageCode.equals(
139-
options.targetLanguageCode.toLowerCase(),
140-
true
141-
) || options.excludeSources.indexOfFirst {
142-
it.equals(
143-
phraseDetected.languageCode,
144-
true
145-
)
146-
} > 0
147-
) {
148-
actionStatus = ActionStatus.SHOWING_SOURCE
149-
onContentChanged(this@PhraseSpannableBuilder)
150-
return@launch
151-
}
152175
}
153-
154-
if (detectedMedium == null && !behaviors.ignoreDetection()) {
176+
// another check to ensure nullable phraseDetected is only allowed for BEHAVIOR_IGNORE_DETECTION
177+
if (phraseDetected == null && !behaviors.ignoreDetection()) {
155178
onContentChanged(this@PhraseSpannableBuilder)
156179
return@launch
157180
}
181+
// final check to confirm original text is not empty.
182+
// also, actionLabel is appended only if BEHAVIOR_HIDE_TRANSLATE_PROMPT is not set and source is not same with the content of PhraseSpannableBuilder (to avoid text repetition)
158183
if (!source.isNullOrBlank() && !behaviors.hideTranslatePrompt() && (this@PhraseSpannableBuilder.toString() == source.toString())) {
159184
appendln("\n")
160185
val start = length
161-
append(options.translateText.invoke(detectedMedium))
186+
append(options.translateText.invoke(phraseDetected))
187+
// add clickableSpan to actionLabel
162188
setSpan(
163189
SpannablePhraseClickableSpan(),
164190
start,
@@ -172,26 +198,35 @@ abstract class PhraseSpannableBuilder constructor(
172198
}
173199
}
174200

201+
/**
202+
* build string to include original text and a translated actionLabel and the translated string
203+
* Phrase adds translate resultActionLabel based options provided via phraseOptions.
204+
* this is called after translation has happened.
205+
*/
175206
private fun buildTranslatedPhraseSpan() {
176207
val options = options() ?: return
177208
val optionBehavior = options.behavioursOptions.behaviours
178209
phraseTranslation?.let { phraseTranslation ->
179210
init()
180-
appendln("\n")
211+
// if BEHAVIOR_REPLACE_SOURCE_TEXT is set, we want to replace the source text with translation.
181212
if (optionBehavior.replaceSourceText()) {
182213
clear()
183214
}
215+
// resultActionLabel is appeneded to result is BEHAVIOR_HIDE_TRANSLATE_PROMPT is not set.
184216
if (!optionBehavior.hideTranslatePrompt()) {
185217
var start = length
186218
append(options.translateFrom.invoke(phraseTranslation))
219+
// add clickableSpan to resultActionLabel
187220
setSpan(
188221
SpannablePhraseClickableSpan(),
189222
start,
190223
length,
191224
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
192225
)
226+
// style translation medium name.
193227
if (!optionBehavior.hideSignature()) {
194228
start = length
229+
appendln("\n")
195230
append("${phraseTranslation.translationMediumName}")
196231
options.behavioursOptions.signatureTypeFace?.let { typeFace ->
197232
setSpan(
@@ -214,8 +249,8 @@ abstract class PhraseSpannableBuilder constructor(
214249
)
215250
}
216251
}
217-
appendln("\n")
218252
}
253+
appendln("\n")
219254
append(phraseTranslation.translation)
220255
actionStatus = ActionStatus.SHOWING_TRANSLATED
221256
onContentChanged(this@PhraseSpannableBuilder)
@@ -234,21 +269,30 @@ abstract class PhraseSpannableBuilder constructor(
234269
append(source)
235270
}
236271

272+
/**
273+
* customer ClickableSpan impementation to handle click actions on actionLabel and resultActionLabel
274+
*/
237275
inner class SpannablePhraseClickableSpan : ClickableSpan() {
238276
override fun onClick(widget: View) {
239277
onActionClick(actionStatus)
278+
// if text is currently prompting user to translate
240279
if (actionStatus == ActionStatus.SHOWING_WITH_TRANSLATE_ACTION) {
280+
// notify listener of tranalation
241281
onPhraseTranslating()
282+
// translate text
242283
GlobalScope.launch(Dispatchers.Main) {
243284
val options = options()
244285
phraseTranslation =
245286
withContext(Dispatchers.IO) {
246287
Phrase.instance().translate(source.toString(), options)
247288
}
248289
}
290+
// build translated string
249291
buildTranslatedPhraseSpan()
292+
// notify listener of translation
250293
onPhraseTranslated(phraseTranslation)
251294
} else {
295+
// resultActionLabel is clicked, show original state of text
252296
buildTranslateActionSpan(source)
253297
}
254298
widget.invalidate()

phrase/src/main/java/xyz/belvi/phrase/helpers/PhraseTextWatcher.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,10 @@ open class PhraseTextWatcher(
6969
}
7070

7171
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
72+
// we do not want to update source for empty string.
7273
if (s.isNullOrBlank())
7374
return
75+
// only update source when changes is not same with current content of phraseSpannableBuilder
7476
if (s.toString() != phraseSpannableBuilder.toString())
7577
phraseSpannableBuilder.updateSource(s.subSequence(0, s.length), sourceLanguage)
7678
}

phrase/src/main/java/xyz/belvi/phrase/options/Models.kt

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ import xyz.belvi.phrase.options.Behaviour.Companion.BEHAVIOR_IGNORE_DETECTION
77
import xyz.belvi.phrase.options.Behaviour.Companion.BEHAVIOR_REPLACE_SOURCE_TEXT
88
import xyz.belvi.phrase.options.Behaviour.Companion.BEHAVIOR_TRANSLATE_PREFERRED_OPTION_ONLY
99

10+
/**
11+
* object return when TranlationMedium.detect() is called.
12+
* @param text : original text whose source language was detected
13+
* @param languageCode: language code of detected language
14+
* @param languageName: language name of detected language
15+
* @param detectionMediumName: medium used in detecting text
16+
* @param fromCache: there was no new api call made to retrieve this result. It was fetched from cache
17+
*/
1018
data class PhraseDetected(
1119
val text: String,
1220
val languageCode: String,
@@ -15,24 +23,51 @@ data class PhraseDetected(
1523
val fromCache: Boolean = false
1624
)
1725

26+
/**
27+
* used by Phrase to handle text Translation
28+
* @param translation : translated text
29+
* @param detectedSource: PhraseDetected object for the original text
30+
* @param fromCache: there was no new api call made to retrieve this result. It was fetched from cache
31+
*/
1832
data class PhraseTranslation(
1933
val translation: String,
2034
val translationMediumName: String?,
2135
val detectedSource: PhraseDetected?,
2236
val fromCache: Boolean = false
2337
)
2438

39+
/**
40+
* for defining behavior and flags for Phraseoptions
41+
* @param behaviorSet is a set of @BehaviorFlags
42+
*/
2543
class Behaviour(private val behaviorSet: Set<@BehaviorFlags Int> = setOf()) {
2644

2745
companion object {
46+
/**
47+
* include this flag if translated text should be replaced by source text
48+
*/
2849
const val BEHAVIOR_REPLACE_SOURCE_TEXT: Int = 1
29-
50+
/**
51+
* include this flag if Phrase should only run translation for languageCode included in PhraseOptions preferredSource and sourceTranslationPreference.
52+
*/
3053
const val BEHAVIOR_TRANSLATE_PREFERRED_OPTION_ONLY: Int = 2
3154

55+
/**
56+
* include this flag if Phrase should ignore language detection before processing translation.
57+
* This is helpful if you already know the source language of the original text (in this case, ensure source language is pass to PhraseSpannableBuilder or
58+
* PhraseTextView.prepare(), depending on the Implementation of Phrase you are using.)
59+
* @see https://github.com/KingsMentor/Phrase documentation.
60+
*/
3261
const val BEHAVIOR_IGNORE_DETECTION: Int = 3
3362

63+
/**
64+
* include this flag if Phrase should not include translationMediumName to resultActionLabel
65+
*/
3466
const val BEHAVIOR_HIDE_CREDIT: Int = 4
3567

68+
/**
69+
* include this flag if Phrase not show actionLabel ot resultActionLabel at all.
70+
*/
3671
const val BEHAVIOR_HIDE_TRANSLATE_PROMPT: Int = 5
3772
}
3873

phrase/src/main/java/xyz/belvi/phrase/translateMedium/Languages.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package xyz.belvi.phrase.translateMedium
22

3+
/**
4+
* source language code.
5+
* @see 'https://cloud.google.com/translate/docs/languages' for an updated list.
6+
*/
37
enum class Languages(val code: String) {
48
Afrikaans("af"),
59
Albanian("sq"),

0 commit comments

Comments
 (0)