-
-
Notifications
You must be signed in to change notification settings - Fork 341
/
UpdatePresetsTask.kt
118 lines (102 loc) · 4.7 KB
/
UpdatePresetsTask.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import com.beust.klaxon.JsonArray
import com.beust.klaxon.JsonObject
import com.beust.klaxon.Parser
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
import java.io.File
import java.io.StringWriter
import java.net.URL
import java.util.Locale
/** Update the presets metadata and its translations for use with the de.westnordost:osmfeatures
* library */
open class UpdatePresetsTask : DefaultTask() {
@get:Input var languageCodes: Collection<String>? = null
@get:Input var targetDir: String? = null
@get:Input var version: String? = null
@TaskAction fun run() {
val targetDir = targetDir ?: return
/* eagerly also fetch different variants of a language (e.g. "en-NZ" also when just "en"
is specified as well as "sr" if just "sr-Cyrl" is specified). Hence, we only look at the
language code */
val exportLanguages = languageCodes?.map { Locale(Locale.forLanguageTag(it).language) }
val version = version ?: return
// copy the presets.json 1:1
val presetsFile = File("$targetDir/presets.json")
presetsFile.writeText(fetchPresets(version))
// download each language
val localizationMetadataList = fetchLocalizationMetadata()
for (localizationMetadata in localizationMetadataList) {
val locale = localizationMetadata.locale
val languageLocale = Locale(locale.language)
if (exportLanguages != null && !exportLanguages.any { it == languageLocale }) continue
val javaLanguageTag = locale.toLanguageTag()
println(javaLanguageTag)
val presetsLocalization = fetchPresetsLocalizations(localizationMetadata)
File("$targetDir/$javaLanguageTag.json").writeText(presetsLocalization)
}
// Norway has two languages, one of them is called Bokmål
// coded "no" in iD presets, but "nb" is also expected by Android.
// https://github.com/streetcomplete/StreetComplete/issues/3890
if ("no" in languageCodes.orEmpty()) {
val bokmalFile = File("$targetDir/no.json")
bokmalFile.copyTo(File("$targetDir/nb.json"), overwrite = true)
}
}
/** Fetch iD presets */
private fun fetchPresets(version: String): String {
val presetsUrl = "https://raw.githubusercontent.com/openstreetmap/id-tagging-schema/$version/dist/presets.json"
return URL(presetsUrl).readText()
}
/** Fetch relevant meta-infos for localizations from iD */
private fun fetchLocalizationMetadata(): List<LocalizationMetadata> {
// this file contains a list with meta information for each localization of iD
val contentsUrl = "https://api.github.com/repos/openstreetmap/id-tagging-schema/contents/dist/translations"
val languagesJson = Parser.default().parse(URL(contentsUrl).openStream()) as JsonArray<JsonObject>
return languagesJson.mapNotNull {
if (it["type"] != "file") return@mapNotNull null
val name = it["name"] as String
if (name.endsWith(".min.json")) return@mapNotNull null
val filename = name.subSequence(0, name.indexOf(".")).toString()
val locale = Locale.forLanguageTag(filename.replace('@', '-'))
LocalizationMetadata(locale, it["download_url"] as String)
}
}
/** Download and pick the localization for only the presets from iD localizations
* (the iD localizations contain everything, such as localizations of iD UI etc)*/
private fun fetchPresetsLocalizations(localization: LocalizationMetadata): String =
URL(localization.downloadUrl).openStream().bufferedReader().use { it.readText() }.unescapeUnicode()
}
private data class LocalizationMetadata(val locale: Locale, val downloadUrl: String)
private fun String.unescapeUnicode(): String {
val out = StringWriter(length)
val unicode = StringBuilder(4)
var hadSlash = false
var inUnicode = false
for (ch in this) {
if (inUnicode) {
unicode.append(ch)
if (unicode.length == 4) {
val unicodeChar = unicode.toString().toInt(16).toChar()
out.write(unicodeChar.toString())
unicode.setLength(0)
inUnicode = false
hadSlash = false
}
} else if (hadSlash) {
hadSlash = false
if (ch == 'u') {
inUnicode = true
} else {
out.write(92)
out.write(ch.toString())
}
} else if (ch == '\\') {
hadSlash = true
} else {
out.write(ch.toString())
}
}
if (hadSlash) out.write(92)
return out.toString()
}