refactored the template experiment1 implementation to add i18n support of numbers & dates
This commit is contained in:
@@ -3,6 +3,12 @@ package kotlin.template.experiment1
|
|||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
import kotlin.dom.toXmlString
|
import kotlin.dom.toXmlString
|
||||||
|
|
||||||
|
import java.lang.Number
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.text.NumberFormat
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
fun StringBuilder.text(value : String) : Unit {
|
fun StringBuilder.text(value : String) : Unit {
|
||||||
this.append(value)
|
this.append(value)
|
||||||
}
|
}
|
||||||
@@ -22,43 +28,61 @@ fun format(fn : (StringBuilder) -> Unit) : String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
abstract class TextBuilder<T>(val output: Appendable) {
|
abstract class TextBuilder(val output : Appendable) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Converts the given string template to a String
|
* Converts the given string template to a String
|
||||||
|
|
||||||
fun toString(fn: (T) -> Unit): String = toString(ToStringFormatter, fn)
|
fun toString(fn: (T) -> Unit): String = toString(ToStringFormatter, fn)
|
||||||
|
|
||||||
fun toString(formatter : ValueFormatter, fn: (T) -> Unit): String {
|
fun toString(formatter : ValueFormatter, fn: (T) -> Unit): String {
|
||||||
fn(this)
|
fn(this)
|
||||||
return output.toString() ?: ""
|
return output.toString() ?: ""
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fun text(value: String): Unit {
|
fun text(value : String) : Unit {
|
||||||
output.append(value)
|
output.append(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class HtmlBuilder(output: Appendable) : TextBuilder<HtmlBuilder>(output) {
|
open class I18nBuilder(output : Appendable, val formatter : I18nFormatter) : TextBuilder(output) {
|
||||||
var nullText : String = ""
|
|
||||||
|
|
||||||
fun expression(value: Node): Unit {
|
fun expression(value : Any?) : Unit {
|
||||||
|
escape(formatter.format(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expression(value : Number) : Unit {
|
||||||
|
escape(formatter.formatNumber(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expression(value : Date) : Unit {
|
||||||
|
escape(formatter.formatDate(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default does no escaping but inherited classes may escape text
|
||||||
|
*/
|
||||||
|
open fun escape(text : String) : Unit {
|
||||||
|
output.append(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class HtmlBuilder(output : Appendable, formatter : I18nFormatter) : I18nBuilder(output, formatter) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nodes are assumed to already be properly HTML/XML encoded so don't escape
|
||||||
|
*/
|
||||||
|
fun expression(value : Node) : Unit {
|
||||||
text(value.toXmlString())
|
text(value.toXmlString())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun expression(value: Any?): Unit {
|
/**
|
||||||
if (value == null) {
|
* Escapes text using HTML escaping of sensitive tokens
|
||||||
output.append(nullText)
|
*/
|
||||||
} else {
|
override fun escape(text : String) : Unit {
|
||||||
escape(value.toString() ?: "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun escape(text : String) : Unit {
|
|
||||||
for (c in text) {
|
for (c in text) {
|
||||||
if (c == '<') output.append("<")
|
if (c == '<') output.append("<")
|
||||||
else if (c == '>') output.append(">")
|
else if (c == '>') output.append(">")
|
||||||
@@ -75,22 +99,57 @@ class HtmlBuilder(output: Appendable) : TextBuilder<HtmlBuilder>(output) {
|
|||||||
* different types for different kinds of language
|
* different types for different kinds of language
|
||||||
*/
|
*/
|
||||||
trait ValueFormatter {
|
trait ValueFormatter {
|
||||||
fun format(buffer : Appendable, val value : Any?) : Unit
|
fun format(val value : Any?) : String
|
||||||
}
|
}
|
||||||
|
|
||||||
object ToStringFormatter : ValueFormatter {
|
open class ToStringFormatter : ValueFormatter {
|
||||||
|
|
||||||
fun toString() = "ToStringFormatter"
|
var nullText : String = "null"
|
||||||
|
|
||||||
override fun format(buffer : Appendable, value : Any?) {
|
open fun toString() = "ToStringFormatter"
|
||||||
val text = if (value == null) "null" else value.toString()
|
|
||||||
buffer.append(text)
|
override fun format(value : Any?): String {
|
||||||
|
return if (value == null) nullText else value.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toHtml(fn: (HtmlBuilder) -> Unit): String {
|
open class I18nFormatter(val locale : Locale = Locale.getDefault().sure()): ToStringFormatter() {
|
||||||
val buffer = StringBuilder()
|
|
||||||
val html = HtmlBuilder(buffer)
|
override fun toString() = "I18nFormatter"
|
||||||
fn(html)
|
|
||||||
return buffer.toString() ?: ""
|
public var numberFormat : NumberFormat = NumberFormat.getInstance(locale).sure()
|
||||||
|
|
||||||
|
public var dateFormat : DateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, locale).sure()
|
||||||
|
|
||||||
|
fun formatNumber(number : Number): String {
|
||||||
|
return numberFormat.format(number) ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun formatDate(date : Date): String {
|
||||||
|
return dateFormat.format(date) ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the internationalised text for this formatter
|
||||||
|
*/
|
||||||
|
fun toString(fn : (I18nBuilder) -> Unit) : String {
|
||||||
|
val buffer = StringBuilder()
|
||||||
|
val builder = I18nBuilder(buffer, this)
|
||||||
|
fn(builder)
|
||||||
|
return buffer.toString() ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the HTML for the given function
|
||||||
|
*/
|
||||||
|
fun toHtml(fn : (HtmlBuilder) -> Unit) : String {
|
||||||
|
val buffer = StringBuilder()
|
||||||
|
val html = HtmlBuilder(buffer, this)
|
||||||
|
fn(html)
|
||||||
|
return buffer.toString() ?: ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,11 @@ class HtmlTemplateTest : TestCase() {
|
|||||||
val foo = "James"
|
val foo = "James"
|
||||||
val bar = "x > 1"
|
val bar = "x > 1"
|
||||||
|
|
||||||
|
val format = I18nFormatter()
|
||||||
// Code generated by the following template expression:
|
// Code generated by the following template expression:
|
||||||
//
|
//
|
||||||
// val actual = toHtml("<h1>$foo</h1> <p>hey $bar</p>")
|
// val actual = format.toHtml("<h1>$foo</h1> <p>hey $bar</p>")
|
||||||
val actual = toHtml{
|
val actual = format.toHtml{
|
||||||
it.text("<h1>")
|
it.text("<h1>")
|
||||||
it.expression(foo)
|
it.expression(foo)
|
||||||
it.text("</h1> <p>hey ")
|
it.text("</h1> <p>hey ")
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package template.experiment1
|
||||||
|
|
||||||
|
import kotlin.template.experiment1.*
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
import junit.framework.TestCase
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class I18nTemplateTest : TestCase() {
|
||||||
|
fun testDefaultLocale() : Unit {
|
||||||
|
format(I18nFormatter())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testFrance() : Unit {
|
||||||
|
format(I18nFormatter(Locale.FRANCE.sure()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testGermany() : Unit {
|
||||||
|
format(I18nFormatter(Locale.GERMANY.sure()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun format(format: I18nFormatter): Unit {
|
||||||
|
val name = "James"
|
||||||
|
// TODO currently numbers cause: java.lang.ClassNotFoundException: jet.Number
|
||||||
|
//val price: Double = 1.99
|
||||||
|
val now = Date()
|
||||||
|
|
||||||
|
|
||||||
|
// Code generated by the following template expression:
|
||||||
|
//
|
||||||
|
// val actual = format.toI18n("hello $name!")
|
||||||
|
val actual = format.toString{
|
||||||
|
it.text("hello ")
|
||||||
|
it.expression(name)
|
||||||
|
/*
|
||||||
|
it.text(" price ")
|
||||||
|
it.expression(price)
|
||||||
|
*/
|
||||||
|
it.text(" date ")
|
||||||
|
it.expression(now)
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Got text: $actual")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user