package ul
import java.{io, util}
import collection.mutable.{HashMap};
/**
* Gettext support class
* @param root translations root directory
* @param domain translation domain (typically 'messages' or program name)
* @param category translation category (typically "MESSAGES")
* @param lang current translation language (default "en")
* @param langDef original language (default "en")
*/
class GetText(
var root: String = "./locale",
var domain: String = "messages",
var category: String = "MESSAGES",
var lang: String = "en",
var langDef: String = "en"
) {
/**
* Translations catalog map in format [lang => [str => [tran1,...tranN]]]
*/
val cat = new HashMap[String, HashMap[String, Array[Array[String]]]];
load; // load/parse catalog
/** Load all translations from root directory */
def load:GetText = {
try {
for (langDir <- new io.File(root).list if (new io.File(root + io.File.separator + langDir).isDirectory)) { //iterate over all dirs in root
try {
println("Loading: " + (root + io.File.separator + langDir + io.File.separator +
"LC_" + category + io.File.separator + domain + ".mo"))
//try to load and parse .MO file
val moBytes = scala.io.Source.fromFile(
root + io.File.separator + langDir + io.File.separator +
"LC_" + category + io.File.separator + domain + ".mo",
"ISO-8859-1"
).map(_.toByte ).toArray;
val moCat = parseMO( moBytes )
cat(langDir) = moCat
} catch { case _:Throwable => }
}
} catch { case _:Throwable => }
this
}
/**
* Parse .MO file contents
* @param mo file contents in byte array
* @return translations map
*/
def parseMO( mo:Array[Byte] ):HashMap[String, Array[Array[String]]] = {
val lcat = new HashMap[String, Array[Array[String]]];
if (mo.length > 24) {
// check magick for msb/lsb
val magick = (((mo(0) & 0xff) * 256 + (mo(1) & 0xff)) * 256 + (mo(2) & 0xff)) * 256 + (mo(3) & 0xff);
val msb = magick == 0x950412deL;
if ((magick == 0x950412de) || (magick == 0xde120495)) {
def u32( i:Int ):Int = if (msb)
((((mo(i) & 0xff) * 256 + (mo(i+1) & 0xff)) * 256 + (mo(i+2) & 0xff)) * 256 + (mo(i+3) & 0xff));
else
((((mo(i+3) & 0xff) * 256 + (mo(i+2) & 0xff)) * 256 + (mo(i+1) & 0xff)) * 256 + (mo(i) & 0xff));
val rev = u32(4); // revision
val (revMaj, revMin) = (rev >> 16, rev % 65536); // major/minor revision
// number of strings, offsets of original and translation strings tables
val (sn, oto, ott) = (u32(8), u32(12), u32(16));
if (sn > 1 && revMaj <= 1 && revMin <= 1) {
for (sc <- 1 to sn-1) { // process all strings
// original string(s) length, offset
val (osl, oso) = ( u32( oto + 8 * sc ), u32( oto + 8 * sc + 4 ) );
// translation string(s) length, offset
val (tsl, tso) = ( u32( ott + 8 * sc ), u32( ott + 8 * sc + 4 ) );
if (osl > 0 && tsl > 0) {
// original string(s)
val os = mo.slice( oso, oso + osl + 1);
// extract all original forms
var (oss, ossp) = (List[String](), 0);
for (i <- 0 to os.length-1) {
if ( os(i) == 0 ) {
oss = oss ::: new String(os.slice(ossp, i), "utf8") :: Nil;
ossp = i + 1;
}
}
// translation string(s)
val ts = mo.slice( tso, tso + tsl + 1);
// extract all translation forms
var (tss, tssp) = (List[String](), 0);
for (i <- 0 to ts.length-1) {
if ( ts(i) == 0 ) {
tss = tss ::: new String(ts.slice(tssp, i), "utf8") :: Nil;
tssp = i + 1;
}
}
lcat( oss(0) ) = Array( oss.toArray, tss.toArray );
}
}
}
}
}
return lcat
}
/**
* Parse .PO file
* @param po sequence of strings from file
* @return translations map
*/
def parsePo( po:Seq[String] ): HashMap[String, Array[String]] = {
val lcat = new HashMap[String, Array[String]];
/* tranMap.clear // clear map
localeDir = aLocaleDir
domainName = aDomainName
// search for translations
try {
for (langDir <- new File(localeDir).list()) {
try {
val poFile = new File(localeDir + File.separator + langDir + File.separator +
messagesDir + File.separator + domainName + ".po")
val moFile = new File(localeDir + File.separator + langDir + File.separator +
messagesDir + File.separator + domainName + ".mo")
if (poFile.exists()) { // parse .po file
tranMap(langDir) = new HashMap[String, String]()
val fi = new FileInputStream( poFile )
val din = new Array[Byte](poFile.length().toInt)
fi.read( din ); fi.close()
val poStr = new String(din, poEncoding)
val poRe = "(?msu:msgid +\"(.*?)\".*?msgstr +\"(.*?)\")".r
for (m <- poRe.findAllIn( poStr ).matchData if ((m.groupCount == 2)&&(m.group(1).length > 0)))
tranMap(langDir)(m.group(1)) = m.group(2);
} else if (moFile.exists()) { // parse .mo file
}
} catch { case _=> if (logger != null) logger.log("Error loading localization directory " + langDir + ".") }
}
} catch { case _=> if (logger != null) logger.log("Error loading localization.") }
*/
return lcat;
}
/**
* Translation function
* @param s original string
* @param pl plural form
* @param aLang translation language
* @return translated string
*/
def tr( s: String, pl: Int, aLang: String ): String = {
try {
return cat(aLang)(s)(1)(pl-1);
} catch {
case _:Throwable => return s;
}
}
/**
* Translation function
* @param s original string
* @param pl plural form
* @return translated string
*/
def tr( s: String, pl: Int ): String = { /// translation function
try {
return cat(lang)(s)(1)(pl-1);
} catch {
case _:Throwable => return s;
}
}
/**
* Translation function
* @param s original string
* @return translated string
*/
def tr( s: String ): String = { /// translation function
try {
return cat(lang)(s)(1)(0);
} catch {
case _:Throwable => return s;
}
}
def langs: Seq[String] = langDef :: (for ((l,t) <- cat) yield l).toList;
def lang(l:String):String = {
if (langs.contains(l)) l
else if (displayLangs.contains(l)) langs(displayLangs.indexOf(l))
else ""
}
def lang(l:Int):String = langs(l);
def displayLangs:Seq[String] =
(for (l <- langs) yield {
val ls = l.split("_");
val loc = if (ls.length == 1) new util.Locale(l) else new util.Locale(ls(0), ls(1));
loc.getDisplayName(loc).capitalize;
});
def displayLang(l:String):String = {
if (langs.contains(l)) displayLangs(langs.indexOf(l))
else if (displayLangs.contains(l)) l
else ""
}
def displayLang(l:Int):String = displayLangs(l);
def langIndex(l:String):Int = {
if (langs.contains(l)) langs.indexOf(l)
else if (displayLangs.contains(l)) displayLangs.indexOf(l)
else -1
}
}
object GetText {
var tran:GetText = null;
def tr(s:String):String = {
if (tran == null) s else tran.tr(s)
}
def lang = tran.lang
def lang_=(l:String) = {tran.lang = l}
def langs = tran.langs
def load(path:String, domain:String, category:String, lang:String, langDef:String) = {
tran = new GetText(path, domain, category, lang, langDef);
}
def load(path:String):Unit = {
load(path, "messages", "MESSAGES", "en", "en")
}
def load:Unit = load("./locale");
}