{"id":19777459,"url":"https://github.com/ilib-js/ilib-loctool-xml","last_synced_at":"2025-09-17T23:31:37.175Z","repository":{"id":45403192,"uuid":"413551642","full_name":"iLib-js/ilib-loctool-xml","owner":"iLib-js","description":"Loctool plugin to localize content in XML files","archived":false,"fork":false,"pushed_at":"2023-10-04T04:28:11.000Z","size":128,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-11-06T10:36:47.984Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iLib-js.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2021-10-04T19:08:50.000Z","updated_at":"2021-12-15T15:46:44.000Z","dependencies_parsed_at":"2022-07-21T02:34:51.317Z","dependency_job_id":"cdce70c1-c876-4d81-a040-5309a9beca25","html_url":"https://github.com/iLib-js/ilib-loctool-xml","commit_stats":{"total_commits":48,"total_committers":3,"mean_commits":16.0,"dds":0.04166666666666663,"last_synced_commit":"46cd22d3f3ab0a75033ac5b2a7042058ec1d87f8"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iLib-js%2Filib-loctool-xml","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iLib-js%2Filib-loctool-xml/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iLib-js%2Filib-loctool-xml/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iLib-js%2Filib-loctool-xml/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iLib-js","download_url":"https://codeload.github.com/iLib-js/ilib-loctool-xml/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":233430256,"owners_count":18675058,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T05:25:03.817Z","updated_at":"2025-09-17T23:31:31.717Z","avatar_url":"https://github.com/iLib-js.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\u003e :warning: **Deprecation Notice** :warning:\n\u003e This repository has been deprecated. Please use the corresponding package from the [iLib-js monorepo](https://github.com/iLib-js/ilib-mono) instead.\n\n# ilib-loctool-xml\n\nIlib loctool plugin to parse and localize xml files.\n\nThis plugin can parse and localize xml files by either\nunderstanding the schema via a schema file, or by\ndefaulting to a list of key/value pairs.\n\n# How it Works\n\nThis plugin parses XML files and extracts localizable strings from them. It\ncan write translations of these strings to a copy of the XML file or to a\nseparate file.\n\nThe way the plugin operates is to first convert the XML file to a json object\nusing the [xml-js](https://www.npmjs.com/package/xml-js) package. From there,\na JSON schema is applied to the resulting json, similar to the way that the\nilib-loctool-json plugin works. The JSON schema tells the plugin how to parse\nthe file and which parts to extract for each resource.\n\nExample XML file:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003cproperties\u003e\n    \u003centry key=\"key1\" i18n=\"translator note 1\"\u003esource string 1\u003c/entry\u003e\n    \u003centry key=\"key2\" i18n=\"translator note 2key\" locale=\"en-US\"\u003esource string 2\u003c/entry\u003e\n    \u003centry key=\"key3\"\u003esource string 3\u003c/entry\u003e\n    \u003centry\u003esource string 4\u003c/entry\u003e\n\u003c/properties\u003e\n```\n\nConverting that file to JSON using xml-js, we get:\n\n```json\n{\n    \"_declaration\": {\n        \"_attributes\": {\n            \"version\": \"1.0\",\n            \"encoding\": \"utf-8\"\n        }\n    },\n    \"properties\": {\n        \"entry\": [\n            {\n                \"_attributes\": {\n                    \"key\": \"key1\",\n                    \"i18n\": \"translator note 1\"\n                },\n                \"_text\": \"source string 1\"\n            },\n            {\n                \"_attributes\": {\n                    \"key\": \"key2\",\n                    \"i18n\": \"translator note 2\",\n                    \"comment\": \"foo\"\n                },\n                \"_text\": \"source string 2\"\n            },\n            {\n                \"_attributes\": {\n                    \"key\": \"key3\"\n                },\n                \"_text\": \"source string 3\"\n            },\n            {\n                \"_text\": \"source string 4\"\n            }\n        ]\n    }\n}\n```\n\nIn the above JSON, you can see that the multiple `entry` elements\nfrom the XML became an array of objects in the JSON. There is a special\nproperty `_attributes` that gives the XML attributes on an element.\nThe special property `_text` specifies the plain text inside of\nan XML element.\n\nSee the [xml-js](https://www.npmjs.com/package/xml-js) documentation\nfor more details on how this conversion works.\n\n## Methods of Localizing\n\nXML files are localized in one of the following methods:\n\n1. copy. Make a copy of the entire source file\nand replace values of certain elements or attributes\nin the copy with translations. The copy can be sent to\nany output desired directory using a template.\n\"Copy\" is the default method with which\nlocalized XML files are handled.\n1. sparse. Make a copy of the source file where only\nthe localized elements appear. The copy has the same\nstructure as the original XML file, but only elements\nwhere text is localized appear in the output.\n1. resource. Make a new XML file that only contains\ntranslations in it with a Java properties format.\n\nThe first method, localizing the entire file, has the\nadvantage that you don't need to change your code in\norder to read the translated file. You just need to pick\nthe right file for the current locale and feed it in\nto the existing code.\n\nThe second method is similar to the\nfirst method above, except that it can save space\nbecause all of the non-localizable data in the original\nXML file is not duplicated. For some XML files, the\nnon-localizable parts are considerable.\nIn this case, you would need to load in the English file\nfirst, then mix-in the localized file to override all\nthe localizable strings in order to create the full\ndata structure with all the translations embedded in it.\n\nThe third method, resource files, are similar to many\nprogramming languages in that source strings are extracted,\ntranslated, and then the translations are written\nto a separate, very simply formatted file which should\nbe easy to read and process: Android resource files. An\nAndroid resource file has the following structure:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003cresources\u003e\n    \u003cstring name=\"key1\"\u003etranslation 1\u003c/string\u003e\n    \u003cstring name=\"key2\"\u003etranslation 2\u003c/string\u003e\n    ...\n\u003c/resources\u003e\n```\n\nThis is also similar to XML style Java properties files, although\nwith different element tag names.\n\n## Configuring the Plugin\n\nThe plugin will look for the `xml` property within the `settings`\nof your `project.json` file. The following settings are\nused within the json property:\n\n- schemas: a string naming a directory full of JSON schema files, or\n  an array of strings naming some JSON schema files or directories to\n  load. If the XML file\n  does not fit any of the schema (ie. it does not validate according to\n  any one of the schema), then that file will be skipped and not localized.\n  Schemas files are discussed below.\n- mappings: a mapping between file matchers and an object that gives\n  info used to localize the files that match it. This allows different\n  XML files within the project to be processed with different schema.\n  The matchers are\n  a [micromatch-style string](https://www.npmjs.com/package/micromatch),\n  similar to the the `includes` and `excludes` section of a\n  `project.json` file. The value of that mapping is an object that\n  can contain the following properties:\n    - schema: schema to use with that matcher. The schema is\n      specified using the `$id` of one of the schemas loaded in the\n      `schemas` property above. The default schema is \"properties-schema\"\n      which is given in the previous section.\n    - method: one of \"copy\" or \"sparse\" or \"resource\"\n        - copy: make a copy of the source file and localize the\n          string contents. (This is the default method if not specified\n          explicitly.)\n        - sparse: make a copy of the source file but only\n          include localized strings\n        - resource: write out a resource file format with the\n          translations in it\n    - flavor: the flavor attribute to use for Android resource files.\n      The flavor usually applies to all resources in a particular\n      subdirectory, and corresponds to a particular customer for a\n      white label app. Each resource produced from the source file\n      will have the given flavor. If not specified, there is no\n      flavor attribute for the resources.\n    - localeMap: an output locale map that specifies the mapping\n      between locales that are used internally in the plugin, and the\n      output locale that should be used for constructing\n      the file name of output files. If a locale does not appear in\n      the mapping, it will not be mapped. That is, the original locale\n      will be used to construct the output file name.\n    - template: a path template to use to generate the path to\n      the translated\n      output files. The template replaces strings in square brackets\n      with special values, and keeps any characters intact that are\n      not in square brackets. The default template, if not specified is\n      \"resources/[localeDir]/[filename]\". The plugin recognizes\n      and replaces the following strings in template strings:\n        - [dir] the original directory where the matched source file\n          came from. This is given as a directory that is relative\n          to the root of the project. eg. \"foo/bar/strings.xml\" -\u003e \"foo/bar\"\n        - [filename] the file name of the matching file.\n          eg. \"foo/bar/strings.xml\" -\u003e \"strings.xml\"\n        - [basename] the basename of the matching file without any extension\n          eg. \"foo/bar/strings.xml\" -\u003e \"strings\"\n        - [extension] the extension part of the file name of the source file.\n          etc. \"foo/bar/strings.xml\" -\u003e \"xml\"\n        - [locale] the full BCP-47 locale specification for the target locale\n          eg. \"zh-Hans-CN\" -\u003e \"zh-Hans-CN\"\n        - [language] the language portion of the full locale\n          eg. \"zh-Hans-CN\" -\u003e \"zh\"\n        - [script] the script portion of the full locale\n          eg. \"zh-Hans-CN\" -\u003e \"Hans\"\n        - [region] the region portion of the full locale\n          eg. \"zh-Hans-CN\" -\u003e \"CN\"\n        - [localeDir] the full locale where each portion of the locale\n          is a directory in this order: [langage], [script], [region].\n          eg, \"zh-Hans-CN\" -\u003e \"zh/Hans/CN\", but \"en\" -\u003e \"en\".\n        - [localeUnder] the full BCP-47 locale specification, but using\n          underscores to separate the locale parts instead of dashes.\n          eg. \"zh-Hans-CN\" -\u003e \"zh_Hans_CN\"\n\n\nExample configuration:\n\n```json\n    \"settings\": {\n        \"xml\": {\n            \"schemas\": \"./xml/schemas\",\n            \"mappings\": {\n                \"**/resources.xml\": {\n                    \"schema\": \"http://www.mycompany.com/xml/resources\",\n                    \"method\": \"resource\",\n                    \"template\": \"resources/[localeDir]/resources.xml\"\n                },\n                \"**/config.xml\": {\n                    \"schema\": \"http://www.lge.com/xml/config\",\n                    \"method\": \"sparse\",\n                    \"template\": \"config/config_[localeUnder].xml\",\n                    \"localeMap\": {\n                        \"de-DE\": \"de\",\n                        \"fr-FR\": \"fr\",\n                        \"ja-JP\": \"ja\"\n                    }\n                },\n                \"customer1/src/**/strings.xml\": {\n                    \"schema\": \"http://www.mycompany.com/xml/strings\",\n                    \"method\": \"copy\",\n                    \"flavor\": \"customer1\",\n                    \"template\": \"[dir]/strings.[locale].xml\"\n                }\n            }\n        }\n    }\n```\n\nIn the above example, any file named `resources.xml` will be parsed with the\n`http://www.mycompany.com/xml/resources` schema. The translated resources\nwill be written out to a file named `resources.xml` in a resource file format\n(Android resource files) in a directory named for the locale.\n\nIn the second part of the example, any `config.xml` file will be parsed with\nthe schema `http://www.mycompany.com/xml/config`. Because the method is\n`sparse`, only the parts of the XML file that have translated strings in them will\nappear in the output config files. The output file is sent to the `config`\ndirectory, and the locale used to construct the file name goes through the\nlocale map first. That is, if you localized to the locale \"de-DE\", the locale\nwill be mapped to \"de\" and the file name will come out as \"config/config_de.xml\"\n\nIn the third part of the example, any `strings.xml` file that appears in\nthe `src` directory will be parsed with the schema\n`http://www.mycompany.com/xml/strings`. A full copy of the file, including\nthe parts that were not localized,\nwill be sent to the same directory as the source file. However, the\nlocalized file name will also contain the name of the locale to distinguish\nit from the source file. Also, every resource in the strings.xml file will have\nthe Android flavor \"customer1\".\n\nIf the name of the localized file that the template produces is the same as\nthe source file name, this plugin will throw an exception, the file will not\nbe localized, and the loctool will continue on to the next file.\n\n# Schema Files\n\nThis plugin uses JSON schemas to document how to extract strings from the\nXML files. The JSON schema is applied to the JSON that is produced after\nit is converted from XML by the [xml-js](https://www.npmjs.com/package/xml-js)\npackage.\n\n## Extensions to JSON Schema\n\nIn regular JSON Schemas, there is no built-in way to indicate that the value\nof a particular JSON property is localizable, nor which properties or\nsubproperties of a JSON object form the various parts of a resource.\nHowever, the JSON Schema spec allows for creating extensions\nto the keywords of your JSON schema and specifies that implementation must\nignore any keywords that it does not recognize.\n\nGiven that, this plugin extends the JSON schema with a `localizable` keyword\nand a `localizableType` keyword.\n\n### The `localizable` Keyword\n\nThis plugin recognizes the new `localizable` keyword as an extension to\nindicate that parts of a XML element or its\nsub-elements should be localized. By default, elements in XML (and\ntherefore objects in JSON after it is converted) are not localizable\nunless explicitly specified using the `localizable` keyword.\n\nIn an XML document, the key of a resource, the English source text, the\ntranslator's comment, and the source locale may all be specified in\nseparate XML elements, sub-elements, or attributes within a specific\nparent element. They may also be specified using the element name itself\nor the names of sub-elements. The `localizable` keyword should only be\nspecified for the parent element that contains all the information for a\nsingle resource. That is, it should not be specified for the sub-elements.\nThe reason is that the plugin will start constructing a new resource\neach time it finds a `localizable` keyword, and then assign various\nparts and subparts of the element to the fields of that new resource.\n\nThe `localizable` keyword is ignored for null or undefined values. For\nthe primitive types string, integer, number, or boolean values, the value\nis directly localizable. Each localizable element can result in a\nloctool resource which becomes a translation unit in the xliff file\nthat the translators may localize.\n\n### The `localizableType` Keyword\n\nBecause the parts of a resource may be specified in sub-elements or\nattributes of a parent element, there needs to be a way to specify which\nparts of which sub-elements or attributes should be assigned to the\nfields of the new resource. That is, there needs to be a mapping\nbetween subparts of the element and the resource fields.\n\nThis mapping is given in the `localizableType` keyword.\n\nSuppose we have the following xml:\n\n```xml\n\u003cresources\u003e\n    \u003cstring key=\"keyname\"\u003e\n        \u003ccomment\u003eThis is the translator's comment\u003c/comment\u003e\n        \u003cvalue\u003eThis is the source text in English\u003c/value\u003e\n    \u003c/string\u003e\n\u003c/resources\u003e\n```\n\nIn the above example, the entire `string` element and its subparts\ncontains information for just one resource. The key is given in the\nvalue of the \"key\" attribute \"keyname\" on the `string` element.\nThe translator's comment is taken\nfrom the text inside of the `comment` element. The source text is\ntaken from the text inside of the `value` element. The last resource\nfield, locale, is assumed to be the same as the source locale because\nno locale was specified in the XML. (This is common.)\n\n#### Resource Types\n\nThe loctool supports three resource types:\n\n1. string - single strings to translate. (This is the default)\n1. array - an array of strings to translate where order matters\n1. plural - a string that has different forms depending on the number\nof a particular item\n\nThe type of resource created for an XML element is specified using\nthe `type` property within the `localizableType`.\n\nExample JSON schema snippet:\n\n```json\n\"properties\": {\n    \"strtype\": {\n        \"type\": \"string\",\n        \"localizable\": true,\n        \"localizableType\": {\n            \"type\": \"array\"\n        }\n    }\n}\n```\nIn this example, the \"strtype\" XML element is localizable, and forms\nan array resource type.\n\nIf the only property inside of a `localizableType` object is the type,\nthen it can be notated as a string instead of an object. The following\nis equivalent to the schema above:\n\n```json\n\"properties\": {\n    \"strtype\": {\n        \"type\": \"string\",\n        \"localizable\": true,\n        \"localizableType\": \"array\"\n    }\n}\n```\n\n#### Resource Fields\n\nResources contain the following fields which can be extracted from the\nXML:\n\n- \"key\" - the unique key of this resource\n- \"source\" - the source text of this resource\n- \"comment\" - a comment for the translator to give more context\n- \"locale\" - the source locale of this resource\n- \"category\" - the plural category for a plural resource\n- \"context\" - the context field for this resource\n- \"formatted\" - for Android resources, the formatted attribute of a resource\n\n#### Element Parts\n\nThe various element parts that can be assigned to resource fields are:\n\n- \"_value\" - the textual value contained within this element\n- \"_element\" - the element name of the current element. eg. \"\u0026lt;x\u003e\" has\nthe element name \"x\".\n- \"_path\" - the XML path to the current element. This is the\nname of all the elements in the tree above the current element\nplus the name of the current element, all separated by slashes. This\nis similar to an xpath selector without any predicates or wildcards\nin it.\n- \"_pathname\" - The full path name of the file from the root of the project\nto the current file\n- \"_basename\" - The basename of the current file without the extension\n\n#### Assigning Element Parts to Resource Fields\n\nUse properties in the `localizableType` keyword to specify a\nmapping between an element part and a resource field.\n\nLet's take the example above of a `string` element that has an attribute\nwhich is the key, and two sub-elements which give the translator's\ncomment and the source text. The schema is specified like this:\n\n```json\n    \"string\": {\n        \"type\": \"object\",\n        \"localizable\": true,\n        \"properties\": {\n            \"_attributes\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"key\": {\n                        \"type\": \"string\",\n                        \"localizableType\": {\n                            \"key\": \"_value\"\n                        }\n                    }\n                }\n            },\n            \"comment\": {\n                \"_text\": {\n                    \"type\": \"string\",\n                    \"localizableType\": {\n                        \"comment\": \"_value\"\n                    }\n                }\n            },\n            \"value\": {\n                \"_text\": {\n                    \"type\": \"string\",\n                    \"localizableType\": {\n                        \"source\": \"_value\"\n                    }\n                }\n            }\n        }\n    }\n```\n\nIn this example, the `string` element has an attribute called\n`key`. When the XML file is converted to json, the values of\nXML attributes are notated in a special property called `_attributes`\nunder the element that contained those attributes. We can use\nregular JSON schema syntax to specify those attributes. In this\ncase, we are picking out the value of the `key` attribute\n(notated as \"_value\") and assigning that to the \"key\" field of\nthe resource.\n\nIn the second section, the `comment` element lives under the\n`string` element, and the text value within that element (given\nas \"_value\") should be assigned to the \"comment\" field of the\nelement.\n\nIn the last section, the `value` element lives under the\n`string` element. The text within the `value` element is\nassigned to the source text of the resource.\n\nSometimes the sub-element of a parent element contains information\nthat is not related to translation. These sub-elements or attributes\nshould be preserved but not extracted for translation.\n\nIt is not recommended to assign an element part to a field multiple\ntimes, but if it happens, the last one processed is the one that will\nbe used.\n\n#### Array Resources\n\nWhen the `localizable` keyword is given for an element that contains\nan array, every item in the array is localizable and must be of a\nprimitive type (string, integer, number, or boolean). If any array\nentries are not of a primitive type, an exception will be thrown and\nthe localization will be halted.\n\nExample XML:\n\n```xml\n    \u003cstring-array id=\"foo\"\u003e\n        \u003citem\u003eoctagon\u003c/item\u003e\n        \u003citem\u003enonagon\u003c/item\u003e\n        \u003citem\u003edecagon\u003c/item\u003e\n        \u003citem\u003eundecagon\u003c/item\u003e\n        \u003citem\u003edodecagon\u003c/item\u003e\n    \u003c/string-array\u003e\n```\n\nThis is specified as follows in the schema:\n\n```json\n    \"string-array\": {\n        \"type\": \"object\",\n        \"localizable\": true,\n        \"localizableType\": \"array\",\n        \"properties\": {\n            \"_attributes\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"id\": {\n                        \"type\": \"string\",\n                        \"localizableType\": {\n                            \"key\": \"_value\"\n                        }\n                    }\n                }\n            },\n            \"item\": {\n                \"type\": \"string\",\n                \"localizableType\": {\n                    \"source\": \"_value\"\n                }\n            }\n        }\n    }\n```\n\nNote that multiple `item` elements appear inside of the `string-array`\nelement in the example XML. The `localizableType` assigns the text value\nof each item to the \"source\" field in the resource. In this way, each one\nof them is added as an entry in the array in the order they appeared\nin the original XML file.\n\n#### Plural Resources\n\nWhen the `localizable` keyword is given for an object type, that object\nmay encode a plural string. Plurals are not possible if the JSON schema\nfor the current element is a primitive type like string or number.\nThe `localizableType` keyword should contain a type property with\nthe value \"plural\" to specify that.\n\nIn order to fully specify a plural, elements or attributes under that\nmain element must contain a string for various plural categories.\nTo specify which category to use, the `localizableType` keyword contains a\n`category` property.\n\nThe list of allowed plural categories are defined in the\n[Unicode plural rules](http://cldr.unicode.org/index/cldr-spec/plural-rules):\n\n- zero\n- one\n- two\n- few\n- many\n- other\n\nThe \"one\" and the \"other\" property are required for source files in English.\nOther languages will have different combinations of plural categories.\n\nExample XML:\n\n```xml\n    \u003cstring-plural id=\"unique\"\u003e\n        \u003csingular\u003eThis is the singular string\u003c/singular\u003e\n        \u003cplural\u003eThis is the plural string\u003c/plural\u003e\n    \u003c/string-plural\u003e\n```\n\nExample JSON schema file for the above XML:\n\n```json\n        \"string-plural\": {\n            \"type\": \"object\",\n            \"localizable\": true,\n            \"localizableType\": \"plural\",\n            \"properties\": {\n                \"_attributes\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"id\": {\n                            \"type\": \"string\",\n                            \"localizableType\": {\n                                \"key\": \"_value\"\n                            }\n                        }\n                    }\n                },\n                \"singular\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"_text\": {\n                            \"type\": \"string\",\n                            \"localizableType\": {\n                                \"source\": \"_value\",\n                                \"category\": \"one\"\n                            }\n                        }\n                    }\n                },\n                \"plural\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"_text\": {\n                            \"type\": \"string\",\n                            \"localizableType\": {\n                                \"source\": \"_value\",\n                                \"category\": \"other\"\n                            }\n                        }\n                    }\n                }\n            }\n        }\n```\n\nThe value of the \"_value\" element is assigned to the \"source\" field of the\nresource in both the singular and plural elements. The difference is that\nthey have different categories, so they do not conflict.\n\nNote that the way the xml to json converter works is that a single subitem\nof a node will just contain that subitem, but if there are multiple subitems\nin a node, it will have an array of them.\n\nExample: plurals with \"item\" subelements as in Android resource files\n\n```xml\n    \u003cplurals name=\"unique\"\u003e\n        \u003citem quantity=\"one\"\u003eThis is the singular string\u003c/item\u003e\n        \u003citem quantity=\"other\"\u003eThis is the plural string\u003c/item\u003e\n    \u003c/plurals\u003e\n```\n\nThe above would convert into the following json:\n\n```json\n    \"plurals\": {\n        \"_attributes\": {\n            \"name\": \"unique\"\n        },\n        \"item\": [\n            {\n                \"_attributes\": {\n                    \"quantity\": \"one\"\n                },\n                \"_text\": \"This is the singular string\"\n            },\n            {\n                \"_attributes\": {\n                    \"quantity\": \"other\"\n                },\n                \"_text\": \"This is the plural string\"\n            }\n        ]\n    }\n```\n\nBut, if there were only one item in the plural, like this:\n\n\n```xml\n    \u003cplurals name=\"unique\"\u003e\n        \u003citem quantity=\"other\"\u003eThis is the plural string\u003c/item\u003e\n    \u003c/plurals\u003e\n```\n\nThen the above would convert into the following json:\n\n```json\n    \"plurals\": {\n        \"_attributes\": {\n            \"name\": \"unique\"\n        },\n        \"item\": {\n            \"_attributes\": {\n                \"quantity\": \"other\"\n            },\n            \"_text\": \"This is the plural string\"\n        }\n    }\n```\n\nIn the first example, the \"item\" node is an array of objects. In the second,\nit is a single object.\n\nCare must be taken to account for this in your schema. Here is how you\nmight make a useful schema using the \"anyOf\" keyword in your json schema\nto parse an array of plurals or just a single plural. These share the\ndefinition of a plural and of an item in the \"$defs\" keyword by using \"$ref\"\nto refer to them:\n\n```json\n    \"$defs\": {\n        \"plural-item-type\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"_attributes\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"quantity\": {\n                            \"type\": \"string\",\n                            \"localizableType\": {\n                                \"category\": \"_value\"\n                            }\n                        }\n                    }\n                },\n                \"_text\": {\n                    \"type\": \"string\",\n                    \"localizableType\": {\n                        \"source\": \"_value\"\n                    }\n                }\n            }\n        },\n        \"plural-type\": {\n            \"type\": \"object\",\n            \"localizable\": true,\n            \"localizableType\": \"plural\",\n            \"properties\": {\n                \"_attributes\": {\n                    \"$ref\": \"#/$defs/resource-attribute-type\"\n                },\n                \"item\": {\n                    \"anyOf\": [\n                        {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/$defs/plural-item-type\"\n                            }\n                        },\n                        {\n                            \"$ref\": \"#/$defs/plural-item-type\"\n                        }\n                    ]\n                }\n            }\n        }\n    },\n    \"properties\": {\n        \"plurals\": {\n            \"anyOf\": [\n                {\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/$defs/plural-type\"\n                    }\n                },\n                {\n                    \"$ref\": \"#/$defs/plural-type\"\n                }\n            ]\n        }\n    }\n```\n\n#### Plural Templates\n\nThe above XML has two strings, one for singular and one for plural. What\nabout the other categories? In other languages, the plurals may use other\ncategories that are not used in the source language (English?), so how do\nwe notate that in the translated file? In this case, the plugin\nneeds to know which elements or attributes to use for those categories when\nwriting out the localized files. The solution is plural templates.\n\nPlural templates allow you to specify the attributes and children of a\nplural element and with special keywords that get replaced with values\nfrom the original source. For example, you can use \"[_key]\" to specify\nthat the unique key of a resource should be placed into the template\nat this point.\n\nExample. A full plural might look something like this in an Android\nresource file:\n\n```xml\n\u003cplurals name=\"plural 1\"\u003e\n    \u003citem quantity=\"one\"\u003eThis is the singular.\u003c/item\u003e\n    \u003citem quantity=\"other\"\u003eThis is the plural.\u003c/item\u003e\n\u003c/plural\u003e\n```\n\nFor English, we would use the \"one\" and \"other\" categories as specified\nabove, but for Russian, we need to use the \"one\", \"few\", and \"other\"\ncategories, so we need a plural template to generate those.\n\nThe plural templates are specified in the schema definition inside the\n`$defs` keyword. Here is a full plural template for the above\nplural xml in the \"$defs\":\n\n```json\n    \"$defs\": {\n        \"templates\": {\n            \"plurals\": {\n                \"default\": {\n                    \"_attributes\": {\n                        \"name\": \"[_key]\"\n                    },\n                    \"[_forEachCategory]\": {\n                        \"item\": {\n                            \"_attributes\": {\n                                \"quantity\": \"[_category]\"\n                            },\n                            \"_text\": \"[_target]\"\n                        }\n                    }\n                }\n            }\n        }\n    }\n```\n\nInside of \"$defs/templates/plurals\" is the \"default\" property. This is the\nname of the template. The schema allows you to have multiple templates,\none for each type of plural you need to support. The name \"default\" is used\nwhen no specific name is given to the template and will be used as the\ndefault template.\n\nInside the template, we specify the name attribute, which takes the value\nof the resource's key, and we have a special property \"[_forEachCategory]\".\nThis specifies that all of the children underneath it get replicated for\nevery category in the translation of a plural resource. That is, in Russian\nthat part of the template gets replicated 3 times, one each for \"one\",\n\"few\", and \"other\".\n\nInside the \"forEachCategory\" property, other strings get replaced with\nvalues from the resource. Here is a table of the replacement values:\n\n|  *label* |  *value* |\n|---------|---------|\n| _key | The unique key of the resource. |\n| _target | The target translation of the resource for the category |\n| _category | The category of the translation |\n| _locale | The locale specifier for the translation |\n| _comment | The translator's comment for the resource |\n| _context | The context attribute for the resource |\n| _flavor  | The flavor attribute for the resource (Android) |\n| _formatted  | The formatted attribute for the resource (Android) |\n\nLet's say we have a plural resource that should look like this:\n\n```xml\n    \u003cstring-plural id=\"unique\"\u003e\n        \u003cone\u003eThis is the singular string\u003c/one\u003e\n        \u003ctwo\u003eThis is the dual string\u003c/two\u003e\n        \u003cmany\u003eThis is the many plural string\u003c/many\u003e\n        \u003cother\u003eThis is the plural string\u003c/other\u003e\n    \u003c/string-plural\u003e\n```\n\nThat is, the subelements of \"string-plural\" are named for the\ncategory instead of having an attribute that names the category.\nIn this case, we can use the \"[_category]\" substitution\nin property name inside the template, like this:\n\n```json\n    \"$defs\": {\n        \"templates\": {\n            \"plurals\": {\n                \"default\": {\n                    \"_attributes\": {\n                        \"id\": \"[_key]\"\n                    },\n                    \"[_forEachCategory]\": {\n                        \"[_category]\": {\n                            \"_text\": \"[_target]\"\n                        }\n                    }\n                }\n            }\n        }\n    }\n```\n\nThe \"[_forEachCategory]\" keyword can also appear inside of\nattributes if necessary.\n\n## Default Schema\n\nIn the absence of any schema information, a default\nschema will be applied. The default schema is for an Android\nresource file. That is, the\nplugin will assume that the XML file is a simple object\nwhere the root tag is \"resources\" and the subtags are\n\"string\", or \"plural\", or \"string-array\". The text inside the tag\nis the source language string.\n\nExample:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003cresources\u003e\n    \u003cstring name=\"key1\" i18n=\"translator note 1\"\u003esource string 1\u003c/string\u003e\n    \u003cstring name=\"key2\" i18n=\"translator note 2\"\u003esource string 2\u003c/string\u003e\n    ...\n\u003c/resources\u003e\n```\n\nEssentially, this means that we assume that the file has\nthe following json-XML schema:\n\n```json\n{\n    \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n    \"$id\": \"android-resource-schema\",\n    \"type\": \"object\",\n    \"description\": \"An Android resource file\",\n    \"$defs\": {\n        \"resource-attribute-type\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"name\": {\n                    \"type\": \"string\",\n                    \"localizableType\": {\n                        \"key\": \"_value\"\n                    }\n                },\n                \"i18n\": {\n                    \"type\": \"string\",\n                    \"localizableType\": {\n                        \"comment\": \"_value\"\n                    }\n                },\n                \"locale\": {\n                    \"type\": \"string\",\n                    \"localizableType\": {\n                        \"locale\": \"_value\"\n                    }\n                }\n            }\n        },\n        \"plural-item-type\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"_attributes\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"quantity\": {\n                            \"type\": \"string\",\n                            \"localizableType\": {\n                                \"category\": \"_value\"\n                            }\n                        }\n                    }\n                },\n                \"_text\": {\n                    \"type\": \"string\",\n                    \"localizableType\": {\n                        \"source\": \"_value\"\n                    }\n                }\n            }\n        },\n        \"plural-type\": {\n            \"type\": \"object\",\n            \"localizable\": true,\n            \"localizableType\": \"plural\",\n            \"properties\": {\n                \"_attributes\": {\n                    \"$ref\": \"#/$defs/resource-attribute-type\"\n                },\n                \"item\": {\n                    \"anyOf\": [\n                        {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/$defs/plural-item-type\"\n                            }\n                        },\n                        {\n                            \"$ref\": \"#/$defs/plural-item-type\"\n                        }\n                    ]\n                }\n            }\n        },\n        \"string-type\": {\n            \"type\": \"object\",\n            \"localizable\": true,\n            \"localizableType\": \"string\",\n            \"properties\": {\n                \"_attributes\": {\n                    \"$ref\": \"#/$defs/resource-attribute-type\"\n                },\n                \"_text\": {\n                    \"type\": \"string\",\n                    \"localizableType\": {\n                        \"source\": \"_value\"\n                    }\n                }\n            }\n        },\n        \"array-item-type\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"_text\": {\n                    \"type\": \"string\",\n                    \"localizableType\": {\n                        \"source\": \"_value\"\n                    }\n                }\n            }\n        },\n        \"array-type\": {\n            \"type\": \"object\",\n            \"localizable\": true,\n            \"localizableType\": \"array\",\n            \"properties\": {\n                \"_attributes\": {\n                    \"$ref\": \"#/$defs/resource-attribute-type\"\n                },\n                \"item\": {\n                    \"anyOf\": [\n                        {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/$defs/array-item-type\"\n                            }\n                        },\n                        {\n                            \"$ref\": \"#/$defs/array-item-type\"\n                        }\n                    ]\n                }\n            }\n        },\n        \"templates\": {\n            \"plurals\": {\n                \"default\": {\n                    \"_attributes\": {\n                        \"name\": \"[_key]\",\n                        \"i18n\": \"[_comment]\"\n                    },\n                    \"[_forEachCategory]\": {\n                        \"item\": {\n                            \"_attributes\": {\n                                \"quantity\": \"[_category]\"\n                            },\n                            \"_text\": \"[_target]\"\n                        }\n                    }\n                }\n            }\n        }\n    },\n    \"properties\": {\n        \"resources\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"string\": {\n                    \"anyOf\": [\n                        {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/$defs/string-type\"\n                            }\n                        },\n                        {\n                            \"$ref\": \"#/$defs/string-type\"\n                        }\n                    ]\n                },\n                \"plurals\": {\n                    \"anyOf\": [\n                        {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/$defs/plural-type\"\n                            }\n                        },\n                        {\n                            \"$ref\": \"#/$defs/plural-type\"\n                        }\n                    ]\n                },\n                \"string-array\": {\n                    \"anyOf\": [\n                        {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/$defs/array-type\"\n                            }\n                        },\n                        {\n                            \"$ref\": \"#/$defs/array-type\"\n                        }\n                    ]\n                }\n            }\n        }\n    }\n}\n```\n\n### One Last Note\n\nGetting the schema definitions is often pretty difficult. If your schema\nis not causing the plugin to extract any resources, it's probably your\nschema definition. Try changing the log4js.json inside of the loctool to\nset the level for the various `loctool.lib.ResourceFactory*` settings\nto \"debug\" or \"trace\". This will give some clue about whether resources\nare being created and if so, what types are they.\n\nAlso, it is a good idea to convert your XML to json format and examine\nthat before writing the JSON schema to parse it. To convert it, use the\nauxiliary program `convertToJson.js` that comes along with this plugin.\n\n```\nUsage: node node_modules/ilib-loctool-xml/convertToJson.js myfile.xml\n```\n\nThat will output the json to the standard output.\n\n## Not an XML Validator\n\nPlease note that this plugin is *not* a XML schema validator, though it\nworks in similar ways. If the XML being parsed does not conform to\nthe given schema, no errors or exceptions will be thrown. The plugin\nwill print some warnings to the output for the engineers to diagnose\nwhat is going on, but the strings will merely not be extracted/localized\nas expected.\n\n## License\n\nThis plugin is license under Apache2. See the [LICENSE](./LICENSE)\nfile for more details.\n\n## Release Notes\n\n### v1.1.2\n\n- update dependencies\n- use the loctool's logger instead of its own\n\n### v1.1.1\n\n- Add unit tests to verify parsing the file name with locale\n- Updated the way that the plugin decides which files to handle\n\n### v1.1.0\n\n- Add support for new replacements and tags\n    - In parsing the resources, you can now extracted the\n      \"formatted\", \"context\", and the \"flavor\" attributes for a resource.\n    - In creating a plural template, you can now use those three\n      attributes.\n    - When getting the value of an attribute, you can now use the\n      \\_pathname and \\_basename values, which will convert into the full\n      path name of the file, and the base name of the file.\n- Make sure required properties exist in the localization\n    - If a property is required, it should exist in the localized\n      file, even if it does not exist in the source file. This\n      allows for localization of empty strings or missing source\n      strings.\n- fixed android resource schema file which was missing from the package\n- added localeMap to the configuration of a mapping\n\n### v1.0.0\n\n- initial version\n- support JSON schema style parsing of XML and also a default schema\n  (Java properties files)\n- support for plural templates\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filib-js%2Filib-loctool-xml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Filib-js%2Filib-loctool-xml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filib-js%2Filib-loctool-xml/lists"}