https://kotlinlang.org logo
#compose
Title
# compose
m

Miguel Vargas

05/24/2019, 7:38 PM
Is the plan that Compose will use the framework's app resource system, eg res/drawable-land etc? All the snippets I've seen show things like
Text ("Hello World")
instead of
Text (R.string.hello_world)
l

Leland Richardson [G]

05/24/2019, 7:49 PM
most of the examples just show string because we don’t have a well-formed answer around this yet. Also because strings are just simpler to show 🙂 The current resource system will continue to work, and any kotlin code you can use to grab those resources will continue to work. The new UI Toolkit (Compose UI) may have a redesigned resource system, but we’re still not sure what that would look like yet, or if it will happen (Not super involved with this part of it personally, so someone can correct me if this isn’t entirely accurate)
l

louiscad

05/24/2019, 7:55 PM
I guess Compose goal is to be multiplatform at some point, and that means platform agnostic resources referencing and usage. Side-note: I strongly dislike Android's string resources that require to escape all the apostrophes or single quotes
'
manually. Raw string literals are better regarding this (except when you want to have double quotes at the beginning/end of a string, or triple+ quotes anywhere inside). I hope we get something better for l10n and i18n with what will work with Compose.
👎 3
k

karelpeeters

05/24/2019, 7:56 PM
You need some way to store translations anyway, xml isn't too bad for that.
1
6
a

Adam Powell

05/24/2019, 7:56 PM
There will be a way to do this as a one-liner one way or another, if only because people have tons of existing resources if nothing else
Chances are it won't take the form of overloaded composable methods with resource id params though
no red 1
👍 1
I'm curious what those of you who are -ing the above comment would rather do for translations given that raw inline string literals really aren't an option there 🙂
If it's just some specifics of the current string xml format like the escape sequences or something else
l

louiscad

05/24/2019, 8:02 PM
Many Android developers are used to xml, but xml is not designed to be human readable. It adds restrictions like escaping some characters, not handling line breaks in a natural way, and adds verbosity like the tags and the attribute quotes. Take JSON (with quotes) and Kotlin DSL (no quotes). What is more readable? You already know the answer.
👎 2
a

Adam Powell

05/24/2019, 8:04 PM
So you'd prefer if aapt would accept json string resource files assuming nothing else changed?
It gets a little weirder when you add in some of the other inline elements that sometimes show up in translations
l

louiscad

05/24/2019, 8:06 PM
So yeah, xml isn't too bad, but I don't want to say it (nor hear/read it) because when we say something isn't too bad, it means we accept settling for repeated "little" pain (let's not mention programming languages 😅 ). Of course it depends on context, but for a page as "blank" as Compose and the Kotlin multiplatform ecosystem, we are not required to choose something that we know does not meet the standard experience we hope for.
l

Leland Richardson [G]

05/24/2019, 8:06 PM
i built a translation system at airbnb that i thought worked pretty well. Basically, you would have a special function, like
t
and it would accept the default string translation, and some context string. ie,
Button(text=t("Sign Up!", "The sign up button on the main screen"))
. We then had a compiler plugin that would go find all instances of these, and as part of our CI process would communicate with our translation service with any new strings and put them in the queue to get translated. You could then have
t(...)
return the translated string at runtime. The compiler can do the translations at build time, too, but we wanted to be able to push updated translations over the air, so the runtime conversion worked better.
3
this was good for some of the specific use cases of airbnb though, and not sure it’s a good general system
a

Adam Powell

05/24/2019, 8:06 PM
I guess in the absence of a super strong opinion here I'd be happy to use something else off the shelf, whether that's the existing format or something else
@Leland Richardson [G] I'm super skeptical of the typo-resistance of anything string keyed like that
4
l

Leland Richardson [G]

05/24/2019, 8:07 PM
can you explain? not sure i understand
a

Adam Powell

05/24/2019, 8:09 PM
I think it assumes a lot of things we would consider good practice but that can't be taken for granted, like the CI pipeline you described
Maybe not though?
r

Ryan Mentley

