Skip to content

Commit

Permalink
Plurals on Android - done
Browse files Browse the repository at this point in the history
  • Loading branch information
amatsegor committed Sep 20, 2018
1 parent 17e2228 commit a4f7516
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 38 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#Changelog

##1.2
* Added support for plurals on Android
* Fixed bug with strings lower-casing

##1.1
* Fixed issue when .NET converter overwrites whole existing file, even if a `AUTO-GENERATED` tag is present
* Kotlin version up to 1.2.70
Expand Down
21 changes: 20 additions & 1 deletion docs/GOOGLE-SHEET-FORMAT.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,23 @@ _______________________________________
----------------------------------------
</pre>

To see a real example of a spreadsheet - [click here](https://docs.google.com/spreadsheets/d/1rVIuMUuuJcZNLmAnCRosxOqiZJ-jtRqBz2rkDXvFG8w/edit?usp=sharing)
* Also, on Android (iOS - soon) you can use a [plural strings](https://developer.android.com/guide/topics/resources/string-resource#Plurals) format.
In this case, you should declare localizations in such format:
<pre>
______________________________________________
| KEY | EN | DE |
______________________________________________
first_plural.plural | | | // plural id with "plural" suffix
----------------------------------------------
zero | ZeroStEN | ZeroStDE |
----------------------------------------------
one | OneStrEN | OneStrDE |
----------------------------------------------
two | TwoStrEN | TwoStrDE |
----------------------------------------------
<b>!</b>first_plural.plural | | | // same id, but with leading exclamation
----------------------------------------------
</pre>
Supported quantifiers (value keys) are: `zero`,`one`,`two`,`few`,`many`,`other`

###To see a real example of a spreadsheet - [click here](https://docs.google.com/spreadsheets/d/1rVIuMUuuJcZNLmAnCRosxOqiZJ-jtRqBz2rkDXvFG8w/edit?usp=sharing)
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package lokalize.extractor

import lokalize.models.LSArray
import lokalize.models.LSEntity
import lokalize.models.LSLine
import lokalize.models.Worksheet
import lokalize.models.*

class DefaultExtractor : Extractor {

private var currentPlural = LSPlural("")
private var isInPlural = false

private var currentArray = LSArray("")
private var isInArray = false

Expand All @@ -27,19 +27,47 @@ class DefaultExtractor : Extractor {
val keyValue = row[keyCol] ?: ""
val valValue = row[valCol] ?: ""

when {
isInArray -> addValueToArray(keyValue, valValue, results)
val shouldOpenArray = keyValue.matches(arrayStartRegex)
val shouldCloseArray = keyValue.matches(arrayEndRegex)

keyValue.matches(arrayEndRegex) -> throw IllegalStateException("Array closing tag found, but reader is not in array now")
val shouldOpenPlural = keyValue.matches(pluralStartRegex)
val shouldClosePlural = keyValue.matches(pluralEndRegex)

keyValue.matches(arrayStartRegex) -> {
val arrayName = keyValue.substringAfter("[").substringBeforeLast("]")
when {
isInArray -> {
if (shouldOpenArray) throw IllegalStateException("Array ${currentArray.key} isn't closed")

if (shouldCloseArray) {
closeArray(results)
} else {
currentArray += LSLine(keyValue, valValue)
}
}

if (arrayName.isBlank()) throw IllegalArgumentException("Array name cannot be empty")
shouldCloseArray -> throw IllegalStateException("Array closing tag found, but reader is not in array now")
shouldOpenArray -> {
if (isInPlural) throw IllegalStateException("Array opening tag found while plural ${currentPlural.key} isn't closed")
createArray(keyValue)
}

currentArray = LSArray(arrayName)
isInPlural -> {
if (shouldOpenPlural) throw IllegalStateException("Plural ${currentPlural.key} isn't closed")

if (shouldClosePlural) {
closePlural(results)
} else {
if (LSPlural.Quantifier.isQuantifierValid(keyValue)) {
currentPlural += LSLine(keyValue, valValue)
} else {
throw IllegalArgumentException("Invalid plural quantifier: $keyValue")
}
}
}

isInArray = true
shouldClosePlural -> throw IllegalStateException("Plural closing tag found, but reader is not in plural now")
shouldOpenPlural -> {
if (isInArray) throw IllegalStateException("Plural opening tag found while array ${currentArray.key} isn't closed")
createPlural(keyValue)
}

keyValue.isNotBlank() -> results.add(LSLine(keyValue, valValue))
Expand All @@ -49,19 +77,41 @@ class DefaultExtractor : Extractor {
return results
}

private fun addValueToArray(key: String, value: String, target: MutableList<LSEntity>) {
if (key.matches(arrayStartRegex)) throw IllegalStateException("Array ${currentArray.key} isn't closed")
private fun createArray(key: String) {
val arrayName = key.substringAfter("[").substringBeforeLast("]")

if (key.matches(arrayEndRegex)) {
target.add(currentArray)
isInArray = false
} else {
currentArray += LSLine(key, value)
}
if (arrayName.isBlank()) throw IllegalArgumentException("Array name cannot be empty")

currentArray = LSArray(arrayName)

isInArray = true
}

private fun closeArray(target: MutableList<LSEntity>) {
target.add(currentArray)
isInArray = false
}

private fun createPlural(key: String) {
val arrayName = key.substringBeforeLast(".")

if (arrayName.isBlank()) throw IllegalArgumentException("Plural name cannot be empty")

currentPlural = LSPlural(arrayName)

isInPlural = true
}

private fun closePlural(target: MutableList<LSEntity>) {
target.add(currentPlural)
isInPlural = false
}

companion object {
private val arrayStartRegex = Regex("\\[(\\w+[\\w\\-_])?]")
private val arrayEndRegex = Regex("\\[/(\\w+[\\w\\-_])+]")

private val pluralStartRegex = Regex("\\w+.plural")
private val pluralEndRegex = Regex("!\\w+.plural")
}
}
26 changes: 25 additions & 1 deletion lokalize-common/src/main/kotlin/lokalize/models/LSPlural.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
package lokalize.models

class LSPlural(key: String, private val values: Map<String, String> = hashMapOf()): LSEntity(key) {
class LSPlural(key: String, val values: MutableMap<String, String> = hashMapOf()) : LSEntity(key) {

override val isEmpty: Boolean
get() = values.isEmpty()

override val isComment: Boolean
get() = false

operator fun plus(line: LSLine): LSPlural {
values[line.key] = line.value
return this
}

enum class Quantifier {
ZERO,
ONE,
TWO,
FEW,
MANY,
OTHER;

companion object {
fun isQuantifierValid(quantifier: String) = try {
Quantifier.valueOf(quantifier.toUpperCase())
true
} catch (e: IllegalArgumentException) {
false
}
}

}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package lokalize.transformer

import lokalize.models.LSArray
import lokalize.models.LSEntity
import lokalize.models.LSLine
import lokalize.models.Options
import lokalize.models.*

abstract class AbstractTransformer {

open fun transformArray(array: LSArray): String = ""

open fun transformPlural(plural: LSPlural): String = ""

abstract fun transformComment(comment: String): String

abstract fun transformKeyValue(key: String, value: String, closing: Boolean): String
Expand All @@ -26,19 +26,31 @@ abstract class AbstractTransformer {

val isClosing = index == nonEmptyEntities.size - 1

if (entity is LSLine) {
if (entity.isComment) {
valueToInsert.append(transformComment(entity.value))
} else {
valueToInsert.append(transformKeyValue(entity.key, entity.value, isClosing))
when (entity) {
is LSLine -> {
if (entity.isComment) {
valueToInsert.append(transformComment(entity.value))
} else {
valueToInsert.append(transformKeyValue(entity.key, entity.value, isClosing))
}
}
is LSArray -> {
val transformedArray = transformArray(entity)

if (transformedArray.isBlank()) {
skipped = true
} else {
valueToInsert.append(transformedArray)
}
}
} else if (entity is LSArray) {
val transformedArray = transformArray(entity)
is LSPlural -> {
val transformedPlural = transformPlural(entity)

if (transformedArray.isBlank()) {
skipped = true
} else {
valueToInsert.append(transformedArray)
if (transformedPlural.isBlank()) {
skipped = true
} else {
valueToInsert.append(transformedPlural)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package lokalize.transformer

import lokalize.models.LSArray
import lokalize.models.LSPlural
import lokalize.models.Options

class AndroidTransformer : AbstractTransformer() {
Expand All @@ -15,6 +16,16 @@ class AndroidTransformer : AbstractTransformer() {
return builder.append("</string-array>").toString()
}

override fun transformPlural(plural: LSPlural): String {
val builder = StringBuilder("<plurals name=\"${plural.key}\">\n").apply {
plural.values.forEach {
append("\t<item quantity=\"${it.key}\">${it.value}</item>\n")
}
append("</plurals>")
}
return builder.toString()
}

override fun transformComment(comment: String) = "<!-- $comment -->"

override fun transformKeyValue(key: String, value: String, closing: Boolean) = "<string name=\"$key\">${normalize(value)}</string>"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ package lokalize.transformer
import lokalize.models.Options

class IOSTransformer : AbstractTransformer() {

//TODO: implement iOS plurals in a right way
/*override fun transformPlural(plural: LSPlural): String {
val builder = StringBuilder()
plural.values.forEach {
builder.append("${plural.key}.${it.key} = \"${it.value}\";\n")
}
return builder.toString()
}*/

override fun transformComment(comment: String): String = "// $comment"

override fun transformKeyValue(key: String, value: String, closing: Boolean): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ actual class GoogleSpreadsheet actual constructor(key: String) {
values.drop(1).forEachIndexed { _, list ->
val row = arrayListOf<Pair<String, String>>()
list.forEachIndexed { index, value ->
val pair = keyRow[index].toLowerCase() to (value as? String ?: "").toLowerCase()
val pair = keyRow[index].toLowerCase() to (value as? String ?: "")
row.add(pair)
}
rows.add(row)
Expand Down

0 comments on commit a4f7516

Please sign in to comment.