github

Github: github.com/zhouyueyued…

introduce

EasyIntern is an internationalization plug-in based on IDEA IDE. Based on the excel internationalization form given by the product, it can flexibly handle the internationalization of the language in the form. The main features are as follows

  • Currently supported<string>The labels and<string-array>The label.
  • The plug-in will default to the values given by the formnameValue, and supports pairsnameValues andvalueValue of editing
  • Special characters such as Spaces and & are replaced by default
  • For text substitution problems, the plugin will automatically extract suspected conflicts by comparing the similarity of a single item before writing to the filenameandvalueLet the user edit in the page, of course you can also directly generate new files, copy and paste, and then in studiostrings-xxIn-file editing

Easyintern origin

In international dictionaries, a large number of newly added and revised international texts are often involved. The common solution is to use scripting languages to write scripts for specific scenarios. The disadvantages are shown below

  • You can’t use a scripting language multiple times.
  • Scripts generally have no specific editing capabilities

I also couldn’t find a project on Github to solve this problem. In view of the above pain points, it is found that idea plug-in mode can be well solved. Just do one thing, use Excel to write internationalization copywriting, and in the plug-in in the subsequent extension can flexibly add support for various formats, even for white users, call the translation engine developed by ourselves, forming an automatic internationalization process is also very convenient.

Easyintern principle

The UI uses the Java Swing-based API provided by IDEA
Read Excel data usingorg.apache.poiA set of Excel reading framework can be

The user can flexibly specify the valid data area and the

tag area in Excel, and then use the above reading framework to transform the UI to display the desired data

At the bottom of similarity calculation, text editing distance is used, as shown in the following figure

The Java-XML conversion uses JAXB parsing, as shown in the following example
  • Build based onstrings.xmlThe model of
package com.youdao.model;

import javax.xml.bind.annotation.*;
import java.util.List;

/ * * *@XmlRootElement(name = "resources") The root element is <resources></resource> *@XmlAccessorTypeFIELD: Maps all fields in this class to XML * XmlAccessType.PROPERTY: Map attributes in this class (get/set methods) to XML * XmlAccessType.PUBLIC_MEMBER: Map all public fields or properties in this class simultaneously to XML (default) * XmlAccessType.NONE: Does not map * *@XmlElement(name = "string") maps the child element * labeled <string> under the root element@XmlAttribute(name = "name") The attribute under the mapping element is name to XML,<string name = "XXX "></string> *@XmlValueThe value under the mapping element, for example, <string name=""> XXX </>, XXX indicates the value of the field under the annotation of the label */
@XmlRootElement(name = "resources")
@XmlAccessorType(XmlAccessType.FIELD)
public class AndroidStringXmlModel {
    @XmlElement(name = "string")
    private List<StringMapModel> stringMapModelList;
    @XmlElement(name = "string-array")
    private List<StringArrayMapModel> stringArrayMapModels;

    @XmlAccessorType(XmlAccessType.FIELD)
    public static class StringMapModel {
        @XmlAttribute(name = "name")
        String name;
        @XmlValue
        String value;
        @XmlAttribute(name = "tools:ignore")
        String toolIgnore;

        public String getName(a) {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getValue(a) {
            return value;
        }

        public String getToolIgnore(a) {
            return toolIgnore;
        }

        public void setToolIgnore(String toolIgnore) {
            this.toolIgnore = toolIgnore;
        }

        public void setValue(String value) {
            this.value = value; }}@XmlAccessorType(XmlAccessType.FIELD)
    public static class StringArrayMapModel {
        @XmlAttribute(name = "name")
        String name;
        List<String> item;

        public String getName(a) {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public List<String> getItems(a) {
            return item;
        }

        public void setItems(List<String> item) {
            this.item = item; }}public List<StringMapModel> getStringMapModelList(a) {
        return stringMapModelList;
    }

    public void setStringMapModelList(List<StringMapModel> stringMapModelList) {
        this.stringMapModelList = stringMapModelList;
    }

    public List<StringArrayMapModel> getStringArrayMapModels(a) {
        return stringArrayMapModels;
    }

    public void setStringArrayMapModels(List<StringArrayMapModel> stringArrayMapModels) {
        this.stringArrayMapModels = stringArrayMapModels;
    }

    public String getXmlnsTools(a) {
        return xmlnsTools;
    }

    public void setXmlnsTools(String xmlnsTools) {
        this.xmlnsTools = xmlnsTools; }}Copy the code
  • The inputstrings.xmlDeserialize the file to a Java Model
fun readStringsXmlByPath(path: String): AndroidStringXmlModel {
    val mJaxb = JAXBContext.newInstance(AndroidStringXmlModel::class.java)
    val unmarshaller = mJaxb.createUnmarshaller()
    val stream = FileInputStream(path)
    val model = unmarshaller.unmarshal(stream) as AndroidStringXmlModel
    return model
}
Copy the code
  • Enter the Java Model, serialized to strings.xml
fun writeModelToXml(model: AndroidStringXmlModel? , path: String, append: kotlin.Boolean) { 
    // To get JAXB's context, you need to pass in the concrete Java bean -> use Student here
    val context = JAXBContext.newInstance(AndroidStringXmlModel::class.java/ / createMarshallerThe instanceval marshaller = context.createMarshaller()
    // Set conversion parameters -> Here is an example of telling the serializer whether to format output
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE)
    // Build the output environment
    val out = FileOutputStream(path, append)
    // Serialize the desired object -> This method returns no value
    marshaller.marshal(model, out)
}
Copy the code

Potholes encountered during development

  • java.lang.ClassCastException: com.sun.org.apache.xerces.internal.parsers.SAXParser cannot be cast to org.xml.sax.XMLReaderThe SAXParser class and the XmlReader class are loaded with two different classloaders

Solution: Search the network fruitless, find the reason as shown in the picture below:

  • Special characters need to be handled in XML parsing