/************************************************************************** OmegaT - Computer Assisted Translation (CAT) tool with fuzzy matching, translation memory, keyword search, glossaries, and translation leveraging into updated projects. Copyright (C) 2014 Enrique Estevez Fernandez Home page: http://www.omegat.org/ Support center: http://groups.yahoo.com/group/OmegaT/ This file is part of OmegaT. OmegaT is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OmegaT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. **************************************************************************/ package org.omegat.filters2.mozlang; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.omegat.core.data.ProtectedPart; import org.omegat.filters2.AbstractFilter; import org.omegat.filters2.FilterContext; import org.omegat.filters2.Instance; import org.omegat.filters2.TranslationException; import org.omegat.util.OStrings; import org.omegat.util.PatternConsts; import org.omegat.util.TagUtil; /** * Filter for support Mozilla lang files. * * Filter for lang files. Something about the format described on * https://sourceforge.net/p/omegat/feature-requests/962/ * https://developer.mozilla.org/en-US/docs/Web_Localizability/Localization_formats * http://bedrock.readthedocs.org/en/latest/l10n.html * * Code adapted from the files: PoFilter.java and SrtFilter.java * * @author Enrique Estevez (keko.gl@gmail.com) */ public class MozillaLangFilter extends AbstractFilter { protected static final Pattern LOCALIZATION_NOTE = Pattern.compile("# (.*)"); protected static final Pattern PATTERN_SOURCE = Pattern.compile("^;(.*)"); enum READ_STATE { WAIT_SOURCE, WAIT_TARGET }; private StringBuilder source, target, localizationNote; private BufferedWriter out; @Override public String getFileFormatName() { return OStrings.getString("MOZLANG_FILTER_NAME"); } @Override public Instance[] getDefaultInstances() { return new Instance[] { new Instance("*.lang") }; } /** * Creating an input stream to read the source .lang file. * <p> * NOTE: Mozilla lang files use always UTF-8 encoding without BOM. */ @Override public BufferedReader createReader(File infile, String encoding) throws UnsupportedEncodingException, IOException { return new BufferedReader(new InputStreamReader(new FileInputStream(infile), StandardCharsets.UTF_8)); } /** * Creating an output stream to save a localized .lang file. * <p> * NOTE: Mozilla lang files use always UTF-8 encoding without BOM. * <p> */ @Override public BufferedWriter createWriter(File outfile, String encoding) throws UnsupportedEncodingException, IOException { // lang file use UTF8 encoding return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outfile), StandardCharsets.UTF_8)); } @Override public boolean isSourceEncodingVariable() { return false; } @Override public boolean isTargetEncodingVariable() { return false; } @Override public String getInEncodingLastParsedFile() { return StandardCharsets.UTF_8.name(); } @Override public void processFile(File inFile, File outFile, FilterContext fc) throws IOException, TranslationException { inEncodingLastParsedFile = fc.getInEncoding(); BufferedReader reader = createReader(inFile, inEncodingLastParsedFile); try { BufferedWriter writer; if (outFile != null) { writer = createWriter(outFile, fc.getOutEncoding()); } else { writer = null; } try { processFile(reader, writer, fc); } finally { if (writer != null) { writer.close(); } } } finally { reader.close(); } } @Override protected void processFile(BufferedReader inFile, BufferedWriter outFile, FilterContext fc) throws IOException, TranslationException { source = new StringBuilder(); target = new StringBuilder(); localizationNote = new StringBuilder(); out = outFile; READ_STATE state = READ_STATE.WAIT_SOURCE; String s; while ((s = inFile.readLine()) != null) { // We trim trailing spaces, otherwise the regexps could fail, thus making some segments // invisible to OmegaT s = s.trim(); Matcher m; switch (state) { case WAIT_SOURCE: if ((m = PATTERN_SOURCE.matcher(s)).matches()) { source.append(m.group(1)); state = READ_STATE.WAIT_TARGET; } if (LOCALIZATION_NOTE.matcher(s).matches()) { localizationNote.append(s); } target.setLength(0); eol(s); break; case WAIT_TARGET: target.append(s); flushTranslation(fc); state = READ_STATE.WAIT_SOURCE; break; default: eol(s); break; } } } protected void eol(String s) throws IOException { if (out != null) { out.write(s); out.write('\n'); } } protected void align(int pair) { String s = source.toString(); String c = ""; String t; if (s.equals(target.toString())) { t = null; } else { t = target.toString(); } if (localizationNote.length() > 0) { c += "\n" + OStrings.getString("LANGFILTER_LOCALIZATION_NOTE") + "\n" + localizationNote.toString(); } if (c.length() == 0) { c = null; } align(s, t, c); } /** * * @param source * @param translation * @param comments */ protected void align(String source, String translation, String comments) { if (entryParseCallback != null) { List<ProtectedPart> protectedParts = TagUtil.applyCustomProtectedParts(source, PatternConsts.PRINTF_VARS, null); entryParseCallback.addEntry(null, source, translation, false, comments, null, this, protectedParts); } else if (entryAlignCallback != null) { entryAlignCallback.addTranslation(null, source, translation, false, null, this); } } protected void flushTranslation(FilterContext fc) throws IOException { if (out != null) { String tr; tr = entryTranslateCallback.getTranslation(null, source.toString(), null); if (tr == null) { tr = source.toString(); } else if (tr.equals(source.toString())) { tr += " {ok}"; } eol(tr); } else { align(0); } source.setLength(0); target.setLength(0); localizationNote.setLength(0); } /** * Returns true to indicate that Text filter has options. * * @return False, because the LANG filter has not options. */ @Override public boolean hasOptions() { return false; } @Override public boolean isBilingual() { return true; } }