/*
* Copyright 2011 Google Inc.
*
* 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 com.google.i18n.pseudolocalization.tool;
import com.google.i18n.pseudolocalization.PseudolocalizationPipeline;
import com.google.i18n.pseudolocalization.format.FormatRegistry;
import com.google.i18n.pseudolocalization.format.MessageCatalog;
import com.google.i18n.pseudolocalization.format.ReadableMessageCatalog;
import com.google.i18n.pseudolocalization.format.WritableMessageCatalog;
import com.google.i18n.pseudolocalization.message.Message;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Run a pseudolocalization pipeline on a set of input files.
* <p>
* See {@link Pseudolocalizer.PseudolocalizerArguments#printUsage()} for
* command-line usage.
*/
public class Pseudolocalizer {
// @VisibleForTesting
static class PseudolocalizerArguments {
/**
* Print a usage message.
*/
private static void printUsage() {
System.err.println("Usage: Pseudolocalizer [--ext=fqcn[,fqcn...]] [--variant=varname|"
+ "--method=method[,method...] [--type=filetype] [<--interactive|files>]");
System.err.println("filetype: a registered file type, typically the same as the extension");
System.err.println();
System.err.println("If given a list of files, output is written to file_variant.ext");
System.err.println("If a method list is used instead of a variant, the suffix is \"pseudo\"");
System.err.println("If no variant or methods are given, psaccent is used");
}
private final List<String> fileNames;
private final String fileType;
private final boolean isInteractive;
private final List<String> methods;
private final PseudolocalizationPipeline pipeline;
private final String variant;
/**
* Process command-line arguments.
*
* @throws RuntimeException with error message on fatal errors.
*/
public PseudolocalizerArguments(String[] args) {
Set<String> validMethods = PseudolocalizationPipeline.getRegisteredMethods();
methods = new ArrayList<String>();
boolean error = false;
boolean tmpIsInteractive = false;
String tmpVariant = null;
String tmpFileType = null;
int argIndex = 0;
while (argIndex < args.length && args[argIndex].startsWith("--")) {
String argName = args[argIndex].substring(2);
if (argName.startsWith("method=")) {
for (String method : argName.substring(7).split(",")) {
if (!validMethods.contains(method)) {
System.err.println("Unknown method '" + method + "'");
error = true;
continue;
}
methods.add(method);
}
} else if (argName.startsWith("variant=")) {
if (tmpVariant != null) {
throw new RuntimeException("More than one variant supplied");
}
tmpVariant = argName.substring(8);
} else if (argName.startsWith("ext=")) {
for (String className : argName.substring(4).split(",")) {
try {
/*
* Just load the named class, let its static initializer do whatever
* registration is required.
*/
Class.forName(className);
} catch (ClassNotFoundException e) {
System.err.println("Unable to load extension class " + className);
error = true;
}
}
} else if (argName.startsWith("type=")) {
tmpFileType = argName.substring(5);
} else if (argName.equals("interactive")) {
tmpIsInteractive = true;
} else {
System.err.println("Unrecognized option: " + argName);
error = true;
}
argIndex++;
}
if (tmpVariant != null) {
if (!methods.isEmpty()) {
System.err.println("May not specify both --variant and --method, using variant");
}
} else if (methods.isEmpty()) {
tmpVariant = "psaccent";
}
fileType = tmpFileType;
variant = tmpVariant;
isInteractive = tmpIsInteractive;
if (error || (isInteractive && argIndex < args.length)) {
printUsage();
System.exit(1);
}
// collect file names to process
fileNames = new ArrayList<String>();
while (argIndex < args.length) {
fileNames.add(args[argIndex++]);
}
// build pipeline
if (variant != null) {
pipeline = PseudolocalizationPipeline.getVariantPipeline(variant);
} else {
pipeline = PseudolocalizationPipeline.buildPipeline(methods);
}
if (pipeline == null) {
throw new RuntimeException("Unable to construct pipeline for methods " + methods);
}
}
/**
* @return list of filenames -- empty if should read from stdin
*/
public List<String> getFileNames() {
return fileNames;
}
/**
* @return the methods
*/
public List<String> getMethods() {
return methods;
}
/**
* @return the pipeline
*/
public PseudolocalizationPipeline getPipeline() {
return pipeline;
}
/**
* @return the file type, or null if not specified
*/
public String getType() {
return fileType;
}
/**
* @return the variant
*/
public String getVariant() {
return variant;
}
/**
* @return the isInteractive
*/
public boolean isInteractive() {
return isInteractive;
}
}
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
Pseudolocalizer pseudolocalizer = new Pseudolocalizer();
PseudolocalizerArguments arguments = new PseudolocalizerArguments(args);
pseudolocalizer.run(arguments);
}
/**
* Run the pseudolocalizer with the supplied arguments.
*
* @param arguments
* @throws IOException
*/
// @VisibleForTesting
void run(PseudolocalizerArguments arguments) throws IOException {
List<String> fileNames = arguments.getFileNames();
PseudolocalizationPipeline pipeline = arguments.getPipeline();
if (arguments.isInteractive()) {
runStdin(pipeline);
return;
}
if (fileNames.size() == 0) {
// if no files given, read from stdin / write to stdout
MessageCatalog msgCat = FormatRegistry.getMessageCatalog(arguments.getType());
writeMessages(msgCat, readAndProcessMessages(pipeline, msgCat, System.in), System.out);
return;
}
// get the suffix to use for output file names
String suffix = arguments.getVariant();
if (suffix == null) {
suffix = "_pseudo";
} else {
suffix = "_" + suffix;
}
for (String fileName : fileNames) {
File file = new File(fileName);
if (!file.exists()) {
System.err.println("File " + fileName + " not found");
continue;
}
// get the extension of the input file and construct the output file name
int lastDot = fileName.lastIndexOf('.');
String extension;
String outFileName;
if (lastDot >= 0) {
extension = fileName.substring(lastDot + 1);
outFileName = fileName.substring(0, lastDot) + suffix + "." + extension;
} else {
extension = "";
outFileName = fileName + suffix;
}
System.out.println("Processing " + fileName + " into " + outFileName);
// get the message catalog object for the specified (or inferred) file type
String fileType = arguments.getType();
if (fileType == null) {
fileType = extension;
}
MessageCatalog msgCat = FormatRegistry.getMessageCatalog(fileType);
// read and process messages
InputStream inputStream = new FileInputStream(file);
List<Message> processedMessages = readAndProcessMessages(pipeline, msgCat, inputStream);
OutputStream outputStream = new FileOutputStream(new File(outFileName));
writeMessages(msgCat, processedMessages, outputStream);
}
}
/**
* @param pipeline
* @param msgCat
* @param inputStream
* @return processed messages
* @throws IOException
*/
private List<Message> readAndProcessMessages(PseudolocalizationPipeline pipeline,
MessageCatalog msgCat, InputStream inputStream)
throws IOException {
List<Message> processedMessages = new ArrayList<Message>();
ReadableMessageCatalog input = msgCat.readFrom(inputStream);
try {
for (Message msg : input.readMessages()) {
pipeline.localize(msg);
processedMessages.add(msg);
}
} finally {
input.close();
}
return processedMessages;
}
/**
* @param pipeline
* @throws IOException
*/
private void runStdin(PseudolocalizationPipeline pipeline) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line;
System.out.println("Enter text to pseudolocalize:");
while ((line = reader.readLine()) != null) {
if (line.length() == 0) {
break;
}
System.out.println("=> " + pipeline.localize(line));
}
}
/**
* @param msgCat
* @param messages
* @param outputStream
* @throws IOException
*/
private void writeMessages(MessageCatalog msgCat, List<Message> messages,
OutputStream outputStream) throws IOException {
// write messages
WritableMessageCatalog output = msgCat.writeTo(outputStream);
try {
for (Message msg : messages) {
output.writeMessage(msg);
}
} finally {
output.close();
}
}
}