ru.makkarpov.scalingua.pofile.PoFile.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scalingua_2.11 Show documentation
Show all versions of scalingua_2.11 Show documentation
A simple gettext-like internationalization library for Scala
The newest version!
/******************************************************************************
* Copyright © 2016 Maxim Karpov *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
package ru.makkarpov.scalingua.pofile
import java.io._
import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat
import java.util.Date
import ru.makkarpov.scalingua.StringUtils
import ru.makkarpov.scalingua.pofile.parse.{ErrorReportingParser, PoLexer}
object PoFile {
/**
* This file parsing code has a few assumptions for the structure of .po file (which are always held in case when
* file is generated by code in this object).
*
* 1. Each message has comments before the first `msgctxt` or `msgid`.
* 2. Multi-line string literals are separated only by empty strings.
* 3. All message entries are in following order:
* * singluar: [msgctxt] msgid msgstr
* * plural: [msgctxt] msgid msgid_plural msgstr[0] .. msgstr[N]
* 4. After entry key there is always a string literal, e.g. no enties like "msgstr\n\"\""
* 5. Encoding of file is always UTF-8
*
* These assumptions helps to simplify parsing code a lot.
*/
val encoding = StandardCharsets.UTF_8
val GeneratedPrefix = "!Generated:"
private def headerComment(s: String) = s"# $GeneratedPrefix $s"
def apply(f: File): Seq[Message] = apply(new FileInputStream(f), f.getName)
def apply(is: InputStream, filename: String = ""): Seq[Message] = {
val parser = new ErrorReportingParser(new PoLexer(new InputStreamReader(is, StandardCharsets.UTF_8), filename))
parser.parse().value.asInstanceOf[Seq[Message]]
}
def update(f: File, messages: Seq[Message], escapeUnicode: Boolean = true, includeHeaderComment: Boolean = true): Unit = {
val output = new NewLinePrintWriter(new OutputStreamWriter(new FileOutputStream(f), encoding), false)
try {
if (includeHeaderComment) {
output.println(headerComment(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())))
output.println()
}
def printEntry(s: String, m: MultipartString): Unit = {
output.print(s + " ")
if (m.parts.isEmpty) output.println("\"\"")
else for (p <- m.parts) output.println("\"" + StringUtils.escape(p, escapeUnicode) + "\"")
}
for (m <- messages) {
for (s <- m.header.comments)
output.println(s"# $s")
for (s <- m.header.extractedComments)
output.println(s"#. $s")
for (s <- m.header.locations.sorted)
if (s.line < 0)
output.println(s"#: ${s.fileString}")
else
output.println(s"#: ${s.fileString}:${s.line}")
if (m.header.flags.nonEmpty)
output.println(s"#, " + m.header.flags.map(_.toString).mkString(", "))
for (t <- m.header.tag)
output.println(s"#~ $t")
for (c <- m.context)
printEntry("msgctxt", c)
printEntry("msgid", m.message)
m match {
case Message.Singular(_, _, _, tr) =>
printEntry("msgstr", tr)
case Message.Plural(_, _, _, id, trs) =>
printEntry("msgid_plural", id)
for ((m, i) <- trs.zipWithIndex)
printEntry(s"msgstr[$i]", m)
}
output.println()
}
} finally output.close()
}
}