{"id":19777521,"url":"https://github.com/ilib-js/ilib-resource-webpack-loader","last_synced_at":"2026-05-11T13:32:08.599Z","repository":{"id":90506556,"uuid":"227978228","full_name":"iLib-js/ilib-resource-webpack-loader","owner":"iLib-js","description":"A webpack loader that dynamically includes translated resources in separate chunks","archived":false,"fork":false,"pushed_at":"2020-01-12T22:17:12.000Z","size":56,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-11T01:50:02.013Z","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":"2019-12-14T06:38:39.000Z","updated_at":"2020-01-12T22:17:14.000Z","dependencies_parsed_at":null,"dependency_job_id":"119b4cc8-4b33-4adb-bcfe-9d7ed544e749","html_url":"https://github.com/iLib-js/ilib-resource-webpack-loader","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iLib-js%2Filib-resource-webpack-loader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iLib-js%2Filib-resource-webpack-loader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iLib-js%2Filib-resource-webpack-loader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iLib-js%2Filib-resource-webpack-loader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iLib-js","download_url":"https://codeload.github.com/iLib-js/ilib-resource-webpack-loader/tar.gz/refs/heads/master","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:22.970Z","updated_at":"2025-11-23T13:05:11.514Z","avatar_url":"https://github.com/iLib-js.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ilib-resource-webpack-loader\n\nilib-resource-webpack-loader is a webpack loader for translated resource files that\nallows your application to include those files automatically within the webpacked\napplication.\n\n# Using the Resource Loader\n\n## webpack.config.js\n\nTo add translated resources to a webpacked application, modify the\nrules section of the application's webpack.config.js file to add\nconfiguration for the loader:\n\n```\n    module: {\n        rules: [{\n            test: /.js$/,\n            use: {\n                loader: 'ilib-resource-webpack-loader',\n                options: {\n                    locales: [\"en-US\", \"de-DE\", \"ja-JP\" ],\n                    mode: 'development',\n                    assembly: 'assembled',\n                    resourceDirs: [\n                        path.resolve('./assets/translations'),\n                        path.resolve('./src/components/res')\n                    ]\n                }\n            }\n        }]\n    },\n```\n\nHere are what the options mean:\n\n- locales - an array of BCP-47 style tags that specify the list of locales that\n  the application needs to support. (See below for more details.)\n- mode - 'development' vs. 'production'\n    - in 'development', files are included with formatting, not compressed\n    - in 'production' mode, files will be compressed/uglified\n- assembly - \"assembled\" vs. \"dynamic\"\n    - in \"assembled\" mode, the translations are included directly into\n      the webpack bundle. In \"dynamic\" mode, translated files are put\n      into separate webpack chunks that are loaded dynamically when needed.\n- resourceDirs - an array of paths where resource files can be found\n    - an array allows for including resource files from\n      multiple parts of an applications which can be released\n      and localized separately\n    - paths may be absolute or relative. When relative, they\n      should be relative to the root of the applications.\n\n## project.json\n\nIn the project.json configuration file for loctool, choose the \"custom\"\nstyle of project and use the loctool plugins that make sense for the\napplication.\n\n```\n{\n    \"name\": \"My Web App\",\n    \"id\": \"mywebapp\",\n    \"projectType\": \"custom\",\n    \"sourceLocale\": \"en-US\",\n    \"resourceDirs\": {\n        \"jst\": \"./i18n\",\n        \"javascript\": \"./i18n\"\n    },\n    \"plugins\": [\n        \"html\",\n        \"javascript\",\n        \"jst\"\n    ],\n    \"excludes\": [\n        \"./.git\",\n        \"./assets\",\n        \"./bin\",\n        \"./libs\",\n        \"./script/**/*.sh\"\n    ],\n    \"includes: [\n        \"**/*.html\",\n        \"**/*.js\",\n        \"**/*.jst\"\n    ],\n    settings: {\n        locales: [\"es-ES\", \"de-DE\", \"fr-FR\"],\n    }\n}\n```\n\nIn the `plugins` property, list all of the loctool plugins necessary\nfor all of the file types in the project, and make sure the `resourceDirs`\nproperty contains the output resource directory for each file type. These\nare the directories where the loctool will write the resource files. This\nshould match the `resourceDirs` property in the webpack.config.js file\nso that the resource loader can find those resource files and include them.\n\n## Configuration Choices\n\nTo configure the loader, you will need to decide upon:\n\n- Which locales does the application need?\n- Do you want to assemble the translation\n  data directly into the the app's webpack\n  bundle, or should they be dynamically lazy-loaded?\n- Is the application being built for debug or production mode?\n\n### Which Locales?\n\nThe loader is configured by default to support the top 20 locales around the world in\nterms of Internet traffic. If no locales are explicitly chosen, the loader will default\nto the translations for these top 20 locales if they exist. That is a very small subset\nof all the locales that could be supported, yet the set of translated strings could still\nbe potentially large.\n\nWhen the loader is including translations, it first checks if any of the translated\nresource files for the given locales are available in any of the resources directories.\nIf they are not there, it does not need to include them, of course, and it silently\nskips those locales.\n\nIf the app does not support that many locales, it can have a significantly\nsmaller footprint by specifying a smaller set of locales in the webpack.config.js.\n\nLocales should be specified using [BCP-47 locale tags](https://en.wikipedia.org/wiki/IETF_language_tag)\n(aka. IETF tags). This uses ISO 639 codes for languages, ISO 15924 codes for scripts,\nand ISO 3166 codes for regions, separated by dashes. eg. US English is \"en-US\" and\nChinese for China written with the simplified script is \"zh-Hans-CN\".\n\n\n### Which Files are Included?\n\nThe loader automatically includes all files that use the locale spec somewhere in the\nfile name. The file name may have other characters in it as well. \nFor example, if the locale is \"de-DE\", then all of the files \"resources-de-DE.json\",\n\"strings_de-DE.json\" and \"de-DE-text.json\" are all included.\n\nIlib's ResBundle class has a concept of \"locale fallbacks\", which allows the application\nto use the translated resources of other locales if the strings are not available\nin the current locale. Right now, the fallbacks are simply variations on the full\nlocale, but in the future may include other locales. Accordingly, this resource\nloader will include all of the files for the fallback locales as well.\n\nThe fallback locale variations are as follows:\n\n- language-script-region-variant\n- language-region-variant\n- language-script-region\n- region-variant\n- language-region\n- language-script\n- region\n- language\n- root\n\nEach of \"language\", \"script\", \"region\" and \"variant\" are replaced with the corresponding\npart from the full locale spec if they are available.\n\n\"Root\" is a special locale that all locales derive from, and usually contains text\nwritten in the source language (typically English). The root in ilib also contains\nany data that is not locale-sensitive.\n\nSo, for example, if the full locale spec is \"zh-Hans-CN\", then the list of variations that\nthe loader will look for are:\n\n- zh-Hans-CN\n- zh-CN\n- zh-Hans\n- CN\n- zh\n- root\n\nNote that in this example, there is no \"variant\" part in the full locale spec, so any\nfallback locale that uses the variant is simply left out.\n\nThe list of fallbacks can be a little tricky -- if full locale spec is \n\"de-DE\" for example, then any file name that includes the language spec \"de\" will\nbe included, including such file names as \"device.json\" or \"delimiter.json\" because\nthey happen to include the substring \"de\". Those are probably not locale files. As such,\nthe developer should be careful which files appear in the resource directories. Only\nlocalized files should appear there. Unless you need to have multiple files for the\nsame locale, it is best just to name the file simply for the locale, such as \"de-DE.json\"\nor \"de.json\". \n\n### Assembled or Dynamic Translations?\n\nThere are two ways to include the translation data into the webpack configuration for an\napplication: assembled and dynamic.\n\n1. Assembled. Translations are included directly into the app's webpack bundle.\n\n   Advantages:\n   - everything is loaded and cached at once\n   - all translations are available for synchronous use as soon as the browser has\n     loaded the webpacked js file. No async calls, callbacks, or promises are needed!\n   - there are less files to move around, push to production, or to check into your repo\n\n   Disadvantages:\n   - that single file can get large quickly if there are a lot of locales or a lot of strings\n   - the app would load all locales at once, even if you only use one locale at a time,\n     means extra network bandwidth and load time for data, as well as memory and\n     disk footprint that the user isn't actually using\n\n   Assembled translations are a good choice if the app only supports a few locales or\n   if there are only a few strings to translate. The first\n   time a user hits the website, they download a larger-than-needed webpack file, but then\n   it is cached and everything after that is relatively simple.\n\n2. Dynamic. Webpack has the ability to lazy-load translations as they are needed. With\n   this type of configuration, the code is assembled into one file, but the translation\n   data goes into other separate webpack chunk files, which are lazy-loaded as they\n   are needed.\n\n   Advantages:\n   - file size and therefore initial page load time are minimized\n   - the application code can be cached in the browser and doesn't change often even if\n     the translations do\n   - the translation files can be cached separately, allowing the app to add new locales\n     to the web site later without affecting any existing cache for the translations in\n     other locales\n\n   Disadvantages:\n   - the number of chunk files can get unwieldy if the app has a lot of locales. The\n     loader will create a different chunk file for every locale variation it finds.\n   - since webpack loads the bundles asynchronously, the app must use the ilib ResBundle class\n     asynchronously with callbacks or promises. Alternately, the app must pre-initialize the\n     translation data asynchronously and then wait for the load to finish in a jquery\n     \"document ready\" kind of callback function before using the translations synchronously\n     after that.\n\n   Using dynamic data is a good choice if you have a lot of locales or have a lot of strings\n   to translate.\n\n### Development vs. Production mode\n\nThe mode property can be set to one of the modes \"development\" or \"production\".\nIn development mode, a developer typically wants to see nicely formatted code and data to\nmake debugging easier. Load time and footprint do not matter so much. In this mode, the\ntranslated output is generated with formatting and line breaks. In production mode,\nload time and footprint are much more of a concern, so translation files are\ncompressed/uglified as they are added to the webpack bundle.\n \n\n## How Does the Loader Work?\n\nWebpack loaders in general are used to read and modify javascript files as they are\nadded to the webpack bundle. The ilib-webpack-loader does this as well. Specifically,\nit is doing these things:\n\n* If the javascript file being added to the bundle happens to be the iLib ResBundle\nclass, it modifies the code to depend on a hidden class that knows how to load the\ntranslated files.\n* It also writes out the code for the hidden file. This code loads the translated\nfiles depending on a locale parameter. This code is different depending on whether\nthe configuration specifies assembled or dynamic translations, and whether it specifies\ndevelopment or production mode.   \n\n# The Translation Cycle for an Application\n\nApplications should be translated frequently so that stories in a sprint can be\nconsidered \"done\" properly. Testers will need the translations to verify that the\nfeature is \"done\".\n\n## Translation of a Regular Webpacked Application\n\nTo get a React application translated, see the next section.\nTo get a non-React Javascript application translated, follow these steps:\n\n1. Make sure to add a dependency on ilib and a devDependency on\n  ilib-resource-webpack-loader to the package.json file\n1. Import and instantiate an ilib `ResBundle` instance in each file\n  of the application classes that contains translatable strings specifying\n  the path where the translated resource files will go or create a\n  `ResBundle` instance once for the whole application and pass it\n  around to the rest of the code\n1. Make sure all user-visible strings in the application are wrapped\n  in a `ResBundle.getString()` or `ResBundle.getStringJS()` call\n1. Make sure the application includes a way for a user to change their\n  locale, either by adding a choice in the UI or by having an admin\n  change it for them.\n    - It is recommended that every user have their own locale\n      which is stored along with the user's information in the\n      backend\n1. Add loctool to the devDependencies in the package.json file and make\n  sure it is installed and that node_modules/.bin is in the path (or you\n  can use npx to run it)\n    - Follow the directions in the loctool README to create\n      a loctool project.json file for the application and commit it\n      to your repo\n1. Run the loctool to extract all of the localizable strings into xliff\n  files\n1. Send the xliff files out for translation\n1. When the translated xliff files are returned, use the loctool again\n  to generate the translated json resource files in the path specified\n  in the second step above.\n1. Add ilib-resource-webpack-loader in the webpack.config.js file with the\n  appropriate options (according to the details above)\n1. Run webpack as normal, and it should find and include all of the\n  translated files that the loctool had just generated\n\nThe translations should now be available so that when the user changes\ntheir locale, the translated strings should appear in the UI.\n\nIn step 6 above, the loctool will simulataneously generate an xliff file\ncontaining the new strings that the developers checked in since the last\nset of xliff files was generated and sent out for translation. This means\nthat you can send those new xliff files out as the next translation batch,\nrestarting the cycle over again. It is recommended that the application\nis translated on a regular basis. In some projects that have \"continuous\nlocalization\" as part of their process, the new strings are sent out\nas frequently as multiple times per week, ensuring that the translators\nget a constant stream of strings to translate, rather than a huge\nbatch at the end of the project. Waiting to the end is usually a bad idea\nbecause it creates a schedule bottle neck.\n\n## Localization Process for React Applications\n\nThe process for localizing React applications is essentially the same\nas above with the following tweaks:\n\n- In step 1 above, instead of adding dependencies on ilib directly, add\n  dependencies on ilib-es6 and react-ilib. Ilib-es6 contains ES6 wrappers\n  around the regular ilib classes which can be\n  included in ES6 code normally using regular `import` statements.\n  Ilib code is written in ES5 and must be\n  included using `require()`. Also, ilib-es6 uses promises to support\n  asychronous ilib calls instead of the callback functions that\n  regular ilib uses. React-ilib is a library of formatters and\n  utility components that make it easier to localize React apps.\n- Make sure the top level app is wrapped in a `LocaleDataProvider`\n  component from react-ilib, which loads the ilib locale data and the\n  translated resources that this webpack plugin includes into the\n  webpack bundle.\n    - The `LocaleDataProvider` component loads the data\n      asynchronously and then renders the rest of the app only\n      when the data is finished being loaded. In this way, the\n      app does not appear in\n      English first and then re-rendered in another language\n      which creates a very odd user experience.\n\nExample index.js file:\n\n```javascript\n// put this in your main index.js file\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport path from 'path';\nimport App from './App'; // this is your main App code\n\nimport { LocaleDataProvider } from 'react-ilib';\n\nReactDOM.render(\n    \u003cLocaleDataProvider\n        locale=\"de-DE\"\n        translationsDir={[path.join(__dirname, \"res\")]}\n        bundleName=\"resources\"\n        app={App}  // don't render the App until after the locale data is loaded!\n    /\u003e,\n    document.getElementById('root')\n);\n```\n\n- For any user-visible JSX strings, wrap them in a `Translate` component,\n  and use the `rb.getString()` and `rb.getStringJS()` methods for strings in\n  the JS code\n    - The loctool has plugins that know how to extract strings from jsx\n      and JavaScript code\n- For any component that needs translated strings, use the\n  `injectRb()` function to make sure that the translations are available\n  in the code. The `rb` variable is the resource bundle instance that the\n  `LocaleDataProvider` component creates for you when it has finished loading\n  the translated strings.\n\nExample:\n\n```javascript\nimport { Translate, injectRb } from 'react-ilib';\n\nfunction App(props) {\n    const { rb } = props;\n    const string1 = rb.getString(\"Hello World 1!\");\n    return (\n        \u003cspan\u003e\n            {string1}\n        \u003c/span\u003e\n        \u003cspan\u003e\n            \u003cTranslate\u003eHello World 2!\u003c/Translate\u003e\n        \u003c/span\u003e\n    );\n}\n\nexport default injectRb(App);\n```\n\n# Handling the Translated Files\n\nSome file types are localized by extracting strings into a resource\nfile, which is the way that regular JavaScript\ncode works. This webpack loader helps to include those resource files\ninto your webpacked application. The translated resource files that \nthe loctool produces can be checked in to your source control system.\n\nOther file types are localized \"by copy\". That is, they are localized by\ncreating translated copies of the source files\nwith the text in them replaced with translations. Often, these\nare more structured file types such as front end templates or XML.\n\nFor example, regular HTML files are often localized by copy.\nNow if an engineer changes a non-localizable part of an HTML source file,\nsuch as JavaScript code or CSS or HTML attributes, there is no\nneed for a new translation. Yet, the translated HTML files will be out\nof sync with the original source files because the code does not\nmatch.\n\nThe solution is to run the loctool with every build. Doing this will guarantee\nthat the non-localizable bits of the translated files will be the\nsame as the in the source file, even when no localizable strings\nare changed.\n\nThe rule of thumb is this: if the application includes any file types\nthat are localized by copy, it is better to commit the xliff files to the\nsource control system\nand generate the translated files with every build instead of committing\nthe translated resource files. If the application does not have any\nfile types that are localized by copy, then commit the translated\nresource files.\n\nFile types that are typically localized by\ncopy include the following: HTML, XHTML, JSON, and any front-end templates\nsuch as JST, HAML, or JSP. Check with the documentation of the loctool plugin\nfor the particular file type to see how it is localized.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filib-js%2Filib-resource-webpack-loader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Filib-js%2Filib-resource-webpack-loader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filib-js%2Filib-resource-webpack-loader/lists"}