diff --git a/packaging/ATCS_latest b/packaging/ATCS_latest index 215f621..cd7a095 100644 --- a/packaging/ATCS_latest +++ b/packaging/ATCS_latest @@ -1 +1 @@ -v0.6.11 \ No newline at end of file +v0.6.12 \ No newline at end of file diff --git a/src/com/gpl/rpg/atcontentstudio/ui/AboutEditor.java b/src/com/gpl/rpg/atcontentstudio/ui/AboutEditor.java index ed3574b..4401e45 100644 --- a/src/com/gpl/rpg/atcontentstudio/ui/AboutEditor.java +++ b/src/com/gpl/rpg/atcontentstudio/ui/AboutEditor.java @@ -78,6 +78,9 @@ public class AboutEditor extends Editor { "jsoup by Jonathan Hedley
" + "License: MIT License
" + "
" + + "A slightly modified version of General PO Parser by Bal醶s T髏h
" + + "License: GPL v3
" + + "
" + "See the tabs below to find the full license text for each of these.
" + "
" + "The Windows installer was created with:
" + diff --git a/src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PotComparator.java b/src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PotComparator.java new file mode 100644 index 0000000..f326769 --- /dev/null +++ b/src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PotComparator.java @@ -0,0 +1,154 @@ +package com.gpl.rpg.atcontentstudio.ui.tools.i18n; + +import java.io.File; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import com.gpl.rpg.atcontentstudio.model.Project; + +import net.launchpad.tobal.poparser.POEntry; +import net.launchpad.tobal.poparser.POFile; +import net.launchpad.tobal.poparser.POParser; + +public class PotComparator { + + Map> stringsResourcesNew = new LinkedHashMap>(); + Map resourcesStringsNew = new LinkedHashMap(); + + Map> stringsResourcesOld = new LinkedHashMap>(); + Map resourcesStringsOld = new LinkedHashMap(); + + + public PotComparator(Project proj) { + POParser parser = new POParser(); + + POFile newPot = parser.parseFile(new File(proj.alteredContent.baseFolder.getAbsolutePath()+File.separator+"english.pot")); + if (newPot == null) { + System.err.println("Cannot locate new english.pot file at "+proj.alteredContent.baseFolder.getAbsolutePath()+File.separator); + } + extractFromPoFile(newPot, stringsResourcesNew, resourcesStringsNew); + + POFile oldPot = parser.parseFile(new File(proj.baseContent.baseFolder.getAbsolutePath()+File.separator+"assets"+File.separator+"translation"+File.separator+"english.pot")); + if (oldPot == null) { + System.err.println("Cannot locate old english.pot file at "+proj.baseContent.baseFolder.getAbsolutePath()+File.separator+"assets"+File.separator+"translations"+File.separator); + } + extractFromPoFile(oldPot, stringsResourcesOld, resourcesStringsOld); + } + + + private void extractFromPoFile(POFile po, Map> stringsResources, Map resourcesStrings) { + for (POEntry entry : po.getEntryArray()) { + Vector resources = entry.getStringsByType(POEntry.StringType.REFERENCE); + Vector msgids = entry.getStringsByType(POEntry.StringType.MSGID); + if (resources == null || resources.size() == 0 || msgids == null || msgids.size() == 0) continue; + String msgid = msgids.get(0); + if (msgids.size() > 1) { + for (int i = 1; i < msgids.size(); i++) { + msgid += "\n"; + msgid += msgids.get(i); + } + } + for (String resLine : resources) { + String[] resArray = resLine.split(" "); + for (String res : resArray) { + resourcesStrings.put(res, msgid); + if (stringsResources.get(msgid) == null) { + stringsResources.put(msgid, new LinkedList()); + } + stringsResources.get(msgid).add(res); + } + } + } + } + + public void compare() { + for (String oldRes : resourcesStringsOld.keySet()) { + String newString = resourcesStringsNew.get(oldRes); + String oldString = resourcesStringsOld.get(oldRes); + if (newString != null) { + if (!newString.equals(oldString)) { + List allOldResources = stringsResourcesOld.get(oldString); + List allNewResources = stringsResourcesNew.get(oldString); + System.out.println("---------------------------------------------"); + System.out.println("--- TYPO CHECK ------------------------------"); + System.out.println("---------------------------------------------"); + System.out.println("String at: "+oldRes); + if (allOldResources.size() > 1) { + System.out.println("Also present at:"); + for (String res : allOldResources) { + if (!res.equals(oldRes)) { + System.out.println("- "+res); + } + } + } + if (allNewResources != null) { + System.out.println("Still present at: "); + for (String res : allNewResources) { + System.out.println("- "+res); + } + } + System.out.println("Was : \""+oldString+"\""); + System.out.println("Now : \""+newString+"\""); + + } + } else { + List allOldResources = stringsResourcesOld.get(oldString); + List allNewResources = stringsResourcesNew.get(oldString); + if (allOldResources.size() >= 1) { + System.out.println("---------------------------------------------"); + System.out.println("--- REMOVED RESOURCE ------------------------"); + System.out.println("---------------------------------------------"); + System.out.println("String at: "+oldRes); + if (allOldResources.size() > 1) { + System.out.println("And also at:"); + for (String res : allOldResources) { + if (!res.equals(oldRes)) { + System.out.println("- "+res); + } + } + } + System.out.println("Was: \""+oldString+"\""); + if (allNewResources == null) { + System.out.println("Absent from new."); + } else { + System.out.println("Still present at: "); + for (String res : allNewResources) { + System.out.println("- "+res); + } + + } + } + } + } + removedStrings: for (String oldString : stringsResourcesOld.keySet()) { + if (stringsResourcesNew.get(oldString) == null) { + List allOldResources = stringsResourcesOld.get(oldString); + if (allOldResources.size() >= 1) { + if (allOldResources.size() > 0) { + for (String res : allOldResources) { + String newString = resourcesStringsNew.get(res); + if (newString != null) { + continue removedStrings; + } + } + } + System.out.println("---------------------------------------------"); + System.out.println("--- REMOVED STRING --------------------------"); + System.out.println("---------------------------------------------"); + System.out.println("String: \""+oldString+"\""); + if (allOldResources.size() > 0) { + System.out.println("Was at:"); + for (String res : allOldResources) { + System.out.println("- "+res); + } + } + System.out.println("This string is absent from the new file, and its attached resources are missing too."); + } + } + } + } + +} diff --git a/src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PotGenerator.java b/src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PotGenerator.java new file mode 100644 index 0000000..14b5db6 --- /dev/null +++ b/src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PotGenerator.java @@ -0,0 +1,114 @@ +package com.gpl.rpg.atcontentstudio.ui.tools.i18n; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import com.gpl.rpg.atcontentstudio.model.GameSource; +import com.gpl.rpg.atcontentstudio.model.Project; +import com.gpl.rpg.atcontentstudio.model.gamedata.ActorCondition; +import com.gpl.rpg.atcontentstudio.model.gamedata.Dialogue; +import com.gpl.rpg.atcontentstudio.model.gamedata.Item; +import com.gpl.rpg.atcontentstudio.model.gamedata.ItemCategory; +import com.gpl.rpg.atcontentstudio.model.gamedata.JSONElement; +import com.gpl.rpg.atcontentstudio.model.gamedata.NPC; +import com.gpl.rpg.atcontentstudio.model.gamedata.Quest; +import com.gpl.rpg.atcontentstudio.model.gamedata.QuestStage; +import com.gpl.rpg.atcontentstudio.model.maps.WorldmapSegment; + +public class PotGenerator { + + public static void generatePotFileForProject(Project proj) { + Map> stringsResources = new LinkedHashMap>(); + Map resourcesStrings = new LinkedHashMap(); + + GameSource gsrc = proj.baseContent; + + for (ActorCondition ac : gsrc.gameData.actorConditions) { + pushString(stringsResources, resourcesStrings, ac.display_name, getPotContextComment(ac)); + } + + for (Dialogue d : gsrc.gameData.dialogues ) { + pushString(stringsResources, resourcesStrings, d.message, getPotContextComment(d)); + if (d.replies == null) continue; + for (Dialogue.Reply r : d.replies) { + if (r.text != null && !r.text.equals(Dialogue.Reply.GO_NEXT_TEXT) ) { + pushString(stringsResources, resourcesStrings, r.text, getPotContextComment(d)+":"+d.replies.indexOf(r)); + } + } + } + + for (ItemCategory ic : gsrc.gameData.itemCategories) { + pushString(stringsResources, resourcesStrings, ic.name, getPotContextComment(ic)); + } + + for (Item i : gsrc.gameData.items) { + pushString(stringsResources, resourcesStrings, i.name, getPotContextComment(i)); + pushString(stringsResources, resourcesStrings, i.description, getPotContextComment(i)+":description"); + } + + for (NPC npc : gsrc.gameData.npcs ) { + pushString(stringsResources, resourcesStrings, npc.name, getPotContextComment(npc)); + } + + for (Quest q : gsrc.gameData.quests) { + pushString(stringsResources, resourcesStrings, q.name, getPotContextComment(q)); + for (QuestStage qs : q.stages) { + pushString(stringsResources, resourcesStrings, qs.log_text, getPotContextComment(q)+":"+Integer.toString(qs.progress)); + } + } + + for (WorldmapSegment ws : gsrc.worldmap) { + for (WorldmapSegment.NamedArea area : ws.labels.values()) { + pushString(stringsResources, resourcesStrings, area.name, gsrc.worldmap.worldmapFile.getName()+":"+ws.id+":"+area.id); + } + } + + File f = new File(proj.alteredContent.baseFolder, "english.pot"); + try { + FileWriter fw = new FileWriter(f); + for (String msg : stringsResources.keySet()) { + for (String ctx : stringsResources.get(msg)) { + fw.write("#: "); + fw.write(ctx); + fw.write("\n"); + } + fw.write("msgid \""); + fw.write(msg); + fw.write("\"\nmsgstr \"\"\n\n"); + + } + fw.flush(); + fw.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + private static void pushString (Map> stringsResources, Map resourcesStrings, String translatableString, String resourceIdentifier) { + if (translatableString == null) return; + if (translatableString.contains("\n")) { + translatableString = translatableString.replaceAll("\n", "\\\\n\"\n\""); + translatableString = "\"\n\""+translatableString; + } + resourcesStrings.put(resourceIdentifier, translatableString); + List resourceIdentifiers = stringsResources.get(translatableString); + if (resourceIdentifiers == null) { + resourceIdentifiers = new LinkedList(); + stringsResources.put(translatableString, resourceIdentifiers); + } + resourceIdentifiers.add(resourceIdentifier); + } + + private static String getPotContextComment(JSONElement e) { + return e.jsonFile.getName()+":"+e.id; + } + + +} diff --git a/src/net/launchpad/tobal/poparser/POEntry.java b/src/net/launchpad/tobal/poparser/POEntry.java new file mode 100644 index 0000000..140c0cc --- /dev/null +++ b/src/net/launchpad/tobal/poparser/POEntry.java @@ -0,0 +1,100 @@ +/** + * + * @author Bal谩zs T贸th (tobal17@gmail.com) + * + * Modified by Kevin POCHAT for ATCS + */ +package net.launchpad.tobal.poparser; + +import java.util.Vector; + +public class POEntry +{ + private POLine[] Lines; + + public enum StringType + { + /**translator comments*/ + TRLCMNT, + /**extracted comments*/ + EXTCMNT, + /**reference*/ + REFERENCE, + /**flag*/ + FLAG, + /**previous context*/ + PREVCTXT, + /**previous untranslated string singular*/ + PREVUNTRSTRSING, + /**previous untranslated string plural*/ + PREVUNTRSTRPLUR, + /**untranslated string singular*/ + MSGID, + /**translated string*/ + MSGSTR, + /**context*/ + MSGCTXT, + /**header line*/ + HEADER + + // TODO: support for plural untranslated strings, + // and translated string cases + } + + POEntry() + { + Lines = new POLine[0]; + } + + public void addLine(StringType type, String string) + { + boolean hasType = false; + POLine line = null; + for(int i = 0; i < Lines.length; i++) + { + if(Lines[i].getType() == type) + { + hasType = true; + line = Lines[i]; + break; + } + } + if(hasType) + { + line.addString(string); + } + else + { + line = new POLine(type, string); + POLine[] templines = Lines.clone(); + Lines = new POLine[Lines.length+1]; + for(int i = 0; i < Lines.length-1; i++) + { + Lines[i] = templines[i]; + } + Lines[Lines.length-1] = line; + } + } + + public POLine[] getLines() + { + return Lines; + } + + public Vector getStringsFromLine(int index) + { + return Lines[index].getStrings(); + } + + public Vector getStringsByType(POEntry.StringType type) + { + for(int i = 0; i < Lines.length; i++) + { + if(Lines[i].getType() == type) + { + return Lines[i].getStrings(); + } + } + return null; + } +} diff --git a/src/net/launchpad/tobal/poparser/POFile.java b/src/net/launchpad/tobal/poparser/POFile.java new file mode 100644 index 0000000..d55a4d1 --- /dev/null +++ b/src/net/launchpad/tobal/poparser/POFile.java @@ -0,0 +1,139 @@ +/** + * + * @author Bal谩zs T贸th (tobal17@gmail.com) + * + * Modified by Kevin POCHAT for ATCS + */ +package net.launchpad.tobal.poparser; + +import java.io.File; +import java.util.Vector; + +public class POFile +{ + private POEntry[] entries; + private POEntry header; + private File file; + + POFile(POEntry[] entries, POEntry header, File file) + { + this.entries = entries; + this.header = header; + this.file = file; + } + + /** + * Returns with the name of the po file + * @return name of po file + */ + public String getFileName() + { + return file == null ? null : file.getAbsolutePath(); + } + + /** + * Returns with the POEntry object array + * @return POEntry array + */ + public POEntry[] getEntryArray() + { + return entries; + } + + /** + * Gets the POEntry object specified by the index + * @param index, index of the entry + * @return one POEntry object + */ + public POEntry getEntry(int index) + { + return entries[index]; + } + + /** + * Returns how many entries are there in the po file + * @return count of entries + */ + public int getEntryLength() + { + return entries.length; + } + + /** + * Checks if the specified flag is set in the entry, + * given by the entry index. + * @param flag, string representing the flag + * @param entryIndex, index of the entry to examine + * @return true, if the flag is set, false otherwise + */ + public boolean checkFlag(String flag, int entryIndex) + { + boolean status = false; + Vector strings = new Vector(); + strings = entries[entryIndex].getStringsByType(POEntry.StringType.FLAG); + if (strings != null) + { + for(int i = 0; i < strings.size(); i++) + { + if (strings.get(i).contains(flag)) + { + status = true; + } + } + } + return status; + } + + /** + * Returns with all the strings of the given type, from + * the specified entry. + * @param entryIndex + * @param type + * @return String array of specified type + */ + public String[] getStringsFromEntryByType(int entryIndex, POEntry.StringType type) + { + Vector vector = entries[entryIndex].getStringsByType(type); + String[] str = new String[vector.size()]; + for(int i = 0; i < str.length; i++) + { + str[i] = vector.get(i); + } + return str; + } + + /** + * For debug purposes + */ + public void printFile() + { + for(int i = 0; i < entries.length; i++) + { + POLine[] lines = entries[i].getLines(); + for(int j = 0; j < lines.length; j++) + { + Vector strings = lines[j].getStrings(); + for(int k = 0; k < strings.size(); k++) + { + System.out.println(strings.get(k)); + } + } + } + } + + /** + * For debug purposes + */ + public void printHeader() + { + POLine[] lines = header.getLines(); + for(int j = 0; j < lines.length; j++) + { + Vector strings = lines[j].getStrings(); + for(int k = 0; k < strings.size(); k++) + { + System.out.println(strings.get(k)); + } + } + } +} diff --git a/src/net/launchpad/tobal/poparser/POLine.java b/src/net/launchpad/tobal/poparser/POLine.java new file mode 100644 index 0000000..59b0f8d --- /dev/null +++ b/src/net/launchpad/tobal/poparser/POLine.java @@ -0,0 +1,42 @@ +/** + * + * @author Bal谩zs T贸th (tobal17@gmail.com) + * + * Modified by Kevin POCHAT for ATCS + */ +package net.launchpad.tobal.poparser; + +import java.util.Vector; + +public class POLine +{ + private POEntry.StringType type; + private Vector strings; + + POLine(POEntry.StringType type, String string) + { + this.type = type; + this.strings = new Vector(); + this.strings.add(string); + } + + public void addString(String string) + { + strings.add(string); + } + + public Vector getStrings() + { + return strings; + } + + public POEntry.StringType getType() + { + return type; + } + + public int getVectorSize() + { + return strings.size(); + } +} \ No newline at end of file diff --git a/src/net/launchpad/tobal/poparser/POParser.java b/src/net/launchpad/tobal/poparser/POParser.java new file mode 100644 index 0000000..b516231 --- /dev/null +++ b/src/net/launchpad/tobal/poparser/POParser.java @@ -0,0 +1,275 @@ +/** + * + * @author Bal谩zs T贸th (tobal17@gmail.com) + * + * Based on the work of Istv谩n Nyitrai + * + * Modified by Kevin POCHAT for ATCS + */ +package net.launchpad.tobal.poparser; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.io.BufferedReader; +import java.util.Vector; + +public class POParser +{ + private POEntry[] entries; + private POEntry header; +// private File file; + private POEntry.StringType parserMode; + + /** + * Creates a POParser object. Use getPOFile() method, + * to access parsed data. + * @param file, File object of the PO file + */ + public POParser() + { + parserMode = null; + } + + public POFile parseFile(File file) + { + return parse(file); + } + + public POFile parseStream(BufferedReader br) throws IOException { + return parse(br); + } + + private String unQuote(String string) + { + String str = new String(); + if(string.startsWith("\"")) + { + str = string.substring(1); + string = str; + } + if(string.endsWith("\"")) + { + str = string.substring(0, string.length()-1); + } + return str; + } + + private POFile parse(File file) + { + POFile result = null; + try + { + FileReader fr = new FileReader(file); + BufferedReader br = new BufferedReader(fr); + result = parse(br); + br.close(); + fr.close(); + } + catch (java.io.FileNotFoundException e) + { + System.out.println(e.toString()); + } + catch (java.io.IOException e) { + System.out.println(e.toString()); + } + + return result; + } + + private POFile parse(BufferedReader br) throws IOException { + Vector rawentry = new Vector(1, 1); + Vector> rawentries = new Vector>(); + String line; + int id = 0; + while((line = br.readLine()) != null) + { + if(!line.equals("")) + { + if(!line.startsWith("#~")) // ignore + { + rawentry.add(line); + } + } + else + { + if(rawentry.size() > 0) + { + rawentry.add(0, String.valueOf(id)); + id++; + rawentries.add(new Vector(rawentry)); + rawentry = new Vector(1, 1); + } + } + } + + if(!rawentry.equals(rawentries.lastElement()) && rawentry.size() > 0) + { + rawentry.add(0, String.valueOf(id)); + rawentries.add(new Vector(rawentry)); + } + this.header = parseHeader(rawentries); + this.entries = parseEntries(rawentries); + + return new POFile(entries, header, null); + } + + private POEntry parseHeader(Vector> vectors) + { + POEntry tempheader = new POEntry(); + + // is this header? + Vector rawentry = vectors.get(0); + if(new Integer(rawentry.get(0)) == 0 && rawentry.contains("msgid \"\"")) + { + for(int i = 1; i < rawentry.size(); i++) + { + String str = rawentry.get(i); + tempheader.addLine(POEntry.StringType.HEADER, str); + str = new String(); + } + return tempheader; + } + else + { + return null; + } + } + + private POEntry[] parseEntries(Vector> vectors) + { + String line = new String(); + boolean thereIsHeader = false; + + // is this header + Vector rawentry = vectors.get(0); + if(new Integer(rawentry.get(0)) == 0 && rawentry.contains("msgid \"\"")) + { + thereIsHeader = true; + } + + int size; + if(thereIsHeader) + { + size = vectors.size()-1; + } + else + { + size = vectors.size(); + } + + POEntry[] tempentries = new POEntry[size]; + + for(int i = 0; i < size; i++) + { + POEntry entry = new POEntry(); + + if(thereIsHeader) + rawentry = vectors.get(i+1); + else + rawentry = vectors.get(i); + + rawentry.remove(0); + for(int j = 0; j < rawentry.size(); j++) + { + line = rawentry.get(j); + POEntry.StringType strType = null; + int subStrIndex = 0; + if(line.startsWith("#")) + { + parserMode = null; + if(line.startsWith("# ")) + { + strType = POEntry.StringType.TRLCMNT; + if (line.startsWith("# ")) + { + subStrIndex = 3; + } + else + { + subStrIndex = 2; + } + } + if(line.startsWith("#.")) + { + strType = POEntry.StringType.EXTCMNT; + subStrIndex = 3; + } + if(line.startsWith("#:")) + { + strType = POEntry.StringType.REFERENCE; + subStrIndex = 3; + } + if(line.startsWith("#,")) + { + strType = POEntry.StringType.FLAG; + subStrIndex = 3; + } + if(line.startsWith("#|")) + { + // TODO: can these comments be multi line? if no, + // drop support for it + if(line.startsWith("#| msgctxt ")) + { + strType = POEntry.StringType.PREVCTXT; + parserMode = strType; + subStrIndex = 11; + } + if(line.startsWith("#| msgid ")) + { + strType = POEntry.StringType.PREVUNTRSTRSING; + parserMode = strType; + subStrIndex = 9; + } + if(line.startsWith("#| msgid_plural ")) + { + strType = POEntry.StringType.PREVUNTRSTRPLUR; + parserMode = strType; + subStrIndex = 16; + } + } + String str = new String(); + str = line.substring(subStrIndex); + entry.addLine(strType, str); + } + else if(line.startsWith("msg")) + { + parserMode = null; + if(line.startsWith("msgctxt ")) + { + strType = POEntry.StringType.MSGCTXT; + parserMode = strType; + subStrIndex = 8; + } + if(line.startsWith("msgid ")) + { + strType = POEntry.StringType.MSGID; + parserMode = strType; + subStrIndex = 6; + } + if(line.startsWith("msgstr ")) + { + strType = POEntry.StringType.MSGSTR; + parserMode = strType; + subStrIndex = 7; + } + String str = new String(); + // TODO: is unquoting nessessary? + str = unQuote(line.substring(subStrIndex)); + entry.addLine(strType, str); + } + else + { + if(parserMode != null) + { + entry.addLine(parserMode, unQuote(line)); + } + } + } + tempentries[i] = entry; + } + + return tempentries; + } +} diff --git a/src/net/launchpad/tobal/poparser/ParserTest.java b/src/net/launchpad/tobal/poparser/ParserTest.java new file mode 100644 index 0000000..5964aab --- /dev/null +++ b/src/net/launchpad/tobal/poparser/ParserTest.java @@ -0,0 +1,24 @@ +/** + * @author Bal谩zs T贸th (tobal17@gmail.com) + * + * Modified by Kevin POCHAT for ATCS + */ +package net.launchpad.tobal.poparser; + +import java.io.File; + +public class ParserTest +{ + public static void main(String args[]) + { + File file = new File("C:\\file.po"); + POParser parser = new POParser(); + POFile po = parser.parseFile(file); + po.printHeader(); + po.printFile(); + // is the 3th entry fuzzy? + boolean fuzzy = po.checkFlag("fuzzy", 3); + // give me the msgid of the 4th entry + String[] str = po.getStringsFromEntryByType(4, POEntry.StringType.MSGID); + } +}