05/24/2019, 8:09 PM
My biggest question about that would be how you handle string reuse across multiple locations
f

Fudge

05/24/2019, 8:09 PM
In some other place you would have
"Sign Up" to "הירשם"
which is prone to typos
l

Leland Richardson [G]

05/24/2019, 8:10 PM
The string reuse was built into the system
a

Adam Powell

05/24/2019, 8:10 PM
@Fudge yeah. If you assume a lot of tooling around the pipeline you can make it work
l

Leland Richardson [G]

05/24/2019, 8:10 PM
technically if you had two “Sign Up!” strings with different context strings, it would be two different “keys”
r

Ryan Mentley

05/24/2019, 8:10 PM
The former i18n dev in me feels like he should point out that
"The sign up button on the main screen"
is a really bad example of a context string 😛
l

Leland Richardson [G]

05/24/2019, 8:10 PM
but our backend would see that one translation already existed, and default to that, but put it in the queue for a translater to differentiate based on the context string
lol, it’s a bad example, but nowhere near the worst that i’ve seen 😉 I was just providing an example off the cuff though but yeah
r

Ryan Mentley

05/24/2019, 8:11 PM
Oh I totally agree
I've made people change lots of them in code reviews 😛
l

Leland Richardson [G]

05/24/2019, 8:12 PM
yep, same!
r

Ryan Mentley

05/24/2019, 8:12 PM
But my broader point was that generally "Sign up!" should have the same context basically everywhere it's used in the app
l

Leland Richardson [G]

05/24/2019, 8:12 PM
hmm. actually i don’t necessarily agree with that
maybe depends on the message
a

Adam Powell

05/24/2019, 8:13 PM
@louiscad part of what makes the kotlin ecosystem great is that it's full of pragmatic decisions to reuse even when there are tradeoffs involved. See: the baggage we inherited around generics, lack of value types, etc 🙂
r

Ryan Mentley

05/24/2019, 8:13 PM
with that context being something like "The text of a button for the user to create an account for the service"
l

Leland Richardson [G]

05/24/2019, 8:13 PM
but for instance, a string like “OK” might be the same translation in english in a lot of places, but a different translation for other languages depending on the context
f

Fudge

05/24/2019, 8:14 PM
Aren't inline classes essentially value types?
a

Adam Powell

05/24/2019, 8:14 PM
Only if they fit in a primitive
r

Ryan Mentley

05/24/2019, 8:14 PM
We generally tried to use context to make sure the translator understood the exact meaning we were trying to convey, so if it means the same thing throughout the app, it has the same context.
l

Leland Richardson [G]

05/24/2019, 8:15 PM
yeah - that makes sense. i could see the primary call to action button having a different translation than, for instance, a secondary smaller button though. but in this example the likelihood of reuse is high
a

Adam Powell

05/24/2019, 8:15 PM
Once you've provided that much context in place you start to lose the thread reading the surrounding code, I'd fear
At that point you'd extract to a constant and then you're off to the usual races investigating alternatives
a

alexsullivan114

05/24/2019, 8:17 PM
Hopefully this isn't too tangential, but the main problem I have with strings at the moment is the dependency on `context`/`resources` it creates. I'm not sure if that's a solvable problem, but it makes unit testing a major concern when creating a function like
t
that Leland mentioned. Using that function in places you'd like to JVM test becomes challenging. I have no idea how Compose could fix/change/work around that. Just a pain point I have.
5
i

itnoles

05/24/2019, 8:17 PM
a

Adam Powell

05/24/2019, 8:17 PM
Yeah I think working around that part of it at least is a goal, @alexsullivan114
a

alexsullivan114

05/24/2019, 8:18 PM
Really? That's....amazing. I don't fully understand how you'd work around that but I'm excited to see!
i

itnoles

05/24/2019, 8:18 PM
Flutter uses JSON for localized strings.
f

Fudge

05/24/2019, 8:19 PM
Not really
It's one way of doing it, for sure
a

Adam Powell

