ru.pocketbyte.locolaser.LocoLaser.kt Maven / Gradle / Ivy
/*
* Copyright © 2017 Denis Shurygin. All rights reserved.
* Licensed under the Apache License, Version 2.0
*/
package ru.pocketbyte.locolaser
import ru.pocketbyte.locolaser.config.Config
import ru.pocketbyte.locolaser.config.ExtraParams
import ru.pocketbyte.locolaser.resource.entity.ResMap
import ru.pocketbyte.locolaser.resource.entity.merge
import ru.pocketbyte.locolaser.summary.Summary
import ru.pocketbyte.locolaser.summary.plus
import ru.pocketbyte.locolaser.utils.LogUtils
import ru.pocketbyte.locolaser.utils.PluralUtils
import java.io.IOException
import java.util.*
/**
* This class contain logic of localization by specified localization config.
*
* @author Denis Shurygin
*/
object LocoLaser {
/**
* Build localization files by specified config.
* @param config Localization config.
* @return Return true if localization success, false otherwise.
*/
fun localize(config: Config): Boolean {
val startTime = System.currentTimeMillis()
val result = localizeInner(config)
LogUtils.info("Total time: " + (System.currentTimeMillis() - startTime) + " millis.")
return result
}
private fun localizeInner(config: Config?): Boolean {
if (config == null)
throw IllegalArgumentException("Config must be not null")
val summary = Summary.loadSummary(config)
val isConfigFileChanged = summary?.isConfigFileChanged(config.file) ?: true
val isRefreshAll = config.isForceImport || isConfigFileChanged
if (summary != null) {
if (config.isForceImport)
LogUtils.info("Force import. All resource files will be refreshed.")
else if (isConfigFileChanged)
LogUtils.info("Config file was changed. All resource files will be refreshed.")
else if (!summary.isDelayPassed(config.delay)) {
LogUtils.info("Delay is not passed. Localization stopped.")
return true
}
}
LogUtils.info("Conflict strategy: " + config.conflictStrategy.toString())
val sourceConfig = config.source ?: throw IllegalArgumentException("Config source must be not null")
val platformConfig = config.platform ?: throw IllegalArgumentException("Config platform must be not null")
val resources = platformConfig.resources
val source = sourceConfig.resources
// Prepare resource files
var isHaveFileToTranslate = false
val resourcesToIgnore = HashSet() // set of locales
val resourcesToLocalize = HashSet() // set of locales
for (locale in config.locales) {
if (!resourcesToLocalize.contains(locale)) {
val isLocaleChanged =
isRefreshAll ||
summary == null ||
summary.isLocaleChanged(resources, source, locale)
if (isLocaleChanged) {
isHaveFileToTranslate = true
resourcesToLocalize.add(locale)
resourcesToIgnore.remove(locale)
} else if (!resourcesToLocalize.contains(locale)) { // summary != null && !isResourceFileChanged
resourcesToIgnore.add(locale)
}
}
}
for (ignoredLocale in resourcesToIgnore)
LogUtils.info("Resource file for "
+ "locale \"" + ignoredLocale + "\""
+ " have not been changed since the last import. Ignore this file.")
if (!isHaveFileToTranslate) {
LogUtils.info("Nothing to update. Localization stopped.")
// Override summary
if (summary != null && config.file != null) {
summary.save()
}
} else {
// =================================
// Read source values
val sourceResMap = source.read(config.locales, ExtraParams())
if (sourceResMap == null && config.conflictStrategy !== Config.ConflictStrategy.EXPORT_NEW_PLATFORM) {
LogUtils.info("Source is empty. Localization stopped.")
return true
}
// Remove ignored resources
for (locale in resourcesToIgnore) {
sourceResMap?.remove(locale)
}
var mergedResMap: ResMap? = null
// =================================
// Export local resources if needed
if (config.conflictStrategy !== Config.ConflictStrategy.REMOVE_PLATFORM) {
// Read local files
val localResMap = resources.read(resourcesToLocalize, ExtraParams())
when (config.conflictStrategy) {
Config.ConflictStrategy.KEEP_NEW_PLATFORM,
Config.ConflictStrategy.EXPORT_NEW_PLATFORM -> {
mergedResMap = localResMap.merge(sourceResMap)
}
Config.ConflictStrategy.KEEP_PLATFORM,
Config.ConflictStrategy.EXPORT_PLATFORM -> {
mergedResMap = sourceResMap.merge(localResMap)
}
else -> { /* Do nothing */ }
}
if (config.trimUnsupportedQuantities) {
mergedResMap?.trimUnsupportedQuantities()
}
if (mergedResMap != null && config.conflictStrategy.isExportStrategy) {
source.write(mergedResMap, null)
}
} else {
mergedResMap = sourceResMap
if (config.trimUnsupportedQuantities) {
mergedResMap?.trimUnsupportedQuantities()
}
}
// =================================
// Write resource files
if (mergedResMap != null) {
try {
resources.write(mergedResMap, config.extraParams)
} catch (e: IOException) {
LogUtils.err(e)
return false
}
for ((locale) in mergedResMap) {
// entry.key = locale, entry.value = strings of the locale
if (resourcesToLocalize.contains(locale)) {
// Getting new summary
(resources.summaryForLocale(locale) + source.summaryForLocale(locale))?.let {
summary?.setResourceSummary(locale, it)
}
}
}
}
// =================================
// Save summary into file.
if (summary != null && config.file != null) {
summary.setConfigSummary(config)
summary.save()
}
}
return true
}
private fun ResMap.trimUnsupportedQuantities() {
forEach { (locale, localeMap) ->
PluralUtils.quantitiesForLocale(locale)?.let { supportedQuantities ->
localeMap.forEach { _, resItem ->
for (i in resItem.values.size - 1 downTo 0) {
val value = resItem.values[i]
if (!supportedQuantities.contains(value.quantity)) {
resItem.removeValue(value)
}
}
}
}
}
}
}