Fix view webapp translations
This commit is contained in:
@@ -103,6 +103,7 @@
|
|||||||
</httpConnector>
|
</httpConnector>
|
||||||
<systemProperties>
|
<systemProperties>
|
||||||
<pairgoth.api.url>http://localhost:8085/api/</pairgoth.api.url>
|
<pairgoth.api.url>http://localhost:8085/api/</pairgoth.api.url>
|
||||||
|
<pairgoth.env>dev</pairgoth.env>
|
||||||
</systemProperties>
|
</systemProperties>
|
||||||
<webApp>
|
<webApp>
|
||||||
<resourceBases>${project.basedir}/src/main/webapp,${project.build.directory}/generated-resources/</resourceBases>
|
<resourceBases>${project.basedir}/src/main/webapp,${project.build.directory}/generated-resources/</resourceBases>
|
||||||
|
@@ -7,12 +7,14 @@ import org.apache.velocity.runtime.parser.node.ASTText
|
|||||||
import org.apache.velocity.runtime.parser.node.SimpleNode
|
import org.apache.velocity.runtime.parser.node.SimpleNode
|
||||||
import org.jeudego.pairgoth.web.WebappManager
|
import org.jeudego.pairgoth.web.WebappManager
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.io.File
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.ConcurrentSkipListSet
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
import kotlin.io.path.readLines
|
import kotlin.io.path.readLines
|
||||||
import kotlin.io.path.useDirectoryEntries
|
import kotlin.io.path.useDirectoryEntries
|
||||||
@@ -79,7 +81,7 @@ class Translator private constructor(private val iso: String) {
|
|||||||
if (groupStart > start) output.print(text.substring(start, groupStart))
|
if (groupStart > start) output.print(text.substring(start, groupStart))
|
||||||
val capture = matcher.group(group)
|
val capture = matcher.group(group)
|
||||||
var token: String = StringEscapeUtils.unescapeHtml4(capture)
|
var token: String = StringEscapeUtils.unescapeHtml4(capture)
|
||||||
if (StringUtils.containsOnly(token, "\r\n\t -;:.\"/<>\u00A00123456789€!")) output.print(capture) else {
|
if (StringUtils.containsOnly(token, "\r\n\t -;:.'\"/<>\u00A00123456789€[]!")) output.print(capture) else {
|
||||||
token = normalize(token)
|
token = normalize(token)
|
||||||
token = translate(token)
|
token = translate(token)
|
||||||
output.print(StringEscapeUtils.escapeHtml4(token))
|
output.print(StringEscapeUtils.escapeHtml4(token))
|
||||||
@@ -104,17 +106,15 @@ class Translator private constructor(private val iso: String) {
|
|||||||
map[0] = (ignoring != null)
|
map[0] = (ignoring != null)
|
||||||
while (pos < text.length) {
|
while (pos < text.length) {
|
||||||
if (ignoring == null) {
|
if (ignoring == null) {
|
||||||
val nextIgnore = ignoredTags.map { tag ->
|
val nextIgnore = ignoredTags.mapNotNull { tag ->
|
||||||
Pair(tag, text.indexOf("<$tag(?:>\\s)"))
|
Regex("<($tag)(?:>|\\s)").find(text)
|
||||||
}.filter {
|
}.minByOrNull {
|
||||||
it.second != -1
|
it.range.first
|
||||||
}.sortedBy {
|
}
|
||||||
it.second
|
|
||||||
}.firstOrNull()
|
|
||||||
if (nextIgnore == null) pos = text.length
|
if (nextIgnore == null) pos = text.length
|
||||||
else {
|
else {
|
||||||
ignoring = nextIgnore.first
|
ignoring = nextIgnore.groupValues[1]
|
||||||
pos += nextIgnore.first.length + 2
|
pos += ignoring.length + 2
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val closingTag = text.indexOf("</$ignoring>")
|
val closingTag = text.indexOf("</$ignoring>")
|
||||||
@@ -132,9 +132,12 @@ class Translator private constructor(private val iso: String) {
|
|||||||
get() = textAccessor[this] as String
|
get() = textAccessor[this] as String
|
||||||
set(value: String) { textAccessor[this] = value }
|
set(value: String) { textAccessor[this] = value }
|
||||||
|
|
||||||
|
private val saveMissingTranslations = System.getProperty("pairgoth.env") == "dev"
|
||||||
|
private val missingTranslations: MutableSet<String> = ConcurrentSkipListSet()
|
||||||
|
|
||||||
private fun reportMissingTranslation(enText: String) {
|
private fun reportMissingTranslation(enText: String) {
|
||||||
logger.warn("missing translation towards {}: {}", iso, enText)
|
logger.warn("missing translation towards {}: {}", iso, enText)
|
||||||
// CB TODO - create file
|
if (saveMissingTranslations) missingTranslations.add(enText)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -165,5 +168,18 @@ class Translator private constructor(private val iso: String) {
|
|||||||
|
|
||||||
val providedLanguages = setOf("en", "fr")
|
val providedLanguages = setOf("en", "fr")
|
||||||
const val defaultLanguage = "en"
|
const val defaultLanguage = "en"
|
||||||
|
|
||||||
|
internal fun notifyExiting() {
|
||||||
|
translators.values.filter {
|
||||||
|
it.saveMissingTranslations && it.missingTranslations.isNotEmpty()
|
||||||
|
}.forEach {
|
||||||
|
val missing = File("${it.iso}.missing")
|
||||||
|
logger.info("Saving missing translations for ${it.iso} to ${missing.canonicalPath}")
|
||||||
|
missing.printWriter().use { out ->
|
||||||
|
|
||||||
|
out.println(it.missingTranslations.map { "${it}\t" }.joinToString("\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package org.jeudego.pairgoth.web
|
|||||||
|
|
||||||
import com.republicate.mailer.SmtpLoop
|
import com.republicate.mailer.SmtpLoop
|
||||||
import org.apache.commons.lang3.tuple.Pair
|
import org.apache.commons.lang3.tuple.Pair
|
||||||
|
import org.jeudego.pairgoth.util.Translator
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.IllegalAccessError
|
import java.lang.IllegalAccessError
|
||||||
@@ -51,7 +52,7 @@ class WebappManager : ServletContextListener, ServletContextAttributeListener, H
|
|||||||
/* ServletContextListener interface */
|
/* ServletContextListener interface */
|
||||||
override fun contextInitialized(sce: ServletContextEvent) {
|
override fun contextInitialized(sce: ServletContextEvent) {
|
||||||
context = sce.servletContext
|
context = sce.servletContext
|
||||||
logger.info("---------- Starting Pairgoth Web Client ----------")
|
logger.info("---------- Starting $WEBAPP_NAME ----------")
|
||||||
context.setAttribute("manager", this)
|
context.setAttribute("manager", this)
|
||||||
webappRoot = context.getRealPath("/")
|
webappRoot = context.getRealPath("/")
|
||||||
try {
|
try {
|
||||||
@@ -78,11 +79,15 @@ class WebappManager : ServletContextListener, ServletContextAttributeListener, H
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun contextDestroyed(sce: ServletContextEvent) {
|
override fun contextDestroyed(sce: ServletContextEvent) {
|
||||||
logger.info("---------- Stopping Web Application ----------")
|
logger.info("---------- Stopping $WEBAPP_NAME ----------")
|
||||||
|
|
||||||
|
Translator.notifyExiting()
|
||||||
|
|
||||||
val context = sce.servletContext
|
val context = sce.servletContext
|
||||||
for (service in webServices.keys) stopService(service, true)
|
for (service in webServices.keys) stopService(service, true)
|
||||||
// ??? DriverManager.deregisterDriver(com.mysql.cj.jdbc.Driver ...);
|
// ??? DriverManager.deregisterDriver(com.mysql.cj.jdbc.Driver ...);
|
||||||
|
|
||||||
|
logger.info("---------- Stopped $WEBAPP_NAME ----------")
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ServletContextAttributeListener interface */
|
/* ServletContextAttributeListener interface */
|
||||||
@@ -95,6 +100,7 @@ class WebappManager : ServletContextListener, ServletContextAttributeListener, H
|
|||||||
override fun sessionDestroyed(se: HttpSessionEvent) {}
|
override fun sessionDestroyed(se: HttpSessionEvent) {}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
const val WEBAPP_NAME = "Pairgoth Web Client"
|
||||||
const val PAIRGOTH_PROPERTIES_PREFIX = "pairgoth."
|
const val PAIRGOTH_PROPERTIES_PREFIX = "pairgoth."
|
||||||
lateinit var webappRoot: String
|
lateinit var webappRoot: String
|
||||||
lateinit var context: ServletContext
|
lateinit var context: ServletContext
|
||||||
|
@@ -1,2 +1,28 @@
|
|||||||
New tournament Nouveau tournoi
|
New tournament Nouveau tournoi
|
||||||
Open Ouvrir
|
Open Ouvrir
|
||||||
|
Cancel Annuler
|
||||||
|
Infos Infos
|
||||||
|
Next Suivant
|
||||||
|
Pairing Appariement
|
||||||
|
Type Type
|
||||||
|
(pairgo / rengo) (pairgo / rengo)
|
||||||
|
(teams of individual players) (équipes de joueurs individuels)
|
||||||
|
MacMahon MacMahon
|
||||||
|
Number of rounds Nombre de rondes
|
||||||
|
Partner teams tournament of Tournoi d'équipes partenaires de
|
||||||
|
Standard tournament of individual players Tournoi standard de joueurs individuels
|
||||||
|
Swiss Suisse
|
||||||
|
Teams tournament of Tournoi par équipes de
|
||||||
|
Tournament dates Dates du tournoi
|
||||||
|
Tournament name Nom du tournoi
|
||||||
|
Tournament pairing Appariement du tournoi
|
||||||
|
Tournament short name Nom abbrégé du tournoi
|
||||||
|
Tournament type Type de tournoi
|
||||||
|
end date date de fin
|
||||||
|
from du
|
||||||
|
players joueurs
|
||||||
|
rounds rondes
|
||||||
|
short_name nom_tournoi
|
||||||
|
start date date de début
|
||||||
|
to au
|
||||||
|
|
||||||
|
@@ -43,7 +43,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="scrolling content">
|
<div class="scrolling content">
|
||||||
#parse('tournament-form.inc.html')
|
#translate('tournament-form.inc.html')
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button class="ui cancel black floating button">Cancel</button>
|
<button class="ui cancel black floating button">Cancel</button>
|
||||||
|
@@ -1,20 +1,20 @@
|
|||||||
<form id="new-tournament-form" class="ui form">
|
<form class="tournament-form" class="ui form">
|
||||||
<div class="ui infos tab segment">
|
<div class="ui infos tab segment">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Tournament name</label>
|
<label>Tournament name</label>
|
||||||
<input type="text" name="name" placeholder="Tournament name"/>
|
<input type="text" name="name" required placeholder="Tournament name"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Tournament short name</label>
|
<label>Tournament short name</label>
|
||||||
<input type="text" name="shortname" placeholder="short_name"/>
|
<input type="text" name="shortname" required placeholder="short_name"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Tournament dates</label>
|
<label>Tournament dates</label>
|
||||||
<span id="date-range">from <input type="text" name="start" class="date" placeholder="start date"/> to <input type="text" name="end" class="date" placeholder="end date"/></span>
|
<span class="date-range">from <input type="text" name="start" required class="date" placeholder="start date"/> to <input type="text" name="end" required class="date" placeholder="end date"/></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Number of rounds</label>
|
<label>Number of rounds</label>
|
||||||
<span><input type="number" name="rounds" min="1"/> rounds</span>
|
<span><input type="number" name="rounds" required min="1"/> rounds</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui type tab segment">
|
<div class="ui type tab segment">
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui radio">
|
<div class="ui radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="type" value="standard"/>
|
<input type="radio" name="type" value="standard" required/>
|
||||||
Standard tournament of individual players
|
Standard tournament of individual players
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui radio">
|
<div class="ui radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="type" value="partners"/>
|
<input type="radio" name="type" value="partners" required/>
|
||||||
<span>Partner teams tournament of <input type="number" min="2" name="partners"/> players</span> (pairgo / rengo)
|
<span>Partner teams tournament of <input type="number" min="2" name="partners"/> players</span> (pairgo / rengo)
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui radio">
|
<div class="ui radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="type" value="teams"/>
|
<input type="radio" name="type" value="teams" required/>
|
||||||
<span>Teams tournament of <input type="number" min="2" name="partners"/> players</span> (teams of individual players)
|
<span>Teams tournament of <input type="number" min="2" name="partners"/> players</span> (teams of individual players)
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui radio">
|
<div class="ui radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="pairing" value="macmahon"/>
|
<input type="radio" name="pairing" value="macmahon" required/>
|
||||||
MacMahon
|
MacMahon
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui radio">
|
<div class="ui radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="pairing" value="swiss"/>
|
<input type="radio" name="pairing" value="swiss" required/>
|
||||||
Swiss
|
Swiss
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,3 +68,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<script type="text/javascript">
|
||||||
|
onLoad(() => {
|
||||||
|
$('#tournament-form').on('input', e => {
|
||||||
|
|
||||||
|
})
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
CB TODO :
|
||||||
|
s'il y a duplication du formulaire (pour nouveau/édition), il faut changer les ids en class, gérer les pbs d'init du date-range, etc.
|
||||||
|
sinon, il faut paramétrer la dialog (mais les steps 1/2/3 deviennent des tabs, etc.)
|
||||||
|
|
||||||
|
Donc dans tous les cas il y a qqc à faire !
|
||||||
|
Reference in New Issue
Block a user