diff --git a/project/MornyConfiguration.scala b/project/MornyConfiguration.scala index dee41cb..d08d67a 100644 --- a/project/MornyConfiguration.scala +++ b/project/MornyConfiguration.scala @@ -8,7 +8,7 @@ object MornyConfiguration { val MORNY_CODE_STORE = "https://github.com/Eyre-S/Coeur-Morny-Cono" val MORNY_COMMIT_PATH = "https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s" - val VERSION = "2.0.0-alpha18" + val VERSION = "2.0.0-alpha19" val VERSION_DELTA: Option[String] = None val CODENAME = "xinzheng" diff --git a/src/main/resources/assets_morny/langs/en_us.hyt b/src/main/resources/assets_morny/langs/en_us.hyt index 3382989..a0d7e75 100644 --- a/src/main/resources/assets_morny/langs/en_us.hyt +++ b/src/main/resources/assets_morny/langs/en_us.hyt @@ -4,11 +4,26 @@ &encoding=utf8 &indent=1 +morny.information.about_links +| source code | backup +| Feedback / issue tracker +| User's Guide / documentations + morny.command.info.message.about | Morny Cono | An assistant rat(micromys minutus) from Annie。 | ———————————————— -| {about_links} +| {morny.information.about_links} + +morny.command.info.sub_start.message +| Welcome you meet the Morny Conoan assistant rat from Annie。 +| Morny has a lot variety of features。 +| +| ———————————————— +| {morny.information.about_links} +| ———————————————— +| +| (You can use /info command to get these message in any time) morny.command.info.version | version: @@ -50,4 +65,4 @@ morny.command.info.message.tasks morny.command.info.message.event | Event Statistics : | in today -| {event_statistics}""".stripMargin +| {event_statistics} diff --git a/src/main/resources/assets_morny/langs/zh_cn.hyt b/src/main/resources/assets_morny/langs/zh_cn.hyt index 6a08f79..a546422 100644 --- a/src/main/resources/assets_morny/langs/zh_cn.hyt +++ b/src/main/resources/assets_morny/langs/zh_cn.hyt @@ -4,8 +4,40 @@ &encoding=utf8 &indent=1 -morny.command.info.about +morny.information.about_links +| source code | backup +| 反馈 / issue tracker +| 使用说明书 / user guide & docs + +morny.command.info.message.about | Morny Cono | 来自安妮的侍从小鼠。 | ———————————————— -| {about_links} +| {morny.information.about_links} + +morny.command.info.sub_start.message +| 欢迎使用 Morny Cono来自安妮的侍从小鼠。 +| Morny 具有各种各样的功能。 +| +| ———————————————— +| {morny.information.about_links} +| ———————————————— +| +| (你可以随时通过 /info 重新获得这些信息) + +morny.misc.command_test.message +| 这只是一个 /test 测试命令。测试 id1。 +| 你可以回复一条消息来测试回复消息的功能。 + +morny.misc.command_test.branch_normal.message +| 你完成了一个回复消息的测试! +| 你所回复的消息为: +| +| {replied_message} + +morny.misc.command_test.branch_oops.err_message_simple +| 将会生成一条错误! (使用 /inspect 命令来洞察错误详细信息) + +morny.misc.command_test.branch_oops.err_message_complex +| Oops: There is just a test error. +| 这个错误是由 /test 命令手动触发的,为了测试错误洞察命令是否可以正常工作。 diff --git a/src/main/resources/assets_morny/langs/zh_tw.hyt b/src/main/resources/assets_morny/langs/zh_tw.hyt new file mode 100644 index 0000000..4437d8e --- /dev/null +++ b/src/main/resources/assets_morny/langs/zh_tw.hyt @@ -0,0 +1,16 @@ + Morny Translations File + +%1.0 +&encoding=utf8 +&indent=1 + +morny.information.about_links +| source code | backup +| 回饋 / issue tracker +| 使用說明書 / user guide & docs + +morny.command.info.message.about +| Morny Cono +| 來自安妮的侍從小鼠。 +| ———————————————— +| {morny.information.about_links} diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala b/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala index a0deba0..f0655ed 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala @@ -2,7 +2,7 @@ package cc.sukazyo.cono.morny.core import cc.sukazyo.cono.morny.core.Log.logger import cc.sukazyo.cono.morny.core.MornyCoeur.* -import cc.sukazyo.cono.morny.core.bot.api.{EventListenerManager, MornyCommandManager, MornyQueryManager} +import cc.sukazyo.cono.morny.core.bot.api.{BotExtension, EventListenerManager, MornyCommandManager, MornyQueryManager} import cc.sukazyo.cono.morny.core.bot.api.messages.ThreadingManager import cc.sukazyo.cono.morny.core.bot.event.{MornyOnInlineQuery, MornyOnTelegramCommand, MornyOnUpdateTimestampOffsetLock} import cc.sukazyo.cono.morny.core.bot.internal.{ErrorMessageManager, ThreadingManagerImpl} @@ -15,6 +15,7 @@ import cc.sukazyo.cono.morny.util.time.WatchDog import cc.sukazyo.cono.morny.util.GivenContext import cc.sukazyo.cono.morny.util.UseString.MString import cc.sukazyo.cono.morny.util.UseThrowable.toLogString +import cc.sukazyo.cono.morny.util.hytrans.Translations import com.pengrad.telegrambot.TelegramBot import com.pengrad.telegrambot.request.GetMe @@ -127,6 +128,12 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes )} |""" + ///>>> BLOCK START local storage / data configuration + + val lang: MornyLangs = MornyLangs() + + ///>>> BLOCK END local storage / data configuration + ///>>> BLOCK START instance configure & startup stage 1 logger `info` "Coeur starting..." @@ -324,6 +331,47 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes ///<<< BLOCK END instance configure & startup stage 2 + /** Provide DSLs of Morny Coeur + * + * Provide the following coeur values to using/given context: + * + * - Coeur itself + * - its Telegram bot account: [[MornyCoeur.account]] + * - its i18n translation instance: [[MornyCoeur.lang.translations]] + * + * Also provides the DSLs defined in [[BotExtension]]. + * + * You can simply add the following line to use the vals and DSL functions in your code: + * + * {{{ + * // assuming the Coeur instance is coeur + * import coeur.dsl.given + * + * // now you can use the vals in your code + * translations.trans(/* ... */) + * // or use as an implicit/using value + * import com.pengrad.telegrambot.request.SendMessage + * import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute + * SendMessage(/* ... */) + * .unsafeExecute // implicitly use coeur.account + * + * // you need to use the * import to use DSL functions + * import coeur.dsl.* + * // or use the following line to import both vals and DSL functions + * import coeur.dsl.{*, given} + * + * // now you can use the DSL functions in your code + * import com.pengrad.telegrambot.model.User + * val prefer_languge = user.asInstanceOf[User].prefer_language + * + * }}} + */ + object dsl extends BotExtension { + given coeur: MornyCoeur = MornyCoeur.this + given account: TelegramBot = MornyCoeur.this.account + given translations: Translations = MornyCoeur.this.lang.translations + } + def saveDataAll(): Unit = { modules.foreach(it => it.onRoutineSavingData) logger `notice` "done all save action." @@ -396,7 +444,8 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes logger `info` s"Succeed logged in to @${remote.username}" break(Some(LoginResult(account, remote.username, remote.id))) } catch - case r: boundary.Break[Option[LoginResult]] => throw r + /* boundary.Break[Option[LoginResult] */ + case r: boundary.Break[_] => throw r case e: Throwable => logger `error` s"""${e.toLogString} diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/MornyLangs.scala b/src/main/scala/cc/sukazyo/cono/morny/core/MornyLangs.scala new file mode 100644 index 0000000..2a86535 --- /dev/null +++ b/src/main/scala/cc/sukazyo/cono/morny/core/MornyLangs.scala @@ -0,0 +1,68 @@ +package cc.sukazyo.cono.morny.core + +import cc.sukazyo.cono.morny.core.Log.logger +import cc.sukazyo.cono.morny.data.MornyAssets +import cc.sukazyo.cono.morny.util.hytrans.* +import cc.sukazyo.cono.morny.util.UseThrowable.toLogString + +import java.io.IOException +import scala.collection.mutable +import scala.util.boundary +import scala.util.boundary.break + +class MornyLangs { + + private val (lang_index, lang_trans) = { + + logger `info` s"Loading Morny's translation data." + + val (lang_dir, lang_index_content) = try {( + MornyAssets.pack.getResDir("langs"), + MornyAssets.pack.getResource("langs/_index.hyl").readAsString() + )} catch case e: IOException => + throw Exception("Cannot read Morny's translations file.", e) + val my_index = LanguageTree.parseTreeDocument(lang_index_content) + val indexed_langs: Set[String] = { + val lang_tags = mutable.ListBuffer.empty[String] + my_index.root.traverseTree(lang_tags += _.langTag.lang) + logger `info` s"indexed following languages: ${lang_tags.mkString(", ")}" + lang_tags.toSet + } + + val language_translations = mutable.HashMap.empty[String, Definitions] + + for (file <- lang_dir.listFiles().filter(_.isFile)) yield { boundary { + import file.getPath as raw_path + if !(raw_path.endsWith(".hyt") || raw_path.endsWith(".hytrans")) then break() + val file_name = file.getPath.reverse.takeWhile(c=>(c!='/')&&(c !='\\')).reverse + val file_basename = file_name.dropRight( + if file_name.endsWith(".hyt") then ".hyt".length + else ".hytrans".length + ) + val normalized = LangTag.normalizeLangTag(file_basename) + if !indexed_langs.contains(normalized) then + logger `warn` s"translation file \"$file_name\" is not in language index, so it got ignored (normalized lang name is \"$normalized\")." + break() + val lang_def = try { + val content = file.readAsString() + Parser.parse(content) + } catch case e: IOException => + logger `error` + s"""Failed read/parse translation file $file_name (normalized lang name is $normalized): + |${e.toLogString.indent(2)} + |due to failed, this ($file_name) has been ignored.""".stripMargin + break() + logger `info` s"read language file $file_name (normalized lang name is $normalized), with ${lang_def.size} entries." + if language_translations contains normalized then + // TODO: merge + logger `warn` s" language $normalized seems already loaded one yet, this will override the old one!" + else language_translations += (normalized -> lang_def) + }} + + (my_index, language_translations.toMap) + + } + + val translations: Translations = Translations(lang_index, lang_trans) + +} diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/BotExtension.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/BotExtension.scala index 6647406..edec824 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/BotExtension.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/BotExtension.scala @@ -2,10 +2,13 @@ package cc.sukazyo.cono.morny.core.bot.api import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.messages.ErrorMessage +import cc.sukazyo.cono.morny.util.hytrans.LangTag +import com.pengrad.telegrambot.model.User -/** Bot extensions for Morny feature. - */ -object BotExtension { +object BotExtension extends BotExtension + +/** Bot extensions for Morny feature. */ +trait BotExtension { extension (errorMessage: ErrorMessage[?, ?]) { @@ -21,4 +24,13 @@ object BotExtension { } + extension (user: User) { + + def prefer_language: String = + user.languageCode match + case null => "" + case x => LangTag.normalizeLangTag(x) + + } + } diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInfoOnStart.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInfoOnStart.scala index b9c2dfa..6d9c449 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInfoOnStart.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInfoOnStart.scala @@ -2,35 +2,30 @@ package cc.sukazyo.cono.morny.core.bot.command import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand} -import cc.sukazyo.cono.morny.data.MornyInformation.{getAboutPic, getMornyAboutLinksHTML} +import cc.sukazyo.cono.morny.data.MornyInformation.{getAboutPic, getMornyAboutLinksVars} import cc.sukazyo.cono.morny.util.tgapi.InputCommand import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.SendPhoto -import com.pengrad.telegrambot.TelegramBot class MornyInfoOnStart (using coeur: MornyCoeur) extends ISimpleCommand { - private given TelegramBot = coeur.account + import coeur.dsl.{*, given} override val name: String = "start" override val aliases: List[ICommandAlias] = Nil override def execute (using command: InputCommand, event: Update): Unit = { + given lang: String = event.message.from.prefer_language SendPhoto( event.message.chat.id, getAboutPic ).caption( - s"""欢迎使用 Morny Cono来自安妮的侍从小鼠。 - |Morny 具有各种各样的功能。 - | - |———————————————— - |$getMornyAboutLinksHTML - |———————————————— - | - |(你可以随时通过 /info 重新获得这些信息)""" - .stripMargin + translations.trans( + "morny.command.info.sub_start.message", + translations.transAsVar("morny.information.about_links", getMornyAboutLinksVars*) + ).stripMargin ).parseMode(ParseMode HTML) .unsafeExecute diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInformation.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInformation.scala index 33a54aa..fc5091d 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInformation.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInformation.scala @@ -3,7 +3,6 @@ package cc.sukazyo.cono.morny.core.bot.command import cc.sukazyo.cono.morny.core.{MornyCoeur, MornySystem} import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand} import cc.sukazyo.cono.morny.core.bot.api.messages.{ErrorMessage, MessagingContext} -import cc.sukazyo.cono.morny.core.Log.logger import cc.sukazyo.cono.morny.data.MornyInformation.* import cc.sukazyo.cono.morny.data.TelegramStickers import cc.sukazyo.cono.morny.reporter.MornyReport @@ -16,11 +15,11 @@ import cc.sukazyo.cono.morny.util.var_text.VarText import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.{SendMessage, SendPhoto, SendSticker} -import com.pengrad.telegrambot.TelegramBot import java.lang.System + class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand { - private given TelegramBot = coeur.account + import coeur.dsl.{*, given} private case object Subs { val STICKERS = "stickers" @@ -76,19 +75,10 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand { cxt.bind_chat.id, getAboutPic ).caption( - // language=html - (VarText( - """Morny Cono - |来自安妮的侍从小鼠。 - |———————————————— - |{about_links}""".stripMargin - ).render( - "about_links" -> getMornyAboutLinksHTML - ) :: Nil) - .map( f => - logger `trace` f - f - ).head + translations.trans( + "morny.command.info.message.about", + translations.transAsVar("morny.information.about_links", getMornyAboutLinksVars*)(using cxt.bind_message.from.prefer_language) + )(using cxt.bind_message.from.prefer_language) ).parseMode(ParseMode HTML).replyToMessageId(cxt.bind_message.messageId) .unsafeExecute @@ -229,7 +219,7 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand { ) ).parseMode(ParseMode.HTML).replyToMessageId(update.message.messageId) .unsafeExecute - } + } private def echoEventStatistics (using update: Update): Unit = { coeur.externalContext >> { (reporter: MornyReport) => diff --git a/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala b/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala index 9718003..571559a 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala @@ -1,7 +1,7 @@ package cc.sukazyo.cono.morny.data import cc.sukazyo.cono.morny.core.{MornyAbout, MornySystem} -import cc.sukazyo.cono.morny.util.var_text.VarText +import cc.sukazyo.cono.morny.util.var_text.Var import java.net.InetAddress import java.rmi.UnknownHostException @@ -39,13 +39,8 @@ object MornyInformation { def getAboutPic: Array[Byte] = TelegramImages.IMG_ABOUT get - def getMornyAboutLinksHTML: String = - VarText( - // language=html - """source code | backup - |反馈 / issue tracker - |使用说明书 / user guide & docs""".stripMargin - ).render( + def getMornyAboutLinksVars: List[Var] = + List( "MORNY_SOURCECODE_LINK" -> MornyAbout.MORNY_SOURCECODE_LINK, "MORNY_SOURCECODE_SELF_HOSTED_MIRROR_LINK" -> MornyAbout.MORNY_SOURCECODE_SELF_HOSTED_MIRROR_LINK, "MORNY_ISSUE_TRACKER_LINK" -> MornyAbout.MORNY_ISSUE_TRACKER_LINK, diff --git a/src/main/scala/cc/sukazyo/cono/morny/morny_misc/Testing.scala b/src/main/scala/cc/sukazyo/cono/morny/morny_misc/Testing.scala index 92919ce..6744ca6 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/morny_misc/Testing.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/morny_misc/Testing.scala @@ -3,29 +3,26 @@ package cc.sukazyo.cono.morny.morny_misc import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand} import cc.sukazyo.cono.morny.core.bot.api.messages.{ErrorMessage, MessagingContext} -import cc.sukazyo.cono.morny.core.bot.api.BotExtension.submit import cc.sukazyo.cono.morny.data.TelegramStickers import cc.sukazyo.cono.morny.util.tgapi.InputCommand import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.{Message, Update} import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.{SendMessage, SendSticker} -import com.pengrad.telegrambot.TelegramBot class Testing (using coeur: MornyCoeur) extends ISimpleCommand { - private given TelegramBot = coeur.account + import coeur.dsl.{*, given} override val name: String = "test" override val aliases: List[ICommandAlias] = Nil override def execute (using command: InputCommand, event: Update): Unit = { given context: MessagingContext.WithUserAndMessage = MessagingContext.extract(using event.message) + given lang: String = context.bind_user.prefer_language SendMessage( event.message.chat.id, - // language=html - "Just a TEST command.\n" - + "Please input something to test the command." + translations.trans("morny.misc.command_test.message") ).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML) .unsafeExecute @@ -34,11 +31,13 @@ class Testing (using coeur: MornyCoeur) extends ISimpleCommand { } private def execute2 (message: Message, previousContext: MessagingContext.WithUserAndMessage): Unit = { + given context: MessagingContext.WithUserAndMessage = MessagingContext.extract(using message) + given lang: String = context.bind_user.prefer_language if (message.text == "oops") SendMessage( message.chat.id, - "A test error message will be generated." + translations.trans("morny.misc.command_test.branch_oops.err_message_simple") ).replyToMessageId(message.messageId) .unsafeExecute ErrorMessage( @@ -48,16 +47,17 @@ class Testing (using coeur: MornyCoeur) extends ISimpleCommand { ).replyToMessageId(message.messageId), _complex = SendMessage( message.chat.id, - "Oops: There is just a test error." + translations.trans("morny.misc.command_test.branch_oops.err_message_complex") ).replyToMessageId(message.messageId) - )(using MessagingContext.extract(using message)) - .submit + ).submit return; SendMessage( message.chat.id, - // language=html - "Test command with following input:\n" + message.text + translations.trans( + "morny.misc.command_test.branch_normal.message", + "replied_message" -> message.text + ) ).replyToMessageId(message.messageId).parseMode(ParseMode HTML) .unsafeExecute diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/hytrans/Parser.scala b/src/main/scala/cc/sukazyo/cono/morny/util/hytrans/Parser.scala index ec29786..941d463 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/hytrans/Parser.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/hytrans/Parser.scala @@ -24,6 +24,7 @@ object Parser { 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('&') => // document meta definition line, currently not supported case Some('|') => // content line addLine(line drop 2) case Some(_) => // a key definition line diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/hytrans/Translations.scala b/src/main/scala/cc/sukazyo/cono/morny/util/hytrans/Translations.scala index 906fcef..6ebd177 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/hytrans/Translations.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/hytrans/Translations.scala @@ -1,6 +1,7 @@ package cc.sukazyo.cono.morny.util.hytrans import cc.sukazyo.cono.morny.util.var_text.{Var, VarText} +import cc.sukazyo.cono.morny.util.var_text.Var.String_As_VarText import scala.util.boundary @@ -42,4 +43,7 @@ class Translations ( def trans (key: String, args: Var*)(using lang: String): String = trans(key).render(args*) + def transAsVar (key: String, args: Var*)(using lang: String): Var = + trans(key, args*).asVar(key) + } diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/var_text/Var.scala b/src/main/scala/cc/sukazyo/cono/morny/util/var_text/Var.scala index 9e88f89..5fd4e40 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/var_text/Var.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/var_text/Var.scala @@ -8,7 +8,7 @@ import scala.language.implicitConversions * * You can just call the [[Var]] constructor to create a new Var, or use the implicit * conversion to create a var from a tuple of ([[String]], [[String]]), or use the extension - * method [[Var.StringAsVarText.asVar]] to convert a [[String]] to a var. + * method [[Var.String_As_VarText.asVar]] to convert a [[String]] to a var. * * @since 2.0.0 * @@ -77,11 +77,11 @@ object Var { * * @since 2.0.0 */ - implicit def StrStrTupleAsVar (tuple: (String, Any)): Var = + implicit def StrStrTuple_as_Var (tuple: (String, Any)): Var = Var(tuple._1, tuple._2.toString) /** @see [[asVar]] */ - implicit class StringAsVarText (text: String): + implicit class String_As_VarText (text: String): /** Convert this string text to a [[Var]]. * @since 2.0.0 * @param id the var-id. diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/var_text/VarText.scala b/src/main/scala/cc/sukazyo/cono/morny/util/var_text/VarText.scala index c0ac344..f29b855 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/var_text/VarText.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/var_text/VarText.scala @@ -2,6 +2,8 @@ package cc.sukazyo.cono.morny.util.var_text import cc.sukazyo.cono.morny.util.var_text.Var.isLegalId +import scala.language.implicitConversions + /** A text/string template that may contains some named replaceable variables. It's concept may * be similar with scala's `StringContext` or `GString` in groovy. * @@ -56,6 +58,9 @@ object VarText { def apply (_nodes: VTNode*): VarText = new VarText: override val nodes: List[VTNode] = _nodes.toList + implicit def VarText_is_String (varText: VarText): String = + varText.render() + private val symbol_escape = '/' private val symbol_var_start = '{' private val symbol_var_end = '}'