Skip to content

Commit

Permalink
Added Translations Code & Tests!
Browse files Browse the repository at this point in the history
* Added Translation.kt
* Added TranslationManager.kt
* Added TranslationManagerTest.kt
* Updated build.gradle
* TranslationManager is initialized on module enable!
  • Loading branch information
Im-Fran committed Jan 1, 2022
1 parent 1e0d7d4 commit 3af986b
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 1 deletion.
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ dependencies {
compileOnly 'net.md-5:bungeecord-api:1.18-R0.1-SNAPSHOT'
compileOnly 'xyz.theprogramsrc:simplecoreapi:0.1.9-SNAPSHOT'
compileOnly 'xyz.theprogramsrc:filesmodule:0.1.0-SNAPSHOT'
compileOnly 'xyz.theprogramsrc:loggingmodule:0.2.0-SNAPSHOT'

testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2'
}
Expand Down
10 changes: 10 additions & 0 deletions src/main/kotlin/xyz/theprogramsrc/translationsmodule/Main.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package xyz.theprogramsrc.translationsmodule

import xyz.theprogramsrc.simplecoreapi.global.module.Module

class Main: Module() {

override fun onEnable() {
TranslationManager()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package xyz.theprogramsrc.translationsmodule

import xyz.theprogramsrc.filesmodule.config.YmlConfig
import xyz.theprogramsrc.filesmodule.utils.folder
import xyz.theprogramsrc.translationsmodule.objects.Translation
import java.io.File

/**
* Representation of the translation manager.
*/
class TranslationManager {

private val translationsCache = mutableMapOf<String, MutableList<Translation>>()
private val cache = mutableMapOf<String, MutableMap<String, MutableMap<String, String>>>()

companion object {
private val translationSettings = YmlConfig(File(File("plugins/SimpleCoreAPI").folder(), "TranslationSettings.yml")).add("language", "en")
lateinit var instance: TranslationManager

fun getCurrentLanguage(): String = translationSettings.getStringOrSet("language", "en")
}

init {
instance = this
loadTranslations()
}

/**
* Register the given translations to the given group id
* @param group The group (folder) of the translation. Defaults to "common"
* @param translations The translations to register
*/
fun registerTranslations(group: String = "common", translations: Collection<Translation>) {
val translationsCache = this.translationsCache[group] ?: mutableListOf()
translations.forEach { t ->
if(translationsCache.find { it == t } == null) {
translationsCache.add(t)
}
}
this.translationsCache[group] = translationsCache
loadTranslations()
}

/**
* Get the translation for the given id in the given language
* @param id The id of the translation
* @param language The language of the translation. Set to null to use the default language. Defaults to null
* @param group The group (folder) of the translation. Defaults to "common"
* @param placeholders The placeholders to replace in the translation. Defaults to empty map
* @return The translation. If the translation is not found, the id is returned
*/
fun translate(id: String, language: String? = null, group: String = "common", placeholders: Map<String, String> = emptyMap()): String {
val cached = cache[group] ?: return id
var translation = ((cached[language] ?: cached[getCurrentLanguage()] ?: return id)[id] ?: return id)
placeholders.forEach { (key, value) ->
translation = translation.replace("{$key}", value).replace("%$key%", value)
}
return translation
}

/**
* Loads the translations to the cache.
*/
fun loadTranslations() {
val translationsFolder = File("translations/").folder()
// First load the default translations
translationsCache.forEach { (group, translations) ->
val folder = File(translationsFolder, group).folder()
translations.forEach {
val languageFile = YmlConfig(File(folder, "${it.language}.lang"))
languageFile.add(it.id, it.defaultValue)
}
}

// Then load the translations from the language files
(translationsFolder.listFiles() ?: emptyArray()).filter(File::isDirectory).forEach { groupFolder ->
val cached = cache[groupFolder.name] ?: mutableMapOf()
(groupFolder.listFiles() ?: emptyArray()).filter { it.extension == "lang" }.forEach {
val langCache = cached[it.nameWithoutExtension] ?: mutableMapOf()
val cfg = YmlConfig(it)
cfg.keys(true).forEach { id ->
val t = translationsCache[groupFolder.name]?.find { t1 -> t1.id == id }
if(t != null){
langCache[id] = t.translate(groupFolder.name, it.nameWithoutExtension)
}
}
if(langCache.isNotEmpty()){
cached[it.nameWithoutExtension] = langCache
}
}

if(cached.isNotEmpty()) {
cache[groupFolder.name] = cached
}
}
}

/**
* Count the amount of translations
* @param group The group (folder) of the translation. If null it'll count all the groups. Defaults to null
* @param lang The language of the translation. If null it'll count all the languages. Defaults to null
*/
fun countTranslations(group: String? = null, lang: String? = null): Int {
return if(group == null){
cache.values.sumOf {
if(lang != null){
it[lang]?.size ?: 0
}else {
it.values.sumOf { it2 ->
it2.values.size
}
}
}
} else {
val c = (this.cache[group] ?: return -2)
if(lang != null){
c[lang]?.size ?: 0
}else {
c.values.sumOf { it2 ->
it2.values.size
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package xyz.theprogramsrc.translationsmodule.objects

import xyz.theprogramsrc.filesmodule.config.YmlConfig
import xyz.theprogramsrc.filesmodule.utils.folder
import xyz.theprogramsrc.translationsmodule.TranslationManager
import java.io.File

/**
* Representation of a translation.
* @param id The id of the translation
* @param defaultValue The default value of the translation.
* @param language The language of the translation. (Defaults to "en")
* @param colors The colors to use in the translation replacing strings. Example (using color '&c'): '**test**' should return '&ctest'. Defaults to empty array.
* @param placeholders The placeholders to use in the translation replacing strings. Example (using placeholder id 'test' and value 'test_value'): '{test}' should return 'test_value'.
* You can use '{}' or '%%' as placeholder identifiers like '{test}' or '%test%'. Defaults to empty map.
*/
data class Translation(val id: String, val defaultValue: String, val language: String = "en", val colors: Array<String> = emptyArray()) {

/**
* Translates this [Translation] to the current language.
* @param group The group (folder) of the translation. Defaults to "common"
* @param language The language of the translation. Set to null to use the default language. Defaults to null
* @return The translated string.
*/
fun translate(group: String = "common", language: String? = null): String {
val file = YmlConfig(File(File("translations/${if(group.endsWith("/")) group else "$group/"}").folder(), (language ?: TranslationManager.getCurrentLanguage()) + ".lang"))
var translation = if(file.has(id)) file.getString(id) else defaultValue
for(i in colors.indices) {
try {
val color = colors[i]
val string = Regex("\\*\\*(.+?)\\*\\*").findAll(translation).first().groupValues[1]
translation = translation.replaceFirst("**$string**", "$color$string")
}catch (_: Exception){}
}

return translation
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as Translation

if (id != other.id) return false
if (defaultValue != other.defaultValue) return false
if (!colors.contentEquals(other.colors)) return false

return true
}

override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + defaultValue.hashCode()
result = 31 * result + colors.contentHashCode()
return result
}
}
7 changes: 7 additions & 0 deletions src/main/resources/module.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
main=xyz.theprogramsrc.translationsmodule.Main
name=TranslationsModule
description=@description@
version=@version@
author=TheProgramSrc
repository-id=translationsmodule
dependencies=FilesModule
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package xyz.theprogramsrc.translationsmodule

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import xyz.theprogramsrc.translationsmodule.objects.Translation

internal class TranslationManagerTest {
private val tm = TranslationManager()
private val translations = listOf(
Translation(
id = "id",
defaultValue = "My **colorful** translation!",
language = "en",
colors = arrayOf("&a"),
),
Translation(
id = "id2",
defaultValue = "My **{text}** translation!",
language = "en",
colors = arrayOf("&a"),
),
Translation(
id = "id3",
defaultValue = "My **{text}** translation!",
language = "en",
),
)

@Test
fun countTest(){
tm.registerTranslations(translations = translations)
assertEquals(3, tm.countTranslations())
}

@Test
fun translationId1Test(){
tm.registerTranslations(translations = translations)
assertEquals("My &acolorful translation!", tm.translate("id", "en"))
}

@Test
fun translationId2Test() {
tm.registerTranslations(translations = translations)
assertEquals("My &acolorful translation!", tm.translate("id2", "en", placeholders = mapOf("text" to "colorful")))
}

@Test
fun translationId3Test() {
tm.registerTranslations(translations = translations)
assertEquals("My **colorful** translation!", tm.translate("id3", "en", placeholders = mapOf("text" to "colorful")))
}
}

0 comments on commit 3af986b

Please sign in to comment.