{"id":19777500,"url":"https://github.com/ilib-js/ilib-resbundle","last_synced_at":"2025-08-30T18:23:35.460Z","repository":{"id":62186378,"uuid":"534529240","full_name":"iLib-js/ilib-resbundle","owner":"iLib-js","description":"A resource bundle/string translation class","archived":false,"fork":false,"pushed_at":"2023-11-26T18:52:06.000Z","size":130,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-01-11T01:49:53.086Z","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,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-09-09T06:41:34.000Z","updated_at":"2022-10-26T16:23:52.000Z","dependencies_parsed_at":"2024-11-12T05:35:51.806Z","dependency_job_id":null,"html_url":"https://github.com/iLib-js/ilib-resbundle","commit_stats":{"total_commits":18,"total_committers":2,"mean_commits":9.0,"dds":0.05555555555555558,"last_synced_commit":"a17016b2728c87fad9391738021943b360ae0c52"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iLib-js%2Filib-resbundle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iLib-js%2Filib-resbundle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iLib-js%2Filib-resbundle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iLib-js%2Filib-resbundle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iLib-js","download_url":"https://codeload.github.com/iLib-js/ilib-resbundle/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241107734,"owners_count":19910975,"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:17.515Z","updated_at":"2025-02-28T05:50:37.777Z","avatar_url":"https://github.com/iLib-js.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ilib-resbundle\n\nLoad bundles of translated strings.\n\n## Installation\n\n```\nnpm install ilib-resbundle\n\nor\n\nyarn add ilib-resbundle\n```\n\n## Quick Start\n\nIn `myapp/src/example.js` file:\n\n```javascript\nimport ResBundle from 'ilib-resbundle';\n\n[...]\n    const rb = new ResBundle({\n        name: \"strings\",\n        locale: appLocale\n    });\n\n    const buttonLabel = rb.getString(\"Send now\");\n\n    const dialogBodyStr = rb.getString(\"one#There is {count} invitation|#There are {count} notifications\");\n    const dialogBody = dialogBodyStr.formatChoice(count);\n```\n\nIf your app's locale was, let's say, German, then in `myapp/locale/de/strings.json` file:\n\n```\n{\n    \"Send now\": \"Jetzt schicken\",\n    \"\"one#There is {count} invitation|#There are {count} invitations\": \"one#Es gibt {count} Einladung|#Es gibt {count} Einladungen\"\n}\n```\n\nIn your `myapp/package.json` file (only the relevant parts are shown):\n\n```\n{\n    \"dependencies\": {\n        \"loctool\": \"^2.18.0\"\n    },\n    \"scripts\": {\n        \"localize\": \"loctool\"     \u003c- most of the settings come from the project.json (see below)\n    }\n}\n```\n\nIn your `myapp/project.json` file, which configures the loctool (only the relevant parts are shown):\n\n```\n{\n    \"name\": \"myapp\",\n    \"plugins\": [ \"ilib-loctool-javascript\" ],\n    \"resourceDirs\": {\n        \"javascript\": \"locale\"\n    },\n    \"resourceFileTypes\": {\n        \"javascript\":\"ilib-loctool-javascript-resource\"\n    },\n    \"settings\": {\n        \"xliffsDir\": \"./xliffs\",\n        \"locales\":[\n            \"ko-KR\",\n            \"de-DE\"\n        ]\n    }\n}\n```\n\n## API Reference\n\nYou can see the [generated API reference docs](./docs/ilibResBundle.md)\nfor full details.\n\n## Translation with ResBundle\n\nAt the core of any globalization effort is the translation of text. Many people\nerroneously think that translation is all there is to globalization, and they\ndon’t know about the internationalization of code or the localization of other,\nnon­text resources. The fact that you are reading this documentation means that\nyou probably already know better than that! This section, however, will focus\non the translation support in iLib. Other ilib libraries implement more of the\nglobalization functionality that ilib supports.\n\nTranslating Javascript strings is often done with some ad­hoc methods such as\ncreating homebrew classes to load strings, or even creating multiple copies of\nthe Javascript source files, one for each language. Additionally, there are some\npopular libraries that do this task for you, but mostly they exist without the\necosystem around them. Without tools, the strings would have to be extracted by\nhand, which is error-prone and tedious. iLib-resbundle allows your engineers\nwrite their code as normal with all strings in the source language, and allow\nthe tools to do the tedious work.\n\nFortunately, ilib-resbundle is indeed part of a whole ecosystem of internationalization\ntools. First and foremost is the [loctool](https://github.com/ilib-js/loctool)\nwhich can extract strings not only from Javascript sources but also from any type\nof source or resource file with the right plugins.\n\nWith the loctool, strings can be extracted from the JS or HTML source files (or\nany other type!), translated, and then re­inserted into your project by writing\nthem with the appropriate resource file format (json) for the ResBundle class to read.\nThen, using the ResBundle class, you can load the translated strings, format them,\nand display them properly to the user.\n\nThe entire translation cycle using the loctool is described nicely in the\n[documentation for the loctool](https://github.com/iLib-js/loctool#running-the-tool).\n\n### Using ResBundle in Nodejs Apps\n\nUnder nodejs, the resource files written out by the loctool can read directly and\nsynchronously using the ResBundle class:\n\n```javascript\nimport ResBundle from 'ilib-resbundle';\n\n[...]\n    const rb = new ResBundle({\n        name: \"strings\",\n        locale: appLocale\n    });\n\n    const buttonLabel = rb.getString(\"Send now\");\n```\n\nThe ResBundle options include a name option that gives the base name of the file to\nload. In the example above, that is \"strings.json\". (The \".json\" gets appeneded\nautomatically). The locale option specifies the locale for which to load the\ntranslations, and can be given as a string locale specifier or as an instance of\nthe ilib `Locale` class.\n\nUsing the regular constructor above results in synchronous loading of the files.\nSynchronous loading is only supported on certain platforms such as nodejs. If\nyou would like to operate in an asynchronous manner, you can do that as well,\neven on nodejs:\n\n```javascript\nimport ResBundle from 'ilib-resbundle';\n\n[...]\n    const promise = ResBundle.create({\n        name: \"strings\",\n        locale: appLocale\n    });\n\n    promise.then(rb =\u003e {\n        const buttonLabel = rb.getString(\"Send now\");\n    });\n```\n\nAs with all ilib ESM classes, there is a static `create()` method which returns a promise\nto load the files. The \"accept\" result of the promise is the ResBundle instance. After\nthe resource bundle has been loaded, all of the translations are cached in the ResBundle\ninstance. This means you can call `rb.getString()` to get a string synchronously for\nas many strings you like without needing to load any more files.\n\n### Using ResBundle on Web Pages\n\nIf you plan to use a bundler such as Webpack, see the section below. For this section,\nwe will simply include all of the possible translations into a page. This works nicely\nor small pages and sites that do not have many strings and do not support many locales.\nIf you have a large page/site, or if your app supports many locales and you want to\nreduce the memory footprint or loading time of your web pages, you will want to use the\nasynchronous dynamic load support of Webpack in the section below.\n\nFirst, run the loctool and generate a number of translated files. In this example, we\ngenerated translations for German, French, Spanish, and the root is English:\n\n```\nmyapp/\n  locale/\n    strings.json     (this is the root)\n    de/\n      strings.json\n    fr/\n      strings.json\n    es/\n      strings.json\n```\n\nThen, in the Javscript module, you would do something like this:\n\n```javascript\nimport rootTranslations from '../locale/strings.json';\nimport germanTranslations from '../locale/de/strings.json';\nimport frenchTranslations from '../locale/fr/strings.json';\nimport spanishTranslations from '../locale/es/strings.json';\nimport { LocaleData } from 'ilib-localedata';\n\n// explicitly save the data in the cache so ResBundle can find it\nLocaleData.cacheData({\n    \"root\": {\n        \"strings\": rootTranslations // \"strings\" is the base name in the constructor\n    },\n    \"de\": {\n        \"strings\": germanTranslations\n    },\n    \"fr\": {\n        \"strings\": frenchTranslations\n    },\n    \"es\": {\n        \"strings\": spanishTranslations\n    }\n});\n\n\nconst rb = new ResBundle({\n    name: \"strings\",\n    locale: appLocale\n});\n```\n\nNote that we can use the ResBundle class synchronously, even in a web page, because the data\nis already loaded and inserted into the cache, so no files need to be loaded. The ResBundle\nclass uses the LocaleData class to load and cache its data.\n\n### Using ResBundle in Webpack\n\nTo use the ResBundle class inside of a webpack bundle, you can do the same trick as in the\nlast section above where the translations are synchronously loaded and then explicitly cached\ninto the LocaleData cache. If your app has many strings or is translated to many locales, you\ndefinitely do not want to do that because your webpack bundle will become very large and\nbloated. Instead, you can take advantage of webpack's ability to dynamically load chunks\nof files.\n\nNow, ilib data is encoded in many different files, especially if you have many locales.\nEach ilib library can come with its own locale data, and your own app also has locale data in\nthe form of translated strings. To do dynamic loading of all this locale data in webpack,\nyou must first use the [ilib-assemble](https://github.com/ilib-js/ilib-assemble) tool to\nassemble all of it into a minimal set of files, one per locale. That way, when the locale\ndata is need for a particular locale, then only one file is loaded, and it comes with all\nthe data needed for that locale across all ilib classes. This helps to minimize the costly\nhttp requests or file load requests at the expense of somewhat larger files.\n\nTo use `ilib-assemble`, add a script to your package.json:\n\n```\n{\n    \"devDependencies\": {\n        \"ilib-assemble\": \"^1.0.2\"\n    },\n    \"scripts\": {\n        \"assemble\": \"ilib-assemble --localefile ./locales.json --resources locale assembled src\"\n    }\n}\n```\n\nThe above works when your translated files are in a directory called `locale`, the output should\ngo to a directory called `assembled` and the sources for your app are located in a directory\ncalled `src`. The option `--resources` names the directory containing your translated resource\nfiles that need to be included into the rest of the locale data.\n\nThe `locales.json` file documents the locales that your app supports. It has a very simple format:\n\n```\n{\n    \"locales\": [ \"en-US\", \"fr-FR\", \"de-DE\", \"es-ES\" ]\n}\n```\n\nOnce you run `npm run assemble`, the `assembled` directory will be created and populated\nwith javascript files named for the locale which contain all of the locale data for all ilib\nlibraries and the translations of your app for that locale. These files form a minimal set\nof locale data.\n\n```\nmyapp/\n  assembled/\n    root.js\n    fr-FR.js   \u003c- these contain ilib library locale data + your translations\n    de-DE.js\n    es-ES.js\n```\n\nSee the [documentation of ilib-assemble](https://github.com/ilib-js/ilib-assemble) for more details\non how this works.\n\nIn order to include all of those files into your webpack bundle and to get the `LocaleData`\nclass to see them, you will need to change your webpack.config.js file to add a `calling-module`\nalias that points at the `assembled` directory:\n\n```\n{\n    externals: {\n        \"./NodeLoader.js\": \"NodeLoader\",\n        \"./QtLoader.js\": \"QtLoader\",\n        \"./RhinoLoader.js\": \"RhinoLoader\",\n        \"./NashornLoader.js\": \"NashornLoader\",\n        \"./RingoLoader.js\": \"RingoLoader\",\n        \"log4js\": \"log4js\",\n        \"nodeunit\": \"nodeunit\"\n    },\n    resolve: {\n        alias: {\n            \"calling-module\": path.join(path.dirname(module.id), \"assembled\")\n        }\n    }\n}\n```\n\nThe way it works is that the LocaleData class will include every file it finds in the\n`calling-module` directory as a separate chunk that will be loaded dynamically when\nneeded. (ie. the first time you create an instance of an ilib class). The\n`calling-module` should point to the same one that you specify as a parameter to\nthe `ilib-assemble` tool in your `package.json`.\n\nIn your module, you can ensure that all of the strings have been loaded into the cache\nfirst and then you can use all ilib classes synchronously thereafter in the rest of\nyour app. In `index.js`:\n\n```javascript\nimport { LocaleData } from 'ilib-localedata';\nimport ResBundle from 'ilib-resbundle';\n\n\nLocaleData.ensureLocale(theCurrentLocale).then(data =\u003e {\n    const rb = new ResBundle({\n        name: \"strings\",\n        locale: theCurrentLocale\n    });\n\n    const str = rb.getString(\"my string\");\n});\n```\n\n## String Loading\n\nThe main workhorse method of the ResBundle class is `getString()`. This searches for a\ntranslation of the given source string and returns it.\n\nTypically, the source string is\nin your source language which is often English, but that does not have to be the case.\nYou can use any language you like as the source language.\n\nIf the translation for the string does not exist, `getString()` will return the source\nstring itself. This\nmeans that while you are developing your program or web site and the translations haven't\nbeen done yet, you will always get a reasonable string of some sort out of this method. Your\nGerman QA people may see the older parts of the UI translated to German and the new strings\nwill be in English until the translations are ready and re­integrated back into the project.\n\nWhen the strings are being loaded, the ResBundle class will also look up the “locale hierarchy”\ntree for fallback translations and merge them together in much the same way that Java does. For\nexample, let’s say you had your locale set to “fr-­CH-­govt” for the French­ speaking part of\nSwitzerland. The variant is for the Swiss government, as perhaps you have special translations\njust for them. In this case, when you load in the resources for that locale, the resource bundle\nwill first load any generic strings that are shared between all versions of French. ie. the “fr”\nlocale. Then, it will load in all the translations for “fr­-CH” for Swiss­-French­ specific\ntranslations. Any translation in “fr­-CH” overrides the corresponding translations in “fr” with\nthe same source string, and may add a few of its own. Finally, the “fr-­CH-­govt” file is loaded\nand any translations it provides overrides any existing translations in \"fr\" or \"fr-CH\".\n\nBased on this merging, it is recommended that you put most translations into the set for the\nbase language (ie. \"fr\"), and only override them with locale-­specific translations where\nthey are different from the general language. In this example, the \"fr-CH\" would contain only\nthe few strings that are written differently in Swiss French than in world-wide general French.\n\n### String Formatting\n\nConsider this code:\n\n```\nconst str = rb.getString(\"Your packaged arrived on \") + date +\n    rb.getString(\" and took \") + days + rb.getString(\" days to get there.\");\n```\n\nIn the above example, the translated strings are nicely wrapped with a `rb.getString()`\ncall. However, they are concatenated together with variables and a simple plus operator.\nThis is a big problem for translation! The reason is that in\nvarious languages, the variables sometimes need to be at the beginning,\nthe middle, or the end of the string in order to be grammatically correct. When they are\nconcatenated as above, they translator has no ability to make the translation grammatically\ncorrect for their language.\n\nThe solution to this problem is to do proper string formatting using the IString class.\nThe `getString()` method returns an instance of an ilib [IString](https://github.com/ilib-js/ilib-istring)\nwhich has a `format()` method for this purpose.\n\nHere is how the above code should be modified to do formatting properly:\n\n```\nconst strTemplate = rb.getString(\"Your package arrived on {date} and took {n} days to get there.\");\nconst str = strTemplate.format({\n    date: arriveDate,\n    n: arriveDays\n});\n```\n\nNote that the strings now contain replacement parameters enclosed in braces, such as “{date}”\nand “{n}”. These are replaced by the values of the parameters to the `format()` method, and\nthe resulting string is returned. Translators know not to translate replacement parameters.\nInstead, they move them around in the sentence so that they are grammatically correct for their\nlanguage.\n\nAs a general rule of thumb, if you have string concatenation in your code for any user­-visible\nstrings, you should be using string formatting instead.\n\nAlso note that the example above is still not totally correct. The \"n\" replacement parameter\nproduces a grammatically incorrect string in English when n has the value 1. The situation is\neven worse in other languages that have multiple types of plurals! To resolve that\nproblem, read the next section.\n\n### Plural Support and Choice Formatting\n\nIn some cases, you need different strings based on the number of items you have. For example,\nin English, you would say, “There is 1 object,” for singular and you would say, “There are 2 objects”\nfor plural. There is an odd convention where one item is singular, two or more items are plural,\nand most bizarrely, zero items are also plural. That is, you would say, “There are 0 objects.”\nThe plurality of the words “is/are” and “object/objects” corresponds to the actual\nnumber of objects you are talking about.\n\nYou might be tempted to solve this problem by writing \"clever\" code like this:\n\n```\nstr = resBundle.getString(\"It took {n} day\" + ((objects === 1) ? \"\" : \"s\") + \" to get there.\");\n```\n\nThere are multiple problems with the code above:\n\n1. That type of code assumes English grammar rules. In English, you pluralize by adding \"s\"\nto the end of the word. In other languages, it is not always the case that can add a suffix to pluralize\na word\n1. In some languages there are more than one type of plural, so the above will not work\n1. If you concatenate strings together, the loctool\nwill not be able to extract that string and put it in the resource file, so the `getString()` call will\nnot be able to find it\n\nIn other languages, plurality rules can get even more complex. For example, in Russian, numbers that end\nwith the digits 2 through 4 have a different plural than ones that end with than 5 through 9. ie. 22 is a different\nplural than 27. That means the above code is not translatable to Russian!\n\nTo solve this problem, you should use the `formatChoice()` method of the `IString` class. The\nmodified code would look like this:\n\n```\nconst template = resBundle.getString(\"one#There is {num} object.|#There are {num} objects.\");\nconst str = template.formatChoice(numObjects, {num: numObjects});\n```\n\nThis will pick the right choice based on the value of the variable `numObjects`.\nThe template string for the `formatChoice()` method has a number of parts. In this example, there\nare 2 choices separated by a vertical bar. The first choice is selected if the pivot number in\n`numObjects` is 1\n(the number before the hash character), and the resulting string should be “There is {num} object.”\nThe second choice has no number before the hash character, so it is the default choice. In that\ncase, the string returned is “There are {num} objects.” where “{num}” is replaced with the value\nof objects first. After the choice is selected, `formatChoice()` formats the string using the\nvalues of the properties in its second argument.\n\nNote that the number to match in the first choice is \"one\" which is the name of a category of\nnumbers. In English, that category only includes the number 1, but in other languages, that category\ncan include many numbers that are all considered singular.\n\nCan this support Russian? Yes, because now the translator or localization engineer is free to\nadd or remove choices in the translated string. In Russian, the translator can add a case for\nsingular numbers (the \"one\" category), plurals with 2 through 4 (the \"few\" category), and the\ndefault case would be the one that supports 5 through 9 (the \"many\" category).\n\nThe Russian translation of the above string in the `locale/ru/strings.json` resource file would\nlook like this:\n\n```\n    \"one#There is {num} object.|#There are {num} objects.\": \"one#Есть {num} объект.|few#Есть {num} объекта.|#Есть {num} объектов.\"\n```\n\nThe `formatChoice()` method also has other capabilities to switch not only on numbers, but also\nbooleans, strings, or even regular expressions. See the [IString documentation](https://github.com/ilib-js/ilib-istring)\nfor more details.\n\n### Pseudo­Translation\n\nThe resource bundle can also help your testers discover unresourcified strings using\npseudotranslation. Unresourcified strings are ones that are displayed to the user, but which\nhave not yet been wrapped in a `getString()` call.\n\nTo do this type of testing, you must set the default locale of ilib to a special “unknown”\nlocale, which is specified as “zxx-­XX”. The ilib-env package has a `setLocale()` method that\nallows you to set the overall default locale for ilib which is used if no explicit locale\nis set in the parameters to a constructor or method.\n\nExample:\n\n```\nimport { setLocale } from 'ilib-env';\n\nsetLocale(\"zxx-­XX\");\n```\n\nThe “unknown” locale causes the `ResBundle` class to return strings that are pseudo­localized.\nThat is, many of the regular Latin characters are replaced algorithmically with accented versions\nof those same base characters. This is still readable by an English­-speaking QA person,\nand yet they immediately know that the string was resourcified properly. If a string appears in\nplain text, then it is either user-­entered data, strings from 3rd party software or services,\nor it is a string that has not been resourcified properly. The QA person can submit a bug for\nthe engineer to investigate which of those possibilities it is, and fix the code if it turns out\nto be a string that is not wrapped with a `getString()` method call.\n\nHere is what a pseudo­localized string looks like:\n\n```\n    \"Greetings from Paris\": \"Ĝŕëëţíñğš fŕõm Pàŕíš\"\n```\n\nThings get a little tricky if the string is intended to be formatted or used in a web page. In\ngeneral, it is not a good idea to put HTML inside of a translatable string because the translators\nare linguists, not programmers, and they do not fully understand the syntax of HTML. They often\ntranslate things like CSS class names or HTML keywords. However, sometimes it is necessary to do this.\n\nFor example, if you put a link in your string like this:\n\n```\n    \"Greetings from \u003ca href='url'\u003e{city}\u003c/a\u003e \u0026amp; {country}\"\n```\n\nNow if you just pseudotranslated every character, you would end up with a string like this:\n\n```\n    \"Ĝŕëëţíñğš fŕõm \u003cà ĥŕëf='üŕľ'\u003e{çíţÿ}\u003c/à\u003e \u0026àmþ; {çõüñţŕÿ}\"\n```\n\nObviously, that would not work as intended in the browser, as the HTML tags and entities are\nalso pseudotranslated, as are the replacement parameter names. In this case, you should instantiate\nyour ResBundle with the `type` property set to “html”, and the psuedo­translation code will\nautomatically parse the strings and skip any HTML tags or entities.\n\nIn the examples above, we already have the `type` property set correctly. With the type\nparameter set, the above example would come out as this instead:\n\n```\n    \"Ĝŕëëţíñğš fŕõm \u003ca href='url'\u003e{city}\u003c/a\u003e \u0026amp; {country}\"\n```\n\nNote that the IString replacement parameters have been left alone as well. Without that, the\nsubstitution would not work too well either!\n\nThe constructor also supports other types of strings: xml, html, text, c, raw, ruby, or template.\n\nSee the full [API reference documentation](./docs/ilibResBundle.md) for a full description of\nall the parameters to the ResBundle and String classes.\n\n## Samples\n\nThere are some simple sample projects available that illustrate using `ResBundle`, `loctool`,\nand `ilib-assemble` together to localize a project.\n\n* A sample that illustrates [ResBundle usage in nodeJS](https://github.com/iLib-js/ilib-loctool-samples/tree/main/js-json)\n\n## License\n\nCopyright © 2022, JEDLSoft\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n## Release Notes\n\n### v1.0.2\n\n- added the documentation above\n- updated dependencies\n\n### v1.0.1\n\n- fixed a bug where the basePath parameter to the constructor or create\n  factory method was not being used properly\n\n### v1.0.0\n\n- initial version\n- copied from ilib 14.15.2\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filib-js%2Filib-resbundle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Filib-js%2Filib-resbundle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filib-js%2Filib-resbundle/lists"}