{"id":21195396,"url":"https://github.com/fallenmax/a18n","last_synced_at":"2026-02-27T05:35:03.103Z","repository":{"id":51487761,"uuid":"295373540","full_name":"FallenMax/a18n","owner":"FallenMax","description":"Automated I18n solution for JavaScript/TypeScript/React","archived":false,"fork":false,"pushed_at":"2023-02-14T03:55:53.000Z","size":3851,"stargazers_count":264,"open_issues_count":0,"forks_count":7,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-07-22T13:43:16.972Z","etag":null,"topics":["code-modification","i18n","internationalization","react","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/a18n","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FallenMax.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}},"created_at":"2020-09-14T09:52:13.000Z","updated_at":"2025-06-02T11:30:30.000Z","dependencies_parsed_at":"2024-01-18T05:16:33.801Z","dependency_job_id":"0db0c05b-2d29-421b-a24d-7a5b41ca48e9","html_url":"https://github.com/FallenMax/a18n","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/FallenMax/a18n","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FallenMax%2Fa18n","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FallenMax%2Fa18n/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FallenMax%2Fa18n/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FallenMax%2Fa18n/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FallenMax","download_url":"https://codeload.github.com/FallenMax/a18n/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FallenMax%2Fa18n/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29885870,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-26T23:51:21.483Z","status":"online","status_checked_at":"2026-02-27T02:00:06.759Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["code-modification","i18n","internationalization","react","typescript"],"created_at":"2024-11-20T19:27:43.436Z","updated_at":"2026-02-27T05:35:03.087Z","avatar_url":"https://github.com/FallenMax.png","language":"TypeScript","readme":"# a18n - automated i18n solution\n\n[![npm package](https://img.shields.io/npm/v/a18n.svg)](https://www.npmjs.com/package/a18n)\n[![Coverage](https://img.shields.io/codecov/c/github/fallenmax/a18n)](https://codecov.io/github/fallenmax/a18n)\n\n[English](https://github.com/FallenMax/a18n/blob/master/README.md) | [中文](https://github.com/FallenMax/a18n/blob/master/README_zh-cn.md)\n\n`a18n` is a production-ready internationalization solution that aims for maximum automation and simplicity.\n\nIt can **wrap** and **extract** texts in JavaScript/TypeScript code using AST manipulation, so you can effortlessly add i18n support to new or existing projects.\n\nWith its ability to recognize texts and resources, it can **check** for untranslated texts in code and resources, prune unused resources, and automatically provide texts context both for extraction and translation.\n\n[![Screen Recoding](https://github.com/FallenMax/a18n/blob/master/assets/screen-recording.gif?raw=true)](https://github.com/FallenMax/a18n/blob/master/assets/screen-recording.gif)\n\n## Features\n\n- Command Line Tool\n  - **Wrap** texts with translation calls (`a18n wrap`). (English and CJK texts are currently supported)\n  - **Extract** texts from translation calls (`a18n extract`)\n  - **Check** for untranslated text in code and resources (`a18n check`)\n  - **Replace** untranslated text with translations (`a18n replace`)\n  - **Purge** translation calls and imports (`a18n purge`)\n  - Supports dynamic texts in ES6 Template Strings\n  - Supports TypeScript\n  - Supports React and any JSX framework\n  - Provides context for texts with the corresponding module\n- Runtime\n  - Translates both static and dynamic texts using provided locale resources\n  - Small - only ~200 lines of code\n  - Optimized for performance - dynamic texts are compiled into a template on the first run for maximum speed.\n\n## Getting Started\n\n\u003e WARNING: Existing project code will be modified. Please make a backup or commit before proceeding.\n\nInstall as a project dependency (not a devDependency, as `a18n` provides both a CLI and runtime)\n\n```sh\nnpm install --save a18n\n```\n\nScan and modify code files (.js, .ts, .jsx, .tsx) in the src directory. This will wrap plain text strings with translation calls:\n\n- `--namespace` serves to avoid conflicts with the same a18n instance from other dependencies\n- `--module-name` automatically provides context for each text and helps distinguish texts from different modules\n- `--text=cjk` wraps CJK texts. You can also use `--text=capitalized` to wrap English words or sentences\n\n```sh\nnpx a18n wrap src --write --namespace=\"my.unique.project.id\" --module-name=\"fileDirAndName\" --text=\"cjk\"\n```\n\nManually check for unintended modifications and fix them. You can:\n\n- Use the comment `// @a18n-ignore` to ignore the next line\n- Use the comment `/* @a18n-ignore-file */` to ignore the entire file\n\nExtract texts passed to translation calls (this will generate `zh-CN.json`, `en.json` in the `./locales` directory):\n\n```sh\nnpx a18n extract src ./locales --locales zh-CN,en\n```\n\nTranslate resources under `./locales` (e.g. from Chinese to English). After this, you should have something like this:\n(the key is added by the `a18n` tool, the value is filled in by a human translator)\n\n```js\n{\n  // missing translation, will fallback to original key\n  \"no-translation\": null,\n\n  // static text\n  \"早上好\": \"Good morning\",\n\n  // dynamic text\n  \"%s是最好吃的\": \"pizza is better than %s\",\n\n  // with module context\n  \"some.module: {\n    \"%s是最好吃的\": \"noodle is better than %s\",\n  }\n}\n```\n\nLoad translation resources and specify the language at the start of your application, **this must be done BEFORE running any other code**\n\n```js\nimport a18n from 'a18n'\nimport en from './locales/en.json'\n\na18n.addLocaleResource('en', en)\na18n.setLocale('en')\n\n// now, a18n() will produce translated result\na18n('早上好') // === \"Good morning\"\n\nconst food = 'A'\na18n`${food}是最好吃的` // === \"pizza is better than A\"\n```\n\n## Documentation\n\n### API\n\n#### a18n(text)\n\n\u003e This function can and should be auto-added by `a18n wrap` command\n\nTranslates static text, `text` should be literal string (instead of a variable). For example:\n\n```js\na18n('你好') // good\na18n(greeting) // bad, `a18n extract` cannot extract \"你好\" by analyzing code\n```\n\n#### a18n\\`text\\${variable}\\`\n\n\u003e This function can/should be auto-added by `a18n wrap` command\n\nTranslates dynamic text.\n\nThis is an ES6 syntax called [Tagged Template Literal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals)\n\n#### a18n.x\\`text\\${variable}\\`\n\n\u003e This function cannot be auto-added and should be added by user.\n\nTranslates dynamic text and returns an array containing translated parts.\n\nThis method is useful for displaying mixed content.\n\n```jsx\nconst greeting = \u003cdiv\u003e{a18n.x`Hello ${\u003cstrong\u003eJimmy\u003cstrong\u003e}`}\u003c/div\u003e\n// could evaluate to:\n// \u003cdiv\u003e你好 \u003cstrong\u003eJimmy\u003cstrong\u003e\u003c/div\u003e\n```\n\n#### a18n.setLocale(locale)\n\nSet locale to use.\n\nThis method should be called BEFORE every `a18n` translation functions are called.\n\n`a18n` use `navigator.language` as the initial value.\n\n#### a18n.addLocaleResource(locale, resource)\n\nAdds a resource for the specified locale. The resource is usually extracted using the `a18n extract` command.\n\nAn example resource:\n\n```json\n{\n  // missing translation, will fallback to the original key\n  \"no-translation\": null,\n\n  // static text\n  \"早上好\": \"Good morning\",\n\n  // dynamic text\n  \"%s是最好吃的\": \"pizza is better than %s\"\n\n  // the resource can be organized by module, it can then used by the corresponding instance,\n  // created from `getA18n('my-project-namespace', 'my.module.x')`\n  \"my.module.x\": {\n    \"你好\": \"Hello from my module\",\n  }\n}\n```\n\nWill merge with existing resource and overwrite values that have same keys.\n\nThis method should be called BEFORE every `a18n` translation functions are called.\n\n#### a18n.getA18n(namespace, moduleName?)\n\n\u003e this method is usually auto added with `a18n wrap` command, with `--namespace` option\n\nGet an a18n instance with the specified namespace and modules.\n\nYou can use a unique `namespace` to get isolated resources and locales, even if different parts of system are reusing a18n as common dependency (which is common in large projects).\n\nIf `moduleName` is provided, this a18n instance will select `resource[moduleName]` as the resource. This serves as a way to split resources into different modules.\n\nSee [Q \u0026 A](#2-when-do-i-need-to-specify-a-namespace) for more background.\n\n#### a18n.DEBUG_setRepeat(repeatCount, separator?)\n\nRepeats the translated string/array multiple times, so that UI issues like text overflow can be easily spotted.\n\n- `repeatCount`: how many times to repeat the text\n- `separator`: separator between repeated text, default to ` ` (a space)\n\n### CLI\n\nSee: `npx a18n --help`\n\n## Q \u0026 A\n\n### 1. Why is it important to load translation resources and specify a locale **before ** all other code is run?\n\nThis can be illustrated with this example:\n\n```js\nconst s = a18n('apple') // We don't have locale resources yet, so `s` is bound to 'apple', not '苹果' as we intended.\n\na18n.addLocaleResource('zh-CN', { apple: '苹果' }) // ...Too late\na18n.setLocale('zh-CN') // ...Too late\n\nconsole.log(s) // 'apple'\n```\n\nIt is important to load translation resources and specify a locale before all other code is run because if you don't, the a18n function may not have access to the correct translation resources and locale, and will default to the original text.\n\n### 2. When do I need to specify a namespace?\n\nIf there are multiple dependencies in the project that further depend on `a18n`, some bundling tools (such as webpack) may generate a bundle where they all share a single copy of the `a18n` code and a single copy of the `a18n` instance at runtime. Since `a18n` is a singleton, this may cause unintended locale resource sharing/conflict.\n\nTo solve this problem, different dependencies can get their own a18n instances, differentiated by namespace, using `getA18n(namespace)`, and continue to have isolated resources and language configurations. It is also possible to acquire the same a18n instance by specifying the same namespace in order to share language and translation resources.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffallenmax%2Fa18n","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffallenmax%2Fa18n","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffallenmax%2Fa18n/lists"}