Skip to content

Commit

Permalink
Merge pull request #10 from CoreWillSoft/feature/localize_support
Browse files Browse the repository at this point in the history
Add localize CI import/export support
  • Loading branch information
dirong authored Jul 24, 2023
2 parents a4b7b9f + 459cf30 commit 0eb9318
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 0 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/export-en-localization-on-sprint-push.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: export 'en' localization on sprint push

on:
push:
branches:
- main

jobs:
build:
if: false # remove when ready
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: upload en localisation
run: curl --fail -H "Authorization:Loco ${{ secrets.LOCALISE_KEY }}" --data @app/src/main/res/values/strings.xml https://localise.biz/api/import/xml\?locale\=en\&format\=android\&ignore-existing\=true\&flag-new\=Incomplete
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: import translation and test

on:
workflow_dispatch:
inputs:
import_translation:
description: 'Import translation and test'

jobs:
build:
if: false # remove when ready
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: set up JDK 11
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: 11.0.16+1
cache: 'gradle'
- name: get all translations
run: curl --header "Authorization:Loco ${{ secrets.LOCALISE_KEY }}" https://localise.biz/api/export/archive/xml.zip\?format\=android\&status\=translated -o ./app/src/main/res/xml.zip
- name: apply translations
run: |
unzip -o ./app/src/main/res/xml.zip
rm ./app/src/main/res/xml.zip
cp -rf ####ARCHIVE_NAME####/res app/src/main/
rm -rf ####ARCHIVE_NAME####
- name: init build tools
run: ln -s $ANDROID_HOME/build-tools/33.0.2/d8 $ANDROID_HOME/build-tools/33.0.2/dx; ln -s $ANDROID_HOME/build-tools/33.0.2/lib/d8.jar $ANDROID_HOME/build-tools/33.0.2/lib/dx.jar;
- name: run localization tests
run: ./gradlew :app:testRelease --tests io.template.LocalizationTest
- name: Android Test Report
uses: asadmansr/android-test-report-action@v1.2.0
if: ${{ always() }}
- name: commit files
run: |
git status
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add -A
git commit -m "Update translations"
- name: push changes
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
126 changes: 126 additions & 0 deletions app/src/testUnit/kotlin/io/template/LocalizationTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package io.template

import android.content.res.Resources
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
import kotlin.test.assertEquals
import io.template.app.R

@RunWith(RobolectricTestRunner::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class LocalizationTest {

companion object {
private val PLURAL_QUANTITIES = listOf(0, 1, 2, 3, 5, 11, 21, 42, 102)
private val PLATFORM_PATTERN = Regex("%s|%d|%\\d[$]s|%\\d[$]d")
private val PHRASE_PATTERN = Regex("[{].*?[}]")
private val SKIP_KEYS = listOf("abc_shareactionprovider_share_with_application")

private var platformPatternMap: Map<String, List<String>> = mapOf()
private var phrasePatternMap: Map<String, List<String>> = mapOf()
}

private val resources: Resources
get() = RuntimeEnvironment.getApplication().resources

@Test
@Config(sdk = [28])
fun initPatterns() {
platformPatternMap = getAllStringsAndPlurals()
.associate { (key, string) -> key to PLATFORM_PATTERN.findAll(string).map { it.value }.toList() }
.filter { (key) -> !SKIP_KEYS.contains(key) }
.filter { (_, list) -> list.isNotEmpty() }
println("platformPatternMap.size - ${platformPatternMap.size}")

phrasePatternMap = getAllStringsAndPlurals()
.associate { (key, string) -> key to PHRASE_PATTERN.findAll(string).map { it.value }.toList() }
.filter { (key) -> !SKIP_KEYS.contains(key) }
.filter { (_, list) -> list.isNotEmpty() }
println("phrasePatternMap.size - ${phrasePatternMap.size}")
}

private fun getAllStringsAndPlurals(): List<Pair<String, String>> {
val strings = R.string::class.java.fields.map { it.name to resources.getString(it.getInt(null)) }
val quantities = R.plurals::class.java.fields.map { it.name to resources.getQuantityString(it.getInt(null), 1) }
return strings + quantities
}

@Test
@Config(sdk = [28], qualifiers = "en")
fun testEnglish() = test()

@Test
@Config(sdk = [28], qualifiers = "de")
fun testGerman() = test()


private fun test() {
testStrings()
testPlurals()
}

private fun testStrings() {
R.string::class.java.fields.forEach { field ->
val key = field.name
val resourceValue = field.getInt(null)
val value = resources.getString(resourceValue)

platformPatternMap[key].let { expected ->
if (expected.isNullOrEmpty()) return@let

val actual = PLATFORM_PATTERN.findAll(value).map { it.value }.toMutableList()

assertEquals(expected.sorted(), actual.sorted(), "Format arguments are not equal for string with key - $key.")
assertDoesNotThrow("Creating formatted string should be possible with key - $key.") {
resources.getString(
/* id = */ resourceValue,
/* ...formatArgs = */ *expected.map { if (it.contains('s')) "test" else 1 }.toTypedArray()
)
}
}

phrasePatternMap[key]?.onEach { expectedKey ->
assert(value.contains(expectedKey)) { "Key - $key, value - $value, should contain $expectedKey" }
}
}
}

private fun testPlurals() {
R.plurals::class.java.fields.forEach { field ->
PLURAL_QUANTITIES.onEach { quantity ->
val key = field.name
val resourceValue = field.getInt(null)
val value = assertDoesNotThrow("Failed to crate plural with key - $key and quantity - $quantity") {
resources.getQuantityString(resourceValue, quantity)
}
assert(value.isNotBlank()) { "Value for key - $key and quantity - $quantity should no be blank" }

platformPatternMap[key].let { expected ->
if (expected.isNullOrEmpty()) return@let

val actual = PLATFORM_PATTERN.findAll(value).map { it.value }.toMutableList()

assertEquals(expected.sorted(), actual.sorted(), "Format arguments are not equal for plural with key - $key " +
"and quantity - $quantity.")
assertDoesNotThrow("Creating formatted plural should be possible with key - $key and quantity - $quantity.") {
resources.getQuantityString(
/* id = */ resourceValue,
/* quantity = */ quantity,
/* ...formatArgs = */ *expected.map { if (it.contains('s')) "test" else 1 }.toTypedArray()
)
}
}

phrasePatternMap[key]?.onEach { phraseKey ->
assert(value.contains(phraseKey)) { "Key - $key, value - $value, should contain $phraseKey" }
}
}
}
}
}

0 comments on commit 0eb9318

Please sign in to comment.