05/24/2019, 8:20 PM
@alexsullivan114 at a minimum a function layer that abstracts over grab current context/fetch resource and does something else host side 🙂
a

alexsullivan114

05/24/2019, 8:20 PM
I'm not sure what "host side" means but I accept the magic and am jazzed!
l

Leland Richardson [G]

05/24/2019, 8:29 PM
host side ~= “JVM” or “Desktop”
1
a

alexsullivan114

05/24/2019, 8:35 PM
Interesting. I'm not up to date on the latest in the "compose is a platform agnostic library" discussion
l

Leland Richardson [G]

05/24/2019, 8:37 PM
yeah i found it to be an odd term too first time i heard it
coming from a web dev background, it makes me think of the server, as opposed to the machine the app is being developed on
f

Fudge

05/24/2019, 8:37 PM
The approach used by IOS does look very nice, it could be something of the sort
"The button was pressed %d times".localized(20)
1
r

russhwolf

05/24/2019, 8:38 PM
Careful with that example though because now you start throwing plurals into the mix
🔝 4
😆 1
l

Leland Richardson [G]

05/24/2019, 8:38 PM
@itnoles that does look similar. ironically, we used a hand-rolled system on ios as well. don’t remember why
a

Adam Powell

05/24/2019, 8:38 PM
Yeah the terms refer to host vs. target (the Android device), it's used during Android platform builds and elsewhere
l

Leland Richardson [G]

05/24/2019, 8:39 PM
we had a special way of doing pluralization. pluralization is quite difficult
l

louiscad

05/24/2019, 8:39 PM
Not all strings need context (not that
Context
). For example "Sign up" is likely to not need it and be usable anywhere in the app. Some other strings need context though, but IMHO, to make translators lives easier, this context should have grouping, a hierarchy that the developers can define, which could have shorthand identifiers. The place where the strings/translations would be defined for a context could embed comments (easier than xml comments hopefully), to give more information about the context. The translators would read it to see where to navigate in the real app for example, or understand what a particular acronym stands for and the translations known to the developer (which maye code in English but may not be English native and have to translate an acronym from his language to English, potentially leaving misunderstandings or inaccuracies behind to the translator for additional languages). I think grouping/hierarchy would be of great help for multiscreen apps, which are plenty, and would more valuable as your app gets a deep UI.
a

Adam Powell

05/24/2019, 8:41 PM
I've seen designers get very prescriptive around things like sentence case vs title case and punctuation for different parts of a UI that might have otherwise the same string; there's a ton of metadata to consider
1
r

Ryan Mentley

05/24/2019, 8:41 PM
A surprising amount of translation tooling just ignores pluralization, which makes it borderline useless if you're translating into any significant number of languages.
4
l

louiscad

05/24/2019, 8:42 PM
Yeah, pluralization, fun stuff:
4 kids are playing 2 games.
,
1 kid is playing one game.
,
Four kids are playing 1 game.
.
@Adam Powell That "ton of metadata", beyond things like pluralization, to me, is just comments. That's something that xml currently doesn't give us in an industry standard way as I doubt many translating companies suggest to use them, and tools like translation editor in Android Studio don't support it, and the IDE doesn't show those orphan comments that would make sense in autocomplete when reusing a string.
a

alexsullivan114

05/24/2019, 8:46 PM
That iOS example doesn't work though, does it? I was slapped down after trying to create a
localized
extension function since whatever tool iOS developers run specifically looks for
NSLocalizedString
calls when it builds up the translations
i

itnoles

05/24/2019, 8:47 PM
@alexsullivan114
Copy code
extension String {
    var localized: String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
    }
}
?
l

Leland Richardson [G]

05/24/2019, 8:48 PM
i don’t know NSLocalizedString gathers its dataset automatically, but at airbnb it would have had that issue at well. - we required string literals for things to compile correctly, as we looked for those calls specifically. it was a little bit hacky. not sure it’s the right solution here, but if ios does a similar thing, that would be an interesting precedent.
@itnoles but so does it require you build up your Localized.strings file manually, or is that done automatically somehow?
i

itnoles

05/24/2019, 8:49 PM
yup, Localizable.strings
a

alexsullivan114

05/24/2019, 8:50 PM
I understand the extension function, and maybe my data is out of date or wrong, but I thought that wouldn't work because genstrings looks for specific uses of
NSLocalizedString
. So that extension function would result in just one localized string rather than all of the different uses of that function. Again, that could be wrong. iOS isn't my core strength. Also this is tangential to the conversation so we should probably drop it for now
f

Fudge

05/24/2019, 8:52 PM
I didn't mean that's how IOS does it, just that that's how it could look in Kotlin @alexsullivan114.
👍 1
i

itnoles

05/24/2019, 8:57 PM
With that extensions, you have to do like "Context".localized
r

Ryan Mentley

05/24/2019, 9:00 PM
Not all strings need context (not that
Context
). For example "Sign up" is likely to not need it and be usable anywhere in the app.
Disagree here, every string should have context that describes the thing in as plain and jargon-free a way as possible in order to help translators make sense of it. "Sign up" is a very idiomatic phrase in English, imagine a language that doesn't have that phrase - would "Create account" be appropriate? The translator doesn't necessarily even know if your service has accounts, maybe it's a magazine or mailing list or something. Of course, you can probably get away with a lot less context if you have translators familiar with your product.
👍 6
t

themishkun

05/24/2019, 9:26 PM
I’d would be happy to throw out all of the xml and use some proper kotlin dsl’s instead
🙂 1
s

Siyamed

05/25/2019, 12:55 AM
After reading this discussion, i think i know who should implement the localization part of compose :) very useful.
🙂 1
g

ghedeon

05/25/2019, 6:30 AM
Same with @alexsullivan114, no matter what you do, please, get rid of the
android.content.Context
. People are bending over and going great lengths just to workaround a simple case like unit testing formatted string (because of Context). https://kotlinlang.slack.com/archives/C5GB1NDGE/p1550527639020200?thread_ts=1550527639.020200&cid=C5GB1NDGE
1
g

Gil Goldzweig

05/26/2019, 5:43 AM
Why not use kotlin extension methods with sealed classes so we can use non receiver specific code for "general" strings and use extension function for context specific. It will look something similar to this (forgive me if it looks bad I'm writing from my phone) sealed class Strings { open val someString: String = "value" } //Context specific strings sealed class MainActivityStrings(mainActivity: MainActivity) : Strings { override val someString: String = "main activity value" } Or something in that area
Getting it free of
Context
is really helpful, but even when I’m using localized strings in iOS I find it really useful to use
snake_case_keys
instead of
Hardcoded untranslated string keys
Basically it makes copy changes way way way easier
and it makes spotting places where a key is being used instead of a translated value REAL easy
Language support is an obnoxiously hard problem to solve.
f

Fudge

05/26/2019, 9:53 AM
Would be interesting to have something like this:
Copy code
val `The sign up button on the main screen` = LocalizationString("Sign Up", "הירשם")

val `The login button on the main screen` = LocalizationString("Log in","היכנס")
But android doesn't support identifiers with spaces 😞
If we really wanted it we could squash it to
val Thesignupbuttononthemainscreen
in some pre-compilation step though
g

Gil Goldzweig

05/26/2019, 10:51 AM
I guess so, but I don't know how much we want to be "Multi platform positive" especially when Flutter exists
1
r

Ryan Mentley

05/28/2019, 10:21 PM
@ghedeon I'd like to understand that use-case a bit better - why do you need to delay involvement of the Context? Generally the conversion of data -> UI strings is the purview of the view layer, rather than part of the model. It's configuration-dependent, so you need to know the configuration in order to properly resolve the strings. Is this mostly an issue in testing? e.g., you're trying to do host-side unit tests without using Robolectric or something similar
g

ghedeon

05/28/2019, 10:56 PM
@Ryan Mentley "domain data --> view state" is in presentation layer, that's right. So is the ViewModel, that supposed to be unit testable and android dependencies free. But it's not easy to keep it like that. Ex: VM is feeding some dynamic strings for the view to render. It's an instant problem, because in order to format the message you need context... (I don't consider
AndroidViewModel
or Context injection because it's a hack). Same with drawables. Ex: Based on some app logic, the VM is setting different icons to the view. As long as it's an
R.drawable.ic_foo
it's ok, but applying color filter will require the context. To summarize: you want to take some decisions about the model of the view in the controller (VM) and before actual rendering.
l

Leland Richardson [G]

05/28/2019, 10:58 PM
i think what ryan is getting at is… what if it was easy to create a test context without depending on android or requiring a device / robolectric? would you feel like it is no longer a problem at that point?
ie, is it the dependency on android platform that’s the problem, or that there’s some “context” like thing that you need in order to resolve a string
r

Ryan Mentley

05/28/2019, 11:05 PM
It sounds like you're mixing your view and your viewmodel. Your VM shouldn't be setting icons, for instance - the VM provides the data that the View uses to set the icon.
I think some of the problems would be alleviated by splitting those out a bit more
and also what Leland said - I wonder if that would help with the other problems.
g

ghedeon

05/28/2019, 11:07 PM
I see. People come up with those resource wrappers mostly because of tests. Thus, I expect a "test context" to eliminate the pain point. I'm unsure about purity concerns and multiplatform considerations... Like, in ideal world, Interactors(UseCases) are supposed to be platform independent, right? What about view models?
@Ryan Mentley at any point of time I want my VM to be able to send a "snapshot", a declarative 1:1 representation of my view to render. Can I simply send a message like "Your exchange rate is 1USD = 1.12 EUR" to the TextView to render? No. Because you propose to send a set of params, and view to have some logic based on the input. Imho it's a bad practice, people feel that it's allowed and a few months later the view is full with "validation/formatting" logic.
r

Ryan Mentley

05/28/2019, 11:23 PM
I'd say by the time you've converted to a string, you've already "rendered" the data. To me, the ViewModel is anything that's agnostic of how you display the information (it contains the information itself). If I had a different UI that displayed the exchange rate differently, I wouldn't want that string; I'd want the amounts and the currencies. Thus, the View is (and should be) responsible for formatting the data.
g

ghedeon

05/28/2019, 11:32 PM
This way VM is more generic. Do we want generic viewmodels? That reminds me about MVC days, when one controller for multiple views was quite common. I might be wrong, but I don't see it often nowadays.
Anyhow, appreciate your time guys, with [G] badge. A feedback loop this short is invaluable.
👍 1
All I wanted is to share a small friction (justified or not) from my experience with this particular aspect of android resources.
r

Ryan Mentley

05/28/2019, 11:40 PM
Thanks for the input! It's helpful to get perspectives on what's tricky for people currently to help shape things for the future.
l

Leland Richardson [G]

05/28/2019, 11:47 PM
Thus, the View is (and should be) responsible for formatting the data.
Still though, if the formatting we are talking about is the “total price” line of a page that generates 100% of your business’s revenue, you want to be able to test it.
r

Ryan Mentley

05/28/2019, 11:56 PM
Definitely, but at that point you're testing the UI
Today, you could test that either with a standard espresso-style UI test, or you could write a unit test at the UI layer that tests the method that formats it (maybe even parameterized across multiple languages?) with something like Robolectric
l

Leland Richardson [G]

05/29/2019, 1:53 AM
yeah i guess i’m saying that i don’t think that it’s unreasonable to unit test view models, and include “formatting” in the set of things that a viewmodel is responsible for. i’m actually not sure if i think the framework view layer should be responsible for text formatting at all. or at least if it should be, it’s not clear to me why
(and to be clear, i’m not sure there is “one right answer” for this… just expressing what feels right or natural to me)
r

Ryan Mentley

05/29/2019, 2:35 AM
Also to be clear I'm definitely not suggesting that view models shouldn't be unit tested
(I agree on the lack of "one right answer" to the exact divisions here...you can easily ask 4 good engineers and get 5 different answers)
4 Views