2021-08-04 05:04:03 +00:00
|
|
|
import io.ktor.client.HttpClient
|
|
|
|
import io.ktor.client.request.get
|
2022-04-27 08:39:21 +00:00
|
|
|
import io.ktor.client.statement.bodyAsText
|
2021-08-04 05:04:03 +00:00
|
|
|
import kotlinx.serialization.SerialName
|
|
|
|
import kotlinx.serialization.Serializable
|
|
|
|
import kotlinx.serialization.builtins.ListSerializer
|
|
|
|
import kotlinx.serialization.json.Json
|
|
|
|
import java.io.File
|
2021-08-08 16:09:33 +00:00
|
|
|
import java.text.Normalizer
|
2021-08-04 05:04:03 +00:00
|
|
|
|
|
|
|
private val json = Json {
|
|
|
|
ignoreUnknownKeys = true
|
|
|
|
}
|
|
|
|
|
2023-12-21 09:41:11 +00:00
|
|
|
private const val baseClassName = "IetfLang"
|
|
|
|
private const val oldBaseClassName = "IetfLanguageCode"
|
2021-08-04 05:04:03 +00:00
|
|
|
private const val unknownBaseClassName = "Unknown$baseClassName"
|
2023-12-21 09:41:11 +00:00
|
|
|
private const val oldUnknownBaseClassName = "Unknown$oldBaseClassName"
|
|
|
|
private const val baseClassSerializerName = "${baseClassName}Serializer"
|
|
|
|
private const val oldBaseClassSerializerName = "IetfLanguageCodeSerializer"
|
2021-08-04 05:04:03 +00:00
|
|
|
private const val baseClassSerializerAnnotationName = "@Serializable(${baseClassSerializerName}::class)"
|
|
|
|
|
|
|
|
@Serializable
|
|
|
|
private data class LanguageCode(
|
|
|
|
@SerialName("English")
|
2024-07-25 12:27:06 +00:00
|
|
|
val title: String,
|
|
|
|
@SerialName("alpha2")
|
|
|
|
val alpha: String? = null,
|
|
|
|
@SerialName("alpha3-b")
|
|
|
|
val alpha2: String? = null,
|
|
|
|
@SerialName("alpha3-t")
|
|
|
|
val alpha3: String? = null,
|
|
|
|
) {
|
|
|
|
val tag: String
|
|
|
|
get() = alpha ?: alpha2 ?: alpha3!!
|
|
|
|
}
|
2021-08-04 05:04:03 +00:00
|
|
|
|
2024-07-25 12:27:06 +00:00
|
|
|
fun String.adaptAsTitle() = (if (first().isDigit()) {
|
2021-08-04 05:04:03 +00:00
|
|
|
"L$this"
|
|
|
|
} else {
|
|
|
|
this
|
2024-07-25 12:27:06 +00:00
|
|
|
}).replace(".", "_").replace("'", "_")
|
2021-08-04 05:04:03 +00:00
|
|
|
|
2021-08-08 16:09:33 +00:00
|
|
|
fun String.normalized() = Normalizer.normalize(this, Normalizer.Form.NFD).replace(Regex("[^\\p{ASCII}]"), "")
|
|
|
|
|
2021-08-04 05:04:03 +00:00
|
|
|
@Serializable
|
|
|
|
private data class LanguageCodeWithTag(
|
|
|
|
@SerialName("langType")
|
|
|
|
val tag: String,
|
|
|
|
@SerialName("lang")
|
|
|
|
val withSubtag: String
|
|
|
|
) {
|
|
|
|
val partWithoutTag: String
|
|
|
|
get() {
|
|
|
|
return withSubtag.substring(
|
|
|
|
withSubtag.indexOf("-") + 1, withSubtag.length
|
|
|
|
)
|
|
|
|
}
|
|
|
|
val middleTag
|
|
|
|
get() = if (partWithoutTag.contains("-")) {
|
|
|
|
partWithoutTag.substring(0, partWithoutTag.indexOf("-"))
|
|
|
|
} else {
|
|
|
|
null
|
|
|
|
}
|
|
|
|
val middleTagTitle
|
|
|
|
get() = middleTag ?.adaptAsTitle() ?: partWithoutTag.adaptAsTitle()
|
|
|
|
val subtag: String
|
|
|
|
get() = middleTag ?: partWithoutTag
|
|
|
|
val endTag
|
|
|
|
get() = if (partWithoutTag.contains("-")) {
|
|
|
|
partWithoutTag.substring(partWithoutTag.indexOf("-") + 1, partWithoutTag.length)
|
|
|
|
} else {
|
|
|
|
null
|
|
|
|
}
|
|
|
|
val endTagAsTitle
|
|
|
|
get() = endTag ?.adaptAsTitle()
|
|
|
|
}
|
|
|
|
|
|
|
|
data class Tag(
|
|
|
|
val title: String,
|
|
|
|
val tag: String,
|
|
|
|
val subtags: List<Tag>
|
2024-07-25 12:27:06 +00:00
|
|
|
) {
|
|
|
|
val adaptedTitle
|
|
|
|
get() = title.adaptAsTitle()
|
|
|
|
}
|
2021-08-04 05:04:03 +00:00
|
|
|
|
|
|
|
private fun printLanguageCodeAndTags(
|
|
|
|
tag: Tag,
|
|
|
|
parent: Tag? = null,
|
|
|
|
indents: String = " "
|
|
|
|
): String = if (tag.subtags.isEmpty()) {
|
2021-08-04 05:11:43 +00:00
|
|
|
"""${indents}${baseClassSerializerAnnotationName}
|
2024-07-25 12:27:06 +00:00
|
|
|
${indents}object ${tag.adaptedTitle} : ${parent ?.adaptedTitle ?: baseClassName} { override val code: String = "${tag.tag}"${parent ?.let { parent -> "; override val parentLang: ${parent.adaptedTitle} get() = ${parent.adaptedTitle}" } ?: ""}; override fun toString() = code }"""
|
2021-08-04 05:04:03 +00:00
|
|
|
} else {
|
|
|
|
"""
|
|
|
|
${indents}${baseClassSerializerAnnotationName}
|
2024-07-25 12:27:06 +00:00
|
|
|
${indents}sealed interface ${tag.adaptedTitle} : ${parent ?.adaptedTitle ?: baseClassName} {
|
2021-08-04 05:04:03 +00:00
|
|
|
|
|
|
|
${tag.subtags.joinToString("\n") { printLanguageCodeAndTags(it, tag, "${indents} ") }}
|
|
|
|
|
|
|
|
${indents} ${baseClassSerializerAnnotationName}
|
2024-07-25 12:27:06 +00:00
|
|
|
${indents} companion object : ${tag.adaptedTitle} {
|
|
|
|
${indents} override val code: String = "${tag.tag}"${parent ?.let { parent -> "\n${indents} override val parentLang: ${parent.adaptedTitle} get() = ${parent.adaptedTitle};" } ?: ""}
|
|
|
|
${indents} override fun toString() = code
|
|
|
|
${indents} }
|
2021-08-04 05:04:03 +00:00
|
|
|
${indents}}
|
|
|
|
"""
|
|
|
|
}
|
|
|
|
|
2023-12-21 09:41:11 +00:00
|
|
|
fun buildKtFileContent(tags: List<Tag>, prePackage: String): String = """
|
2021-08-04 05:04:03 +00:00
|
|
|
import kotlinx.serialization.Serializable
|
|
|
|
|
2021-08-04 05:14:56 +00:00
|
|
|
/**
|
|
|
|
* This class has been automatically generated using
|
|
|
|
* https://github.com/InsanusMokrassar/MicroUtils/tree/master/language_codes/generator . This generator uses
|
|
|
|
* https://datahub.io/core/language-codes/ files (base and tags) and create the whole hierarchy using it.
|
|
|
|
*/
|
2021-08-04 05:04:03 +00:00
|
|
|
${baseClassSerializerAnnotationName}
|
2024-07-25 12:27:06 +00:00
|
|
|
sealed interface $baseClassName {
|
|
|
|
val code: String
|
|
|
|
val parentLang: $baseClassName?
|
|
|
|
get() = code.split("-").takeIf { it.size > 1 } ?.first() ?.let(::$unknownBaseClassName)
|
|
|
|
val withoutDialect: String
|
2023-12-21 09:41:11 +00:00
|
|
|
get() = parentLang ?.code ?: code
|
2021-08-04 05:04:03 +00:00
|
|
|
|
|
|
|
${tags.joinToString("\n") { printLanguageCodeAndTags(it, indents = " ") } }
|
|
|
|
|
|
|
|
$baseClassSerializerAnnotationName
|
2024-07-25 12:27:06 +00:00
|
|
|
data class $unknownBaseClassName (override val code: String) : $baseClassName {
|
2023-12-21 09:41:11 +00:00
|
|
|
override val parentLang = code.dropLastWhile { it != '-' }.removeSuffix("-").takeIf { it.length > 0 } ?.let(::$unknownBaseClassName)
|
2022-12-02 14:03:29 +00:00
|
|
|
}
|
2023-12-21 09:41:11 +00:00
|
|
|
@Deprecated("Renamed", ReplaceWith("$baseClassName.$unknownBaseClassName", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassName.$unknownBaseClassName"))
|
2024-07-25 12:27:06 +00:00
|
|
|
val $oldUnknownBaseClassName
|
|
|
|
get() = $unknownBaseClassName
|
2021-08-04 05:04:03 +00:00
|
|
|
}
|
2023-12-21 09:41:11 +00:00
|
|
|
@Deprecated("Renamed", ReplaceWith("$baseClassName", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassName"))
|
|
|
|
typealias $oldBaseClassName = $baseClassName
|
2021-08-04 05:04:03 +00:00
|
|
|
""".trimIndent()
|
|
|
|
|
2023-12-21 09:41:11 +00:00
|
|
|
fun createStringConverterCode(tags: List<Tag>, prePackage: String): String {
|
2021-08-04 05:04:03 +00:00
|
|
|
fun createDeserializeVariantForTag(
|
|
|
|
tag: Tag,
|
|
|
|
pretitle: String = baseClassName,
|
2021-08-04 05:51:02 +00:00
|
|
|
indents: String = " "
|
2021-08-04 05:04:03 +00:00
|
|
|
): String {
|
2024-07-25 12:27:06 +00:00
|
|
|
val currentTitle = "$pretitle.${tag.adaptedTitle}"
|
2021-08-04 05:04:03 +00:00
|
|
|
return """${indents}$currentTitle.code -> $currentTitle${if (tag.subtags.isNotEmpty()) tag.subtags.joinToString("\n", "\n") { createDeserializeVariantForTag(it, currentTitle, indents) } else ""}"""
|
|
|
|
}
|
2023-12-21 09:41:11 +00:00
|
|
|
fun createInheritorVariantForTag(
|
|
|
|
tag: Tag,
|
|
|
|
pretitle: String = baseClassName,
|
|
|
|
indents: String = " "
|
|
|
|
): String {
|
2024-07-25 12:27:06 +00:00
|
|
|
val currentTitle = "$pretitle.${tag.adaptedTitle}"
|
2023-12-21 09:41:11 +00:00
|
|
|
val subtags = if (tag.subtags.isNotEmpty()) {
|
|
|
|
tag.subtags.joinToString(",\n", ",\n") { createInheritorVariantForTag(it, currentTitle, "$indents ") }
|
|
|
|
} else {
|
|
|
|
""
|
|
|
|
}
|
|
|
|
return "${indents}$currentTitle$subtags"
|
2021-08-04 05:51:02 +00:00
|
|
|
}
|
2023-12-21 09:41:11 +00:00
|
|
|
fun createInheritorVariantForMapForTag(
|
|
|
|
tag: Tag,
|
|
|
|
pretitle: String = baseClassName,
|
|
|
|
indents: String = " ",
|
|
|
|
codeSuffix: String = ""
|
|
|
|
): String {
|
2024-07-25 12:27:06 +00:00
|
|
|
val currentTitle = "$pretitle.${tag.adaptedTitle}"
|
2023-12-21 09:41:11 +00:00
|
|
|
val subtags = if (tag.subtags.isNotEmpty()) {
|
|
|
|
tag.subtags.joinToString(",\n", ",\n") { createInheritorVariantForMapForTag(it, currentTitle, "$indents ", codeSuffix) }
|
|
|
|
} else {
|
|
|
|
""
|
|
|
|
}
|
|
|
|
return "${indents}$currentTitle.code${codeSuffix} to $currentTitle$subtags"
|
|
|
|
}
|
|
|
|
|
|
|
|
return """val knownLanguageCodesMap: Map<String, $baseClassName> by lazy {
|
|
|
|
mapOf(
|
|
|
|
${tags.joinToString(",\n") { createInheritorVariantForMapForTag(it, indents = " ") }}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
val knownLanguageCodesMapByLowerCasedKeys: Map<String, $baseClassName> by lazy {
|
|
|
|
mapOf(
|
|
|
|
${tags.joinToString(",\n") { createInheritorVariantForMapForTag(it, indents = " ", codeSuffix = ".lowercase()") }}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
val knownLanguageCodes: List<$baseClassName> by lazy {
|
|
|
|
knownLanguageCodesMap.values.toList()
|
2021-08-04 05:51:02 +00:00
|
|
|
}
|
2023-12-21 09:41:11 +00:00
|
|
|
|
|
|
|
fun String.as$baseClassName(): $baseClassName {
|
|
|
|
return knownLanguageCodesMap[this] ?: $baseClassName.${unknownBaseClassName}(this)
|
|
|
|
}
|
|
|
|
fun String.as${baseClassName}CaseInsensitive(): $baseClassName {
|
|
|
|
return knownLanguageCodesMapByLowerCasedKeys[this.lowercase()] ?: $baseClassName.${unknownBaseClassName}(this)
|
|
|
|
}
|
|
|
|
@Deprecated("Renamed", ReplaceWith("this.as$baseClassName()", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}as$baseClassName"))
|
|
|
|
fun String.as$oldBaseClassName(): $baseClassName = as$baseClassName()
|
|
|
|
|
2021-08-04 05:51:02 +00:00
|
|
|
fun convertTo$baseClassName(code: String) = code.as$baseClassName()
|
2023-12-21 09:41:11 +00:00
|
|
|
fun convertTo${baseClassName}CaseInsensitive(code: String) = code.as${baseClassName}CaseInsensitive()
|
|
|
|
@Deprecated("Renamed", ReplaceWith("convertTo$baseClassName(code)", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}convertTo$baseClassName"))
|
|
|
|
fun convertTo$oldBaseClassName(code: String) = convertTo$baseClassName(code)
|
|
|
|
|
2021-08-04 05:51:02 +00:00
|
|
|
fun $baseClassName(code: String) = code.as$baseClassName()
|
2023-12-21 09:41:11 +00:00
|
|
|
fun ${baseClassName}CaseInsensitive(code: String) = code.as${baseClassName}CaseInsensitive()
|
|
|
|
@Deprecated("Renamed", ReplaceWith("$baseClassName(code)", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassName"))
|
|
|
|
fun $oldBaseClassName(code: String) = $baseClassName(code)
|
2021-08-04 05:51:02 +00:00
|
|
|
"""
|
|
|
|
}
|
|
|
|
|
2023-12-21 09:41:11 +00:00
|
|
|
fun createSerializerCode(tags: List<Tag>, prePackage: String): String {
|
2021-08-04 05:04:03 +00:00
|
|
|
return """import kotlinx.serialization.KSerializer
|
|
|
|
import kotlinx.serialization.builtins.serializer
|
|
|
|
import kotlinx.serialization.encoding.Decoder
|
|
|
|
import kotlinx.serialization.encoding.Encoder
|
|
|
|
|
|
|
|
object $baseClassSerializerName : KSerializer<$baseClassName> {
|
|
|
|
override val descriptor = String.serializer().descriptor
|
|
|
|
|
|
|
|
override fun deserialize(decoder: Decoder): $baseClassName {
|
2021-08-04 05:51:02 +00:00
|
|
|
return $baseClassName(decoder.decodeString())
|
2021-08-04 05:04:03 +00:00
|
|
|
}
|
|
|
|
|
2023-12-21 09:41:11 +00:00
|
|
|
override fun serialize(encoder: Encoder, value: $baseClassName) {
|
2021-08-04 05:04:03 +00:00
|
|
|
encoder.encodeString(value.code)
|
|
|
|
}
|
|
|
|
}
|
2023-12-21 09:41:11 +00:00
|
|
|
@Deprecated("Renamed", ReplaceWith("$baseClassSerializerName", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassSerializerName"))
|
|
|
|
typealias $oldBaseClassSerializerName = $baseClassSerializerName
|
2021-08-04 05:04:03 +00:00
|
|
|
"""
|
|
|
|
}
|
|
|
|
|
|
|
|
suspend fun main(vararg args: String) {
|
|
|
|
val outputFolder = args.firstOrNull() ?.let { File(it) }
|
|
|
|
outputFolder ?.mkdirs()
|
2023-12-21 09:41:11 +00:00
|
|
|
val targetPackage = args.getOrNull(1)
|
|
|
|
val targetPackagePrefix = targetPackage ?.let { "package $it\n\n" } ?: ""
|
2021-08-04 05:04:03 +00:00
|
|
|
val ietfLanguageCodesLink = "https://datahub.io/core/language-codes/r/language-codes.json"
|
|
|
|
val ietfLanguageCodesAdditionalTagsLink = "https://datahub.io/core/language-codes/r/ietf-language-tags.json"
|
|
|
|
|
|
|
|
val client = HttpClient()
|
|
|
|
|
|
|
|
val ietfLanguageCodes = json.decodeFromString(
|
|
|
|
ListSerializer(LanguageCode.serializer()),
|
2022-04-27 08:39:21 +00:00
|
|
|
client.get(ietfLanguageCodesLink).bodyAsText()
|
2021-08-04 05:04:03 +00:00
|
|
|
).map {
|
|
|
|
it.copy(
|
|
|
|
title = it.title
|
|
|
|
.replace(Regex("[;,()-]"), "")
|
|
|
|
.split(" ")
|
|
|
|
.joinToString("") { "${it.first().uppercase()}${it.substring(1)}" }
|
|
|
|
)
|
|
|
|
}
|
|
|
|
val ietfLanguageCodesWithTagsMap = json.decodeFromString(
|
|
|
|
ListSerializer(LanguageCodeWithTag.serializer()),
|
2022-04-27 08:39:21 +00:00
|
|
|
client.get(ietfLanguageCodesAdditionalTagsLink).bodyAsText()
|
2021-08-04 05:04:03 +00:00
|
|
|
).filter { it.withSubtag != it.tag }.groupBy { it.tag }
|
|
|
|
|
|
|
|
val tags = ietfLanguageCodes.map {
|
|
|
|
val unformattedSubtags = ietfLanguageCodesWithTagsMap[it.tag] ?: emptyList()
|
|
|
|
val threeLevelTags = unformattedSubtags.filter { it.endTag != null }.groupBy { it.middleTag }
|
|
|
|
val subtags = unformattedSubtags.mapNotNull {
|
|
|
|
if (it.endTag == null) {
|
|
|
|
val currentSubtags = (threeLevelTags[it.subtag] ?: emptyList()).map {
|
2021-08-08 16:09:33 +00:00
|
|
|
Tag(it.endTagAsTitle!!.normalized(), it.withSubtag, emptyList())
|
2021-08-04 05:04:03 +00:00
|
|
|
}
|
2021-08-08 16:09:33 +00:00
|
|
|
Tag(it.middleTagTitle.normalized(), it.withSubtag, currentSubtags)
|
2021-08-04 05:04:03 +00:00
|
|
|
} else {
|
|
|
|
null
|
|
|
|
}
|
|
|
|
}
|
2021-08-08 16:09:33 +00:00
|
|
|
Tag(it.title.normalized(), it.tag, subtags)
|
2021-08-04 05:04:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
File(outputFolder, "LanguageCodes.kt").apply {
|
|
|
|
delete()
|
|
|
|
createNewFile()
|
2024-07-25 12:27:06 +00:00
|
|
|
writeText("@file:Suppress(\"SERIALIZER_TYPE_INCOMPATIBLE\")\n\n" + targetPackagePrefix + buildKtFileContent(tags, targetPackage ?: ""))
|
2021-08-04 05:04:03 +00:00
|
|
|
}
|
|
|
|
|
2021-08-04 05:51:02 +00:00
|
|
|
File(outputFolder, "StringToLanguageCodes.kt").apply {
|
|
|
|
delete()
|
|
|
|
createNewFile()
|
2023-12-21 09:41:11 +00:00
|
|
|
writeText(targetPackagePrefix + createStringConverterCode(tags, targetPackage ?: ""))
|
2021-08-04 05:51:02 +00:00
|
|
|
}
|
|
|
|
|
2021-08-04 05:04:03 +00:00
|
|
|
File(outputFolder, "$baseClassSerializerName.kt").apply {
|
|
|
|
delete()
|
|
|
|
createNewFile()
|
2023-12-21 09:41:11 +00:00
|
|
|
writeText(targetPackagePrefix + createSerializerCode(tags, targetPackage ?: ""))
|
2021-08-04 05:04:03 +00:00
|
|
|
}
|
|
|
|
}
|