com.force.i18n.grammar.offline.JavaScriptLabelsGenerator Maven / Gradle / Ivy
Show all versions of grammaticus Show documentation
/*
* Copyright (c) 2020, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
package com.force.i18n.grammar.offline;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.force.i18n.*;
import com.force.i18n.LanguageLabelSetDescriptor.GrammaticalLabelSetDescriptor;
import com.force.i18n.grammar.*;
import com.force.i18n.grammar.parser.GrammaticalLabelSetLoader;
/**
* Simple file generator to convert language dependent files for grammaticus.js.
*
* See also an sample implementation of executable code in test: com.force.i18n.grammar.tools.JSLabelGen
* @see grammaticus.js
* @author yoikawa
* @since 226
*/
public class JavaScriptLabelsGenerator {
private static final Logger logger = Logger.getLogger(JavaScriptLabelsGenerator.class.getName());
protected static final String COPYRIGHT_TEXT = "Copyright (c) 2020, salesforce.com, inc. All rights reserved.";
protected static final String LABEL_SET_NAME = "js";
protected static final String DEFAULT_LABELS_XML = "labels.xml";
protected static final String DEFAULT_NAMES_XML = "names.xml";
protected static final HumanLanguage BASE_LANG = LanguageProviderFactory.get().getBaseLanguage();
protected final GrammaticalLabelSetLoader loader;
protected final Path sourceRootDir;
protected final String labelFileName;
public JavaScriptLabelsGenerator(File rootDirectory) throws MalformedURLException, URISyntaxException {
this(rootDirectory, DEFAULT_LABELS_XML, DEFAULT_NAMES_XML);
}
public JavaScriptLabelsGenerator(File rootDirectory, String labelFileName, String dictionaryFileName)
throws MalformedURLException, URISyntaxException {
this(new LabelSetDescriptorImpl(rootDirectory.toURI().toURL(), BASE_LANG, LABEL_SET_NAME, labelFileName,
dictionaryFileName));
}
public JavaScriptLabelsGenerator(GrammaticalLabelSetDescriptor baseDesc) throws URISyntaxException {
this.loader = new GrammaticalLabelSetLoader(baseDesc);
this.labelFileName = Paths.get(this.loader.getBaseDesc().getRootFile().toURI()).getFileName().toString();
this.sourceRootDir = Paths.get(this.loader.getBaseDesc().getRootDir().toURI());
}
/**
* Utility method to generate the target language file in JavaScript.
*
* The file name follows UTS#35 by using Locale.toLocaleString(). For example, if the language
is
* British English, the generated file name becomes 'en_GB.js'.
*
* Note that we treat the base label as a 'root' and associate with the en_US locale, this method generates 'en.js'
* instead.
*
* @param language to specify which language file to generate
* @param outDir is the target directory to generate [language].js file
* @param shouldGenerateComment to specify whether generating comment header texts in [language].js file
* @param shouldGenerateAllNames to specify whether generating names defined in the dictionary file
* @return true if success; false otherwise.
*/
protected boolean write(HumanLanguage language, File outDir, boolean shouldGenerateComment, boolean shouldGenerateAllNames) {
// ignore empty directory
if (!isValid(this.sourceRootDir, language)) return false;
// handle only languages that LanguageProviderFactory knows. "en.json" will be skipped because of this.
// However, if you override/implement your own LangaugeProvider and HumanLanguage, you can provide any translations.
GrammaticalLabelSet labelSet = this.loader.getSet(language);
try {
// if the target language is base language, strip off the country/variant part. if base language is "en_US",
// renames to "en" (means that the file name becomes 'en.js'
Locale loc = language != BASE_LANG ? language.getLocale() : new Locale(language.getLocale().getLanguage());
String filename = loc.toString() + ".js";
log("processing language: " + language.getLocaleString() + " (as filename: " + filename + ")");
// assume first existing names.xml (from the bottom of the list) to be target folder
try (Writer out = new FileWriter(new File(outDir, filename))) {
if (shouldGenerateComment) {
out.append("/* Grammaticus v").append(VersionInfo.VERSION).append("\n")
.append(" * ").append(COPYRIGHT_TEXT).append("\n")
.append("\n")
.append(" * language depedent file for grammaticus.js\n")
.append(" * DO NOT EDIT: This file is generated by Grammaticus\n")
.append(" */\n");
}
out.append("export function override(baseObject) {'use strict';");
out.append("baseObject.addLabels(");
Set termsInUse = new HashSet<>();
labelSet.writeJson(out, null, termsInUse);
out.append("); baseObject.addTerms(");
labelSet.getDictionary().writeJsonTerms(out, false, shouldGenerateAllNames ? null : termsInUse);
out.append(");");
labelSet.getDictionary().getDeclension().writeJsonOverrides(out, "baseObject");
out.append("};");
}
} catch (IOException e) {
error("ERROR: unknown exception while processing language: " + language);
e.printStackTrace();
return false;
}
return true;
}
/*
* Returns expected path to the label source file.
* For example, if the baseDir is "/label" and language is en_GB, this returns "/label/en/GB".
*
* for historic reason, we have tweaked directory path for some languages, such as US English.
* Please checkout the actual implementation of
* {@link com.force.i18n.HumanLanguage#getDefaultLabelDirectoryPath()} for more detail.
*
* @param path to the root input directory
* @param language to read
* @return Path to the actual label source file
* @see com.force.i18n.HumanLanguage#getDefaultLabelDirectoryPath()
*/
protected Path resolve(Path baseDir, HumanLanguage language) {
Path path = baseDir.resolve(language.getDefaultLabelDirectoryPath());
Path ret = path.resolve(this.labelFileName);
return ret;
}
protected boolean isValid(Path baseDir, HumanLanguage language) {
if (language == null) return false;
Path rootLabel = resolve(baseDir, language);
return Files.isRegularFile(rootLabel) && Files.isReadable(rootLabel);
}
public void generateLabels(File outputDir, boolean shouldGenerateComment, boolean shouldGenerateAllNames) throws IOException, URISyntaxException {
log("\nGenerating files into: " + outputDir);
// going through all sub-directories in source directory
Path sourceDir = Paths.get(this.loader.getBaseDesc().getRootDir().toURI());
log("Scanning source directory: " + sourceDir);
// ensure the root label file exists
if (!isValid(sourceDir, BASE_LANG))
throw new IOException("root label is missing: " + resolve(sourceDir, BASE_LANG));
// ensure base language (by default, en_US) gets loaded. this may not be required, but for just in case.
this.loader.getSet(BASE_LANG);
AtomicInteger cnt = new AtomicInteger(0);
LanguageProviderFactory.get().getAll().parallelStream().filter(l -> isValid(sourceDir, l)).forEach(l -> {
this.write(l, outputDir, shouldGenerateComment, shouldGenerateAllNames);
cnt.incrementAndGet();
});
log("Done. Generated " + cnt.toString() + " files.");
}
protected void log(String msg) {
logger.log(Level.INFO, msg);
}
protected void error(String msg) {
logger.log(Level.SEVERE, msg);
}
}