-
Notifications
You must be signed in to change notification settings - Fork 0
First Stab at I18N #20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
a09386a
to
496daad
Compare
viewModel: StoneCameraViewModel, | ||
previousMode: String, | ||
nextMode: String | ||
previousMode: TranslatableString, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it possible to keep original String
type?
|
||
override val modeLabel | ||
get() = "video" | ||
get() = @Translatable "video".i18n() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks a bit too "bulky"
is there any options to use only either annotation or i18n()
method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming that this approach is quite bulky, there may be cases where someone simply hard codes a string without using @Translatable
or calling .i18n()
. This would mean that string wouldn't be considered for translation and the app would ship with hardcoded strings. How might we check for this happening to reduce the likelihood the app is shipped with hardcoded strings rather than translatable/translated strings?
val controlModeGet = viewModel.getSetting<TranslatableString>("volumeControlMode") | ||
|
||
if (isZoomMode || controlModeGet == "Zoom") { | ||
if (isZoomMode || controlModeGet?.resolve() == "Zoom") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.resolve() == "Zoom"
will it still work in different langs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question :) @ISNIT0 ?
} | ||
|
||
private fun sanitizeKey(key: String): String { | ||
val sanitized = key.replace(Regex("[^a-zA-Z0-9]"), "_") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not a collision proof solution
a sanitized key for "!Test"
will be equal to the key for "?Test"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
potentially different parts of the app could have same strings in english but with different translations to other languages
so i would suggest to introduce a module prefix for keys
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, for example some strings in English are written the same but have very different meanings so the same translation might not suit each use of what is lexically a common string. I'll provide a small example: "row" as in a row in a spreadsheet is not the same word as "row" as in two people had a row, or to row a boat.
Text(text = modeLabel.uppercase(), | ||
color = if (modeLabel == selectedMode) Color(0xFFFFCC00) else Color.White, | ||
Text(text = modeLabel.resolve().uppercase(), | ||
color = if (modeLabel.resolve() == selectedMode.resolve()) Color(0xFFFFCC00) else Color.White, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as i've got you can leave it as it was modeLabel == selectedMode
sourceDir.walkTopDown() | ||
.filter { it.isFile && it.extension in listOf("kt", "java") } | ||
.forEach { file -> | ||
val regex = Regex("@Translatable\\s*\\\"(.*?)\\\"") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cuts the string if you put \"
in the middle
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like having automated test cases that check the translation will behave as desired even for multi-line strings, etc. might be useful at checking whether these sorts of regex mistakes are dealt with?
/** | ||
* Sanitizes a string to make it a valid XML key. | ||
*/ | ||
private fun sanitizeKey(key: String): String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
duplicated logic with
advanced-android-camera/app/src/main/java/co/stonephone/stonecamera/utils/TranslatableString.kt
Line 26 in 6cea681
private fun sanitizeKey(key: String): String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall I like this innovative approach to bootstrap translations of textual strings and it's a really nice idea. For production use I suspect the project may need suitably competent humans-in-the-loop for the translations, at least for a while.
It's probably worth another round of code reviews if you decide to refine the code based on my and other reviewer's comments. Happy to do so.
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
|
||
TranslationManager.loadTranslationsForLocale(applicationContext) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this guarantee to return a set of elements (translations) regardless of the application's context? In other words are there ever going to be cases where it wouldn't return a) something the program can run with, b) something the human user can understand?
|
||
TranslationManager.loadTranslationsForLocale(applicationContext) | ||
|
||
if (!isChromeOS()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dumb question and not directly part of this PR... Are we targeting ChromeOS?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the removal of android.util.log what's happening about logging?
workList.awaitAll() | ||
Log.d("Analyzer", "All plugins completed successfully") | ||
} catch (e: Exception) { | ||
Log.e("Analyzer", "Error in one or more plugins", e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again a question not directly part of this PR, but triggered when reading the code deleted above, what happens with the worklist.awaitAll() if an exception occurs in one or more of the plugins that are running in parallel?
}, | ||
renderLocation = SettingLocation.TOP, | ||
label = "Aspect Ratio" | ||
label = @Translatable "Aspect Ratio".i18n() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this call respond if a match isn't found for the string being translated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's the potential for translated strings to require significant changes to screen real-estate for various reasons such as phrasing in that locale, to mistranslations, to nefarious attacks. Probably worth adding some forms of sanity checking of translated strings before they're incorporated into an app.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How long is the translation expected to take? and is the translation process intended to complete before the app is compiled? Oh, and are the translations something that are expected to be added into a git repo or simply processed on the build machine (which may be an ephemeral computer in the cloud)? I expect that auditability of translations (localisations) will be key at various times in the life of the project, sometimes long after the event that performed the translation/localisation, so auditing and maintaining easily readable snapshots might also be key to do.
abstract val resDir: DirectoryProperty | ||
|
||
private val apiKey: String = | ||
System.getenv("OPENAI_API_KEY") ?: error("OPENAI_API_KEY environment variable not set") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this topic has already been mentioned in discussion so this is more of a reminder that relying on OpenAI (or other similar services) might be problematic from a legal/licensing perspective as and when the app is in production use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about adding logging to this file as a partial approach to maintaining a record of what's happening?
language: String | ||
): String { | ||
val languageName = when (language) { | ||
"es" -> "Spanish" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are great from a proof-of-concept point-of-view. Worth considering supporting locales rather than raw language codes e.g. to support es_MX
for Mexican Spanish, etc.
sourceDir.walkTopDown() | ||
.filter { it.isFile && it.extension in listOf("kt", "java") } | ||
.forEach { file -> | ||
val regex = Regex("@Translatable\\s*\\\"(.*?)\\\"") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like having automated test cases that check the translation will behave as desired even for multi-line strings, etc. might be useful at checking whether these sorts of regex mistakes are dealt with?
How to make a string auto-internationalise: