Language Tree and Translations Def, scala & sbt version update.

This commit is contained in:
A.C.Sukazyo Eyre 2024-04-19 14:14:09 +08:00
parent 7e3588c221
commit 2cbc75a2ca
Signed by: Eyre_S
GPG Key ID: C17CE40291207874
14 changed files with 421 additions and 7 deletions

View File

@ -3,7 +3,7 @@ aether.AetherKeys.aetherOldVersionMethod := true
ThisBuild / organization := "cc.sukazyo" ThisBuild / organization := "cc.sukazyo"
ThisBuild / organizationName := "A.C. Sukazyo Eyre" ThisBuild / organizationName := "A.C. Sukazyo Eyre"
ThisBuild / scalaVersion := "3.4.0-RC4" ThisBuild / scalaVersion := "3.4.1"
resolvers ++= Seq( resolvers ++= Seq(
"-ws-releases" at "https://mvn.sukazyo.cc/releases" "-ws-releases" at "https://mvn.sukazyo.cc/releases"

View File

@ -1 +1 @@
sbt.version=1.9.4 sbt.version=1.9.9

View File

@ -0,0 +1,30 @@
en, 2 000 000 000
en_us, 328 000 000
en_gb, 66 300 000
en_sg, 5 000 000
en_mt, 1
en_ph, 100 000 000
en_nz, 5 000 000
en_za, 5 000 000
en_au, 25 000 000
en_ie, 5 000 000
en_ca, 25 000 000
en_in, 125 000 000
zh, 1 000 000 000
zh_hans, 1 400 000 000
zh_cn, 1 300 000 000
zh_sg, 5 000 000
zh_mo, 2 000 000
zh_hant, 50 000 000
zh_tw, 23 000 000
zh_hk, 7 000 000
zh_ancient, 10
ja, 125 000 000
ja_jp, 10 000 000
fr, 338 000 000
fr_fr, 67 000 000
fr_ch, 8 000 000
fr_be, 11 000 000
fr_ca, 7 000 000
fr_lu, 1
mt_mt, 520 000

View File

@ -0,0 +1,53 @@
Morny Translations File
%1.0
&encoding=utf8
&indent=1
morny.command.info.message.about
| <b>Morny Cono</b>
| An assistant rat(micromys minutus) from Annie。
| ————————————————
| {about_links}
morny.command.info.version
| version:
| - Morny <code>{version_codename}</code>
| - <code>{version_base}</code>{version_delta_html}{version_git_suffix}
| coeur md5_hash:
| - <code>{md5}</code>
| coding timestamp:
| - <code>{time_millis}</code>
| - <code>{time_utc} [UTC]</code>
morny.command.info.message.runtime
| system:
| - <code>{hostname}</code>
| - <code>{os.name}</code> <code>{os.arch}</code> <code>{os.version}</code>
| java runtime:
| - <code>{java.vm.vendor}.{java.vm.name}</code>
| - <code>{java.vm.version}</code>
| vm memory:
| - <code>{memory_used_mb}</code> / <code>{memory_available_mb}</code> MB
| - <code>{cpu_cores}</code> cores
| coeur version:
| - {version_full}
| - <code>{coeur_md5}</code>
| - <code>{compile_time_utc} [UTC]</code>
| - [<code>{compile_time_millis}</code>]
| continuous:
| - <code>{running_duration}</code>
| - [<code>{running_duration_ms}</code>]
| - <code>{startup_time_utc}</code>
| - [<code>{startup_time_millis}</code>]
morny.command.info.message.tasks
| <b>Coeur Task Scheduler:</b>
| - <i>scheduled tasks</i>: <code>{coeur.tasks.amount}</code>
| - <i>scheduler status</i>: <code>{coeur.tasks.state}</code>
| - <i>current runner status</i>: <code>{coeur.tasks.runnerState}</code>
morny.command.info.message.event
| <b>Event Statistics :</b>
| in today
| {event_statistics}""".stripMargin

View File

@ -0,0 +1,11 @@
Morny Translations File
%1.0
&encoding=utf8
&indent=1
morny.command.info.about
| <b>Morny Cono</b>
| 来自安妮的侍从小鼠。
| ————————————————
| {about_links}

View File

@ -1,6 +1,6 @@
package cc.sukazyo.cono.morny.core package cc.sukazyo.cono.morny.core
import cc.sukazyo.cono.morny.core.MornyAssets import cc.sukazyo.cono.morny.data.MornyAssets
import java.io.IOException import java.io.IOException

View File

@ -397,7 +397,7 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
break(Some(LoginResult(account, remote.username, remote.id))) break(Some(LoginResult(account, remote.username, remote.id)))
} catch } catch
case r: boundary.Break[Option[LoginResult]] => throw r case r: boundary.Break[Option[LoginResult]] => throw r
case e => case e: Throwable =>
logger `error` logger `error`
s"""${e.toLogString} s"""${e.toLogString}
|login failed""" |login failed"""

View File

@ -1,4 +1,4 @@
package cc.sukazyo.cono.morny.core package cc.sukazyo.cono.morny.data
import cc.sukazyo.restools.ResourcesPackage import cc.sukazyo.restools.ResourcesPackage

View File

@ -1,7 +1,6 @@
package cc.sukazyo.cono.morny.data package cc.sukazyo.cono.morny.data
import cc.sukazyo.cono.morny.core.MornyAssets import cc.sukazyo.cono.morny.data.MornyAssets.AssetsException
import cc.sukazyo.cono.morny.core.MornyAssets.AssetsException
import java.io.IOException import java.io.IOException
import scala.language.postfixOps import scala.language.postfixOps

View File

@ -0,0 +1,14 @@
package cc.sukazyo.cono.morny.util.hytrans
//opaque type Definitions = Map[String, String]
class Definitions (
innerData: Map[String, String]
) extends Map[String, String] {
def iterator: Iterator[(String, String)] = innerData.iterator
def removed (key: String): Map[String, String] = innerData.removed(key)
def updated[V1 >: String] (key: String, value: V1): Map[String, V1] = innerData.updated(key, value)
def get (key: String): Option[String] = innerData.get(key)
}

View File

@ -0,0 +1,33 @@
package cc.sukazyo.cono.morny.util.hytrans
case class LangTag (
lang: String,
priority: Long
) extends Comparable[LangTag] {
override def compareTo(o: LangTag): Int =
this.priority `compareTo` o.priority
}
object LangTag {
class IllegalLangTagException (message: String, val original: String)
extends IllegalArgumentException(message)
def normalizeLangTag(tag: String): String =
tag.replaceAll("-", "_").toLowerCase
@throws[IllegalLangTagException]
def ensureLangTag (tag: String): String =
tag.foreach {
case c if c.isLetter =>
case '_' =>
case ' ' | '\t' | '\r' | '\n' =>
throw IllegalLangTagException("Lang Tag cannot contains space", tag)
case ill =>
throw IllegalLangTagException(s"Illegal character '$ill' in Lang Tag \"$tag\"", tag)
}
tag
}

View File

@ -0,0 +1,188 @@
package cc.sukazyo.cono.morny.util.hytrans
import cc.sukazyo.cono.morny.util.hytrans.LangTag.IllegalLangTagException
import java.io.{PrintWriter, StringWriter}
import scala.collection.mutable
import scala.util.boundary
class LanguageTree {
class Node (val langTag: LangTag) extends Comparable[Node] {
private object LangTagOrdering extends Ordering[Node]:
override def compare (x: Node, y: Node): Int = y.langTag `compareTo` x.langTag
override def compareTo (o: Node): Int = this.langTag `compareTo` o.langTag
private type ChildCol = mutable.SortedSet[Node]
private var _parent: Option[Node] = None
private val _children: ChildCol = mutable.SortedSet.empty(using LangTagOrdering)
def parent: Option[Node] = _parent
def children: ChildCol = _children
def traverseParent (f: Node => Unit): Unit =
_parent.foreach { parent =>
f(parent)
parent.traverseParent(f)
}
private def traversingTree (f: Node => Unit)(using visited: mutable.Set[Node]): Unit =
_children.foreach { child =>
if !(visited contains child) then
child.traversingTree(f)
}
if !(visited contains this) then
f(this)
visited += this
this.parent.foreach { parent =>
if !(visited contains parent) then
parent.traversingTree(f)
}
def traverseTree (f: Node=>Unit): Unit =
val visited = mutable.Set.empty[Node]
f(this)
visited += this
traversingTree(f)(using visited)
@throws[IllegalArgumentException]
private infix def setParent (parent: Node): Unit =
this._parent = Some(parent)
parent.traverseParent { p =>
if p == parent then
throw new IllegalArgumentException(
s"failed set parent ${parent.langTag.lang} to ${langTag.lang}: " +
s"Cannot set parent to a child of itself"
)
this._parent = None
}
def detachParent (): Unit =
this._parent.foreach(_.removeChild(this))
def removeChild (child: Node): Unit =
child._parent = None
_children -= child
@throws[IllegalArgumentException]
infix def addChild (child: Node): Unit =
val child_old_parent = child._parent
if child._parent.nonEmpty then
child.detachParent()
try child setParent this
catch case e: IllegalArgumentException =>
child_old_parent.foreach(_ addChild child)
throw e
this._children += child
@throws[IllegalArgumentException]
infix def addChild (child: LangTag): Node =
val node = new Node(child)
addChild(node)
node
private def printTree (node: Node, prefix: String = "", printer: PrintWriter): Unit = {
printer.println(s"$prefix${node.langTag}")
node.children.foreach { child =>
printTree(child, prefix + " ", printer)
}
}
def printTree: String =
val s = StringWriter()
printTree(this, "", PrintWriter(s))
s.toString
override def toString: String =
printTree
}
object Node:
def defaultRoot: Node = Node(LangTag("root", 0))
val root: Node = Node.defaultRoot
def search (langTag: String): Option[Node] =
val _langTag = LangTag.normalizeLangTag(langTag)
boundary {
root.traverseTree { node =>
if node.langTag.lang == _langTag then
boundary.break(Some(node))
}
None
}
}
object LanguageTree {
@throws[IllegalArgumentException]
def parseTreeDocument (document: String): LanguageTree = {
val lines = document.replaceAll("\\r", "").split('\n')
val tree = new LanguageTree
val root = tree.root
import tree.Node
var currentLevel = mutable.ListBuffer[Node](root)
def countHeadingWhitespaceLevel (line: String)(using whitespaceSize: Int): Option[(Int, String)] =
val whitespace = line.takeWhile(_.isWhitespace)
if whitespace.length % whitespaceSize == 0 then
Some(whitespace.length / whitespaceSize -> line.drop(whitespace.length))
else None
for (i <- lines.indices) {
val line = lines(i)
val line_number = i+1
countHeadingWhitespaceLevel(line)(using 2) match
case Some((level, content)) =>
val langTag: LangTag = content.split(",", 2) match
case Array(lang) =>
try LangTag(LangTag.ensureLangTag(LangTag.normalizeLangTag(lang)), 0)
catch case e: IllegalLangTagException =>
throw new IllegalArgumentException(
s"illegal lang name at line $line_number: ${e.getMessage}"
).initCause(e)
case Array(lang, priority) =>
try LangTag(
LangTag.ensureLangTag(LangTag.normalizeLangTag(lang)),
priority.filterNot(List(' ', ',', '-', '_', '\'').contains(_)).toInt
)
catch
case e: NumberFormatException =>
throw new IllegalArgumentException(
s"failed parse lang's priority at line $line_number: ${e.getMessage}"
).initCause(e)
case e: IllegalLangTagException =>
throw new IllegalArgumentException(
s"illegal lang name at line $line_number: ${e.getMessage}"
).initCause(e)
case _ =>
throw new IllegalArgumentException(
s"failed parse at line $line_number: line with invalid format."
)
val node = Node(langTag)
if level < (currentLevel.length + 1) then
currentLevel = currentLevel take (level + 1)
currentLevel.last addChild node
currentLevel += node
else if level == (currentLevel.length + 1) then
currentLevel.last addChild node
currentLevel += node
else
throw new IllegalArgumentException(
s"failed parse at line $line_number: line with invalid indentation."
)
case None =>
throw new IllegalArgumentException(
s"failed parse at line $line_number: line with invalid indentation."
)
}
tree
}
}

View File

@ -0,0 +1,41 @@
package cc.sukazyo.cono.morny.util.hytrans
object Parser {
def parse (document: String): Definitions = {
val lines = document.replaceAll("\\r", "").split('\n')
val keyValues = collection.mutable.Map.empty[String, String]
var keyDef: String | Null = null
def newValue = StringBuilder()
var valueDef: StringBuilder = newValue
def addLine (line: String) =
valueDef ++= line += '\n'
//noinspection TypeAnnotation
def saveThis() =
if keyDef != null then
keyValues += (keyDef -> valueDef.toString.stripSuffix("\n"))
keyDef = null
valueDef = newValue
lines.foreach { line =>
line.headOption match {
case Some(' ') | Some('\t') | None => // empty lines, will be ignored
case Some('#') => // comment line, will be ignored
case Some('%') => // document meta definition line, currently not supported
case Some('|') => // content line
addLine(line drop 2)
case Some(_) => // a key definition line
saveThis()
keyDef = line
}
}
saveThis()
Definitions(keyValues.toMap)
}
}

View File

@ -0,0 +1,45 @@
package cc.sukazyo.cono.morny.util.hytrans
import cc.sukazyo.cono.morny.util.var_text.{Var, VarText}
import scala.util.boundary
class Translations (
langs: LanguageTree,
translations: Map[String, Definitions]
) {
def traverse (using lang: String)(f: (LangTag, Definitions) => Unit): Unit = {
langs.search(lang)
.getOrElse(langs.root)
.traverseTree(node =>
translations.get(node.langTag.lang)
.map(
f(node.langTag, _)
)
)
}
def traverseWithKey (key: String)(using lang: String)(f: (LangTag, Option[String]) => Unit): Unit = {
this.traverse (using lang) { (langTag, definitions) =>
f(langTag, definitions.get(key))
}
}
def get (key: String)(using lang: String): Option[String] = {
boundary {
traverseWithKey(key) { (_, value) =>
if value.nonEmpty then
boundary.break(Some(value.get))
}
None
}
}
def trans (key: String)(using lang: String): VarText =
VarText(get(key).getOrElse(s"#[$key@$lang]"))
def trans (key: String, args: Var*)(using lang: String): String =
trans(key).render(args*)
}