/************************************************************************** OmegaT - Computer Assisted Translation (CAT) tool with fuzzy matching, translation memory, keyword search, glossaries, and translation leveraging into updated projects. Copyright (C) 2010 Alex Buloichik 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.rc; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.omegat.filters2.AbstractFilter; import org.omegat.filters2.FilterContext; import org.omegat.filters2.Instance; import org.omegat.filters2.TranslationException; import org.omegat.util.NullBufferedWriter; import org.omegat.util.OStrings; import org.omegat.util.StringUtil; /** * Filter for support Windows resource files. * * Format described on * http://msdn.microsoft.com/en-us/library/aa380599(VS.85).aspx * * @author Alex Buloichik (alex73mail@gmail.com) */ public class RcFilter extends AbstractFilter { protected static final Pattern RE_DIALOG = Pattern.compile("(\\S+)\\s+DIALOG(EX)?\\s+.+"); protected static final Pattern RE_DIALOG_CAPTION = Pattern.compile("CAPTION\\s+.+"); protected static final Pattern RE_MENU = Pattern.compile("(\\S+)\\s+MENU(EX)?\\s*.*"); protected static final Pattern RE_MESSAGETABLE = Pattern.compile("(\\S+)\\s+MESSAGETABLE\\s*.*"); protected static final Pattern RE_STRINGTABLE = Pattern.compile("STRINGTABLE\\s*.*"); enum PART { DIALOG, MENU, MESSAGETABLE, STRINGTABLE, OTHER, UNKNOWN }; protected String blockId; protected int b, e; protected Map<String, String> align; public String getFileFormatName() { return OStrings.getString("RCFILTER_FILTER_NAME"); } public Instance[] getDefaultInstances() { return new Instance[] { new Instance("*.rc") }; } public boolean isSourceEncodingVariable() { return true; } public boolean isTargetEncodingVariable() { return true; } @Override protected void processFile(BufferedReader inFile, BufferedWriter outFile, FilterContext fc) throws IOException, TranslationException { PART cPart = PART.UNKNOWN; int cLevel = 0; blockId = null; String s; while ((s = inFile.readLine()) != null) { b = -1; e = -1; String id = null; String strim = s.trim(); if (strim.startsWith("//") || strim.startsWith("#")) { outFile.write(s); outFile.newLine(); continue; } if (strim.isEmpty()) { if (cLevel == 0) { cPart = PART.UNKNOWN; } } else if (cPart == PART.UNKNOWN) { cPart = parseFirstLineInBlock(strim); } else if ("{".equals(strim) || "BEGIN".equalsIgnoreCase(strim)) { cLevel++; } else if ("}".equals(strim) || "END".equalsIgnoreCase(strim)) { cLevel--; if (cLevel == 0) { cPart = PART.UNKNOWN; } } else if (cLevel > 0 && cPart != PART.OTHER && cPart != PART.UNKNOWN) { markForTranslation(s); if (b >= 0 && e >= 0 && b < e && e > 0) { id = parseId(cPart, s, b, e); } } else if (cLevel == 0 && cPart == PART.DIALOG) { if (RE_DIALOG_CAPTION.matcher(strim).matches()) { markForTranslation(s); id = "__CAPTION__"; } } if (b >= 0 && e >= 0 && b < e && e > 0) { // extract source String loc = s.substring(b + 1, e); /* * Some software produce escaped quotes, but valid are only * double quotes */ loc = loc.replace("\\\"", "\"").replace("\"\"", "\""); if (entryParseCallback != null) { entryParseCallback.addEntry(blockId + "/" + id, loc, null, false, null, null, this, null); } else if (entryTranslateCallback != null) { // replace translation String trans = entryTranslateCallback.getTranslation(blockId + "/" + id, loc, null); if (trans == null) { trans = loc; } trans = trans.replace("\"", "\"\""); s = s.substring(0, b + 1) + trans + s.substring(e); } else if (entryAlignCallback != null && id != null) { align.put(blockId + "/" + id, loc); } } outFile.write(s); outFile.newLine(); } } @Override protected void alignFile(BufferedReader sourceFile, BufferedReader translatedFile, org.omegat.filters2.FilterContext fc) throws Exception { Map<String, String> source = new HashMap<String, String>(); Map<String, String> translated = new HashMap<String, String>(); align = source; processFile(sourceFile, new NullBufferedWriter(), fc); align = translated; processFile(translatedFile, new NullBufferedWriter(), fc); for (Map.Entry<String, String> en : source.entrySet()) { String tr = translated.get(en.getKey()); if (!StringUtil.isEmpty(tr)) { entryAlignCallback.addTranslation(en.getKey(), en.getValue(), tr, false, null, this); } } } private PART parseFirstLineInBlock(String line) { Matcher m; if ((m = RE_DIALOG.matcher(line)).matches()) { blockId = m.group(1); return PART.DIALOG; } if ((m = RE_MENU.matcher(line)).matches()) { blockId = m.group(1); return PART.MENU; } if ((m = RE_MESSAGETABLE.matcher(line)).matches()) { blockId = m.group(1); return PART.MESSAGETABLE; } if (RE_STRINGTABLE.matcher(line).matches()) { blockId = ""; return PART.STRINGTABLE; } return PART.OTHER; } private String parseId(PART cPart, String line, int b, int e) { String[] w; switch (cPart) { case DIALOG: case MENU: w = line.substring(e).split(","); return w.length > 1 ? w[1].trim() : null; case MESSAGETABLE: case STRINGTABLE: w = line.substring(0, b).split(","); return w[0].trim(); default: // Nothing } return null; } private void markForTranslation(String s) { b = s.indexOf('"'); if (b < 0) { return; } e = b; while (true) { e = s.indexOf('"', s.offsetByCodePoints(e, 1)); if (e < 0) { break; } if (s.codePointBefore(e) == '\\') { // skip escaped quote continue; } if (s.codePointCount(e, s.length()) > 1) { int cp = s.codePointAt(s.offsetByCodePoints(e, 1)); if (cp == '"') { // skip double quote e += Character.charCount(cp); continue; } } break; } } }