-
Notifications
You must be signed in to change notification settings - Fork 1
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
String substitution from variables #11
Comments
I like the idea, but I also see one problem. Not all platforms support strings substitution, and no one from current list of supported platforms. It can lead to mixed results depends on set of used resources. For example, I can implement this feature for Google Sheets, so names of parameters will be provided by Google Sheets. But then, if you would to generate Kotlin classes offline, you can face with different result if your local files doesn't support parameter names. Only one solution that I see is to use parameter names like: |
Another one solution is to generate extension functions for platforms that supports this functionality. |
There is no need of platfrom support. There is no need to do something wih sheets variables. The only thing we need is to replace something that looks like variable with provided arguments. Let's say you have a string in sheets: strategy.price.dropped={symbol} Price was dropped from {from.price} to {to.price} then you have generated a function: fun strategyPriceDropped(symbol: String, fromPrice: String, toPrice: String): String =
"{symbol} Price was dropped from {from.price} to {to.price}".localize(symbol, fromPrice, toPrice) Function which do the job by replacing all fun String.localize(vararg args: Any = arrayOf<String>()): String {
val str = this
var result = ""
var skipping = false
var index = -1
for (char in str) {
if (char == '{') {
index++
if (index >= args.size) throw IllegalArgumentException("Not enough arguments to format a string: $str")
skipping = true
result += args[index].toString()
}
if (!skipping) result += char
if (char == '}') skipping = false
}
if (index != args.size - 1) throw IllegalArgumentException("Too many arguments to format a string: $str")
if (skipping) throw IllegalArgumentException("Expected '}' while parsing string: $str")
return result
} Is this demo code described the idea better? |
Of course an actual implementation on the library's side should be more complex. Added a way to specify a custom locale: fun justFun() {
val resultWithDefaultLocale = strategyPriceDropped("XBTUSD", "10000", "9999")
val resultWithCustomLocale = strategyPriceDropped("XBTUSD", "10000", "9999", "ru")
}
fun strategyPriceDropped(symbol: String, fromPrice: String, toPrice: String, locale: String = ""): String =
localize("strategy.price.dropped", locale, symbol, fromPrice, toPrice)
private fun localize(key: String, locale: String, vararg args: Any = arrayOf<String>()): String {
val str = localizedStringByKey(key, locale)
var result = ""
var skipping = false
var index = -1
for (char in str) {
if (char == '{') {
index++
if (index >= args.size) throw IllegalArgumentException("Not enough arguments to format a string: $str")
skipping = true
result += args[index].toString()
}
if (!skipping) result += char
if (char == '}') skipping = false
}
if (index != args.size - 1) throw IllegalArgumentException("Too many arguments to format a string: $str")
if (skipping) throw IllegalArgumentException("Expected '}' while parsing string: $str")
return result
}
fun localizedStringByKey(key: String, locale: String): String =
if (locale.isEmpty()) {
// Finds value by key using device's locale (or locale that was previously setup by user) inside static map
} else {
// Finds value by key inside static map with provided locale
} |
@KamiSempai hello. Could you please clarify the status of the issue? I mean will you make the feature possible someday when you'll have a time? Because it's reeeealy useful from my point of view. |
For now I working on migration to Kotlin Poet to make code generation simplest. Then I will start implementing this feature. |
Probably this should help. The version of library 1.5.0 But I'm not yet have implement parsing of string resources that you provide. |
I have an app with client-server architecture. Client part (Android + common code) uses LocoLaser, server part uses my code for localizing property files (server.properties, server_ru.properties, etc) like I already wrote in this issue. Properties files contain strings with placeholders and I read these files, parse them, replacing placeholders. Some parts of actual code I wrote in this issue too. Based on mpp example (development branch) and instruction from your link I made some changes to my code:
added inside docs file And I expected that
Also I expected that old strings that have Instead this is how it looks like in values/string.xml:
And in
And old strings with "%s" were look like:
Am I doing something wrong? Or
it means that the
Also, in previous version as well as in the new version there is a problem with UPPERCASE and lowercase strings. For example, if you have a strings with key "Something" and "something" in common StringRepository there will be only one key with lowercase letters but in values/strings.xml there will be both keys. So something wrong with kotlin code generation. P.s. implementation looks really cool right now |
Google sheets use Java formatting. So you could use strings like "Something that makes me %s". Of Course it will erase parameter name, but for now Google Sheets doesn't allow to provide parameters names (I need to think how to bring it into Google Sheets, without breaking old functionality). One more note about your example. Don't translate formatting keys. In your case for
Android implementation of StringProvider works only with Android resources. For backend you should implement your own implementation of StringProvider. Also I would notice that you don't need to access stringProvider property cause it not a part of common interface. To make code multiplatform you should get strings only thru the common interface.
There is some restriction for the keys. The key should be in underscore case format and lowercase. If it does, the tool automatically convert it in the suitable format. I strongly recommend you don't use same strings with different character case. Key should look like a key. Also it's better to add some context into a key. |
What does it mean actually? I just wrote a string with double curly brackets (
It was only one place in the whole codebase where I needed to specify one minute as
But where I should get real values for keys from StringRepository interface? AndroidStringRepository uses values from values/strings.xml but AbsKeyValueStringRepository lacks this opportunity. Let's say I have a Google Docs sheet and in result I will get only interface with a keys, right? No values like in AndroidStringRepository. What if you will make a in-built static files (like |
@avently Please check if Properties Resources is suits for you. |
Hello, Denis. I'm unable to check everything you did because I don't have a PC near me. Maybe after a month or so. Does the new implementation allow to use google docs for kotlin multiplatform development? |
@avently Is the Issue still relevant? Can I close it? |
@KamiSempai as far as I understand you added properties as a source of data. But how can I use google sheets with variables as a source? There are no examples of usage of properties module so can't be sure that I really understand what can be achieved with it. After I switched to Locolaser I'm so happy to use Google Sheets. How properties module can help with variables? Do they have the ability to use variables at all? If yes, will Locolaser generate functions with parameters named as a variable? I mean something like this: In google sheet I have something like:
I expect to get the following function generated:
Will I get this result? If yes, the issue can be closed. If not, let's see what we can receive in the output from this example? P.s. I still use Locolaser from early 2020 just because it does the right job. I apply it to client part of the app because I want to rewrite the whole server part into Nim. And in there I will use Locolaser two (I need Google Sheets:) ). |
Since version 2.1.0 LocoLaser allow to set formatting type for Google Sheets. See here https://github.com/PocketByte/LocoLaser/blob/master/resource-googlesheet/README.md#config If you do set So if you will use resources from such Google Sheet, LocoLaser will able to generate Repository containing methods like You only need to implement
|
@KamiSempai ideal! This is what I need, thank you, Denis! Issue should be definitely closed now. |
@KamiSempai looks like I found something unexpected. I'm trying to make a json localized files in order to use them as a source for StringProvider for custom platform. The problem is that I can't specify JSON Object in config instead of "json". Take a look at example: This one doesn't work with error: {
"platform": [
{
"type" : "json",
"res_dir": "./build/generated/locolaser/json/"
}
],
"source" : {
"type": "googlesheet",
....
}
} This one works ok, but files are placed in top-level directory which I would like to avoid: {
"platform": [
"json"
],
"source" : {
"type": "googlesheet",
....
}
} Did I do something wrong? |
@avently This is an issue and it's already fixed in development branch. But it's not ready yet to be released. |
I see debug info in a console. If it intentional then everything is fine. I see json files generated in separate directories per lang. I don't use other options except files location so can't say about them, |
Hello.
I found this library very useful and two improvements may be made for better usability.
I hope we can find a solutiion for them:
i18n.t()
in generated kotlin files) or (better because of perfomance and easy to implement a parser without external libs) in properties files.Let's focus here on a first issue from the list.
As an example of what I mean by string substitution take a look at the following example:
my src/commonMain/resources/com.me.appname/server.properties file that I use in a common project (other locales have another name like server_ru.properties):
Here we can see:
=
there is a description for this property that can contain variables in{}
brackets. In these brackets you see a readable variable name because it's much easier to understand what I should enter instead of that variable. Also the variable can contain.
dot inside it's name because it's useful too in some situations.In code I read the file line by line (actually a value of any property can have multiple lines ended with
\
just like terminals may do in shell scripts) but I don't think it's important for Locolaser since all properties can be written in one line.This is how I use the properties in code:
The String extension
l()
gets a property (asthis
inside the method), replaces all placeholders aka variables with the values provided in theargs
array, and returns the result.So, what's next. I think LocoLaser can support named arguments inside generated kotlin files, so it can work like this:
strategy.price.dropped
with value{symbol} Price was dropped from {from.price} to {to.price}
from remote doc fileThis way I can be sure that I provided all arguments for a translation and a correct order of arguments without looking into translation files.
What do you think about this proposal?
The text was updated successfully, but these errors were encountered: