{"id":23740111,"url":"https://github.com/cobbzilla/hokeylization","last_synced_at":"2025-06-15T09:05:45.982Z","repository":{"id":58451741,"uuid":"531946459","full_name":"cobbzilla/hokeylization","owner":"cobbzilla","description":"CI-friendly tool to localize your app with Google Translate","archived":false,"fork":false,"pushed_at":"2023-05-21T01:27:21.000Z","size":1898,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-23T22:08:23.159Z","etag":null,"topics":["cli","localization","localization-tool","nodejs"],"latest_commit_sha":null,"homepage":"","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/cobbzilla.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-09-02T13:55:32.000Z","updated_at":"2024-07-09T02:49:34.000Z","dependencies_parsed_at":"2022-09-12T17:25:22.122Z","dependency_job_id":null,"html_url":"https://github.com/cobbzilla/hokeylization","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cobbzilla/hokeylization","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cobbzilla%2Fhokeylization","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cobbzilla%2Fhokeylization/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cobbzilla%2Fhokeylization/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cobbzilla%2Fhokeylization/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cobbzilla","download_url":"https://codeload.github.com/cobbzilla/hokeylization/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cobbzilla%2Fhokeylization/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259949660,"owners_count":22936407,"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":["cli","localization","localization-tool","nodejs"],"created_at":"2024-12-31T09:47:04.987Z","updated_at":"2025-06-15T09:05:45.959Z","avatar_url":"https://github.com/cobbzilla.png","language":"JavaScript","funding_links":["https://www.patreon.com/cobbzilla"],"categories":[],"sub_categories":[],"readme":"Hokeylization\n=============\nWhy can't I run my whole app or site through Google Translate and get a basic translation in another language?\n\n***Now, you can!***\n\nThe name `hokeylization` is a portmanteau, meaning 'hokey localization'\n\nIt's somewhat hokey because it's very simple: it sends strings to Google Translate\n\nAnd it is simple, yet also very powerful. It has special support for HTML documents,\n[HandlebarsJS](https://handlebarsjs.com/) templates,\nand [Markdown](https://daringfireball.net/projects/markdown) files.\n\nYou can translate:\n* a JavaScript object containing messages\n* any number of files or directories, always traversing directories recursively\n\n# Read this in another language\nThis README.md document has been translated, using the hokeylization tool itself, into\n**[every language supported by Google Translate](https://cloud.google.com/translate/docs/languages)!**\n\nI'm certain it's not perfect, but I hope it's better than nothing!\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇸🇦 Arabic](lang/ar/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇧🇩 Bengali](lang/bn/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇩🇪 German](lang/de/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇺🇸 English](lang/en/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇪🇸 Spanish](lang/es/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇫🇷 French](lang/fr/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇹🇩 Hausa](lang/ha/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇮🇳 Hindi](lang/hi/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇮🇩 Indonesian](lang/id/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇮🇹 Italian](lang/it/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇯🇵 Japanese](lang/ja/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇰🇷 Korean](lang/ko/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇮🇳 Marathi](lang/mr/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇵🇱 Polish](lang/pl/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇧🇷 Portuguese](lang/pt/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇷🇺 Russian](lang/ru/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇰🇪 Swahili](lang/sw/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇵🇭 Tagalog](lang/tl/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇹🇷 Turkish](lang/tr/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇵🇰 Urdu](lang/ur/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇻🇳 Vietnamese](lang/vi/README.md)\n\u0026nbsp;\u0026nbsp;\u0026nbsp;[🇨🇳 Chinese](lang/zh/README.md)\n\u003cbr/\u003e\n\u0026nbsp;\u0026nbsp;\u0026nbsp;**[📚 ... All Languages ...](lang/README.md)**\n----\n\n### Is there a problem with this translation of the README?\nThis particular translation of the original [README](https://github.com/cobbzilla/hokeylization/blob/master/README.md)\nmay be flawed -- *corrections are very welcome!* Please send a [pull request on GitHub](https://github.com/cobbzilla/hokeylization/pulls),\nor if you're not comfortable doing that, [open an issue](https://github.com/cobbzilla/hokeylization/issues)\n\nWhen you create a new GitHub issue about a translation, please do:\n* include the page URL (copy/paste from browser address bar)\n* include the exact text that is wrong (copy/paste from browser)\n* please describe what is wrong -- is the translation incorrect? is the formatting broken somehow?\n* kindly offer a suggestion of a better translation, or how the text should be properly formatted\n* **Thank you!**\n\n# Contents\n* [Source](#Source)\n* [Support and Funding](#Support-and-Funding)\n* [Installation](#Installation)\n* [Setup](#Setup)\n* [Translating a JavaScript string resource file](#Translating-a-JavaScript-string-resource-file)\n* [Translating a directory of text files](#Translating-a-directory-of-text-files)\n* [Other options](#Other-options)\n* [JSON batch commands](#JSON-batch-commands)\n\n## Source\n* [hokeylization on GitHub](https://github.com/cobbzilla/hokeylization)\n* [hokeylization on npm](https://www.npmjs.com/package/hokeylization)\n\n## Support and Funding\nI would sincerely appreciate any [contribution via Patreon](https://www.patreon.com/cobbzilla)\n\n## Installation\nTo use the command line tool, install using `npm` or `yarn`:\n\n    npm install -g hokeylization\n    yarn global add hokeylization\n\nTo use as a library, install the `lite` version, which is much smaller:\n\n    npm install -g hokeylization-lite\n    yarn global add hokeylization-lite\n\nThen look at help for the `hokey` command:\n\n    hokey --help\n    hokey -h\n\nWant to see output in your langauge or another language?\n\n`hokey` tries to detect the language automatically from your shell's environment variables\n\nYou can force a language by setting the `LC_ALL` environment variable:\n\n    LC_ALL=it hokey --help\n\nNote that if you have installed `hokeylization-lite`, command help is only available in English\n\n## Setup\nSet the `GOOGLE_TRANSLATE_PROJECT_ID` environment variable to identify your Google Translate project\n\nSet the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to the JSON credentials you downloaded\nafter figuring out how authentication works on Google cloud (it can be fun)\n\nIf you're running from the source code, you can also put these in a `.env` file in the source\ndirectory they'll be loaded at runtime via [dotenv](https://www.npmjs.com/package/dotenv)\n\n## Translating a JavaScript string resource file\nYour string table **must** be in a JavaScript file in one of these two forms:\n\nES6 export:\n\n    export default {\n      string_key: \"some value\",\n      another_key: \"another value\",\n      ... more keys ...\n    }\n\nCommonJS export\n\n    module.exports = {\n      string_key: \"some value\",\n      another_key: \"another value\",\n      ... more keys ...\n    }\n\nIf this file was named `myfile.en.js`, you can translate it to Spanish and German with:\n\n    hokey -l es,de -o myfile.LANG.js myfile.en.js\n\nThe `LANG` in the above is special -- it is a reserved word in this tool!\n\nThe `LANG` is replaced with the language code for the output files\n\nThus the above command creates the files:\n\n    myfile.es.js\n    myfile.de.js\n\nThe `-l` / `--languages` option is a comma-separated list of ISO language codes\n[supported by Google Translate](https://cloud.google.com/translate/docs/languages)\n\nIf the output file already exists, it will be examined to determine which keys already exist.\nExisting keys will not be translated. Translations for missing keys will be generated and appended\nto the end of the JS object. The entire file is always rewritten.\n\nTo force retranslation all keys, use the `-f` / `--force` option\n\n## Translating a directory of text files\nYou can also translate a directory of files. hokeylization will recursively visit every\nfile in the directory and run its contents through Google Translate, and save the output\nto an identically named file in a separate directory tree\n\nWhen the target of your translation is a directory, this mode is enabled\n\nThe `-o` / `--outfile` option specifies the output directory\n\n**BIG WARNING**: When translating directories, **DO NOT** specify an output directory\nthat is within your input directory! If you do this, you will:\n* induce infinite recursion\n* run up your Google bill\n* fill up your disk\n* have less fun\n\nHere is an example of what *not to do*:\n\n    hokey -l es -o templates/es templates    # \u003c--- DON'T DO THIS!\n\nWhen this runs, translated files are written to `templates/es`, and thus become new\nsource files to translate, since they are under `templates/` -- this process continues\nforever, don't do it!\n\n#### Correct usage\nOK, let's say you have some email templates in a directory:\n\n    templates/email/en/welcome.txt\n    templates/email/en/welcome.html\n    templates/email/en/verify-account.txt\n    templates/email/en/verify-account.html\n    templates/email/en/reset-password.txt\n    templates/email/en/reset-password.html\n\nTo translate all of these to Spanish and German, run:\n\n    hokey -l es,de -o templates/email/LANG templates/email/en\n\nIn the above, `LANG` is a reserved word and will be replaced with the ISO language code\n\nWhat happens when the above runs:\n* The `templates/email/es` and `templates/email/de` directories will be created (if they don't exist)\n* Every file in `templates/email/en` will be translated to Spanish and German\n  * Existing output files will not be regenerated unless you use `-f` / `--force`\n* You'll end up with an identical directory structure and files within `es` and `de` like you have under `en`\n\n## Other options\n\n### Dry run\nPass `-n` / `--dry-run` to display what would be done, but do not actually make any API calls or write any files\n\n### Force\nPass `-f` / `--force` to always regenerate translations, even if they already exist\n\n### Match\nPass `-m` / `--match` to limit the files processed when running in directory-mode\n\nYou may not always want to translate *every* file in your source directory to your target directory\n\nThe value of the `-m` / `--match` option is a regex (beware shell quoting rules!) that specifies\nwhich files should be translated\n\nWhen in doubt, you can combine this option with `-n` / `--dry-run` to see which files would be translated\n\n### Excludes\nSometimes your `-m` matches too many files. Use the `-e` / `--excludes` option to explicitly exclude\nfiles that otherwise would have matched\n\nYou can list multiple regexes, separated by spaces\n\nA common usage would be: `--excludes node_modules dist \\.git build tmp`\n\n### Handlebars\nThe strings to translate might contain `{{ handlebars }}` templates, either with two or three curly-braces\n\nYou probably *DON'T* want the stuff inside those templates to be translated\n\nPass the `-H` / `--handlebars` flag, and anything within `{{ ... }}` will not be translated\n\n### Markdown\nMarkdown is neither text nor html, so Google Translate has some difficulties with it\n\nThe `-M` / `--markdown` flag enables special handling for markdown files\n\nWith markdown files, if you don't use the `-M` flag, you will probably find these problems:\n* Broken links. In the translation, a space character appears after a markdown link description ends (with `]`) but\nbefore its target link begins (with `(`). This causes the markdown to render incorrectly, and the link\nis broken when viewing the document.\n* Code blocks get translated. Google translate doesn't know what markdown considers code and what it doesn't\n* Incorrect spacing for indented code blocks. Spacing is difficult to preserve in translation\n* Things inside of `backticks` will get translated, when you almost always want them to be literal values\n\nWhen the `-M` / `--markdown` flag is enabled:\n* The pattern `] (` will be condensed to `](` thus fixing the broken markdown links\n* A \"no translate\" wrapper will be placed around indented code blocks, preserving proper indentation and ensuring they are not translated\n* A \"no translate\" wrapper will be placed around text within `backticks` to ensure that they are not translated\n\n### Process-as\nNormally everything is processed as plain text\n\nIf your content is HTML, it will get mangled unless you pass the `-p html` / `--process-as html` option\n\n### Filter\nFor the adventurous: when processing files in a directory, you can pass the `-F` / `--filter` option\nto filter the output before it is written to the filesystem\n\nThe value of this option must be a path to a JS file that exports a function named `filter`\n\nThe `filter` function must be `async` because `await` will be called upon it\n\nBefore files are written to disk, the entire file contents will be passed to the `filter` function as a string\n\nThe return value from the `filter` function is what will actually be written to storage\n\nThus, you have total control over what will finally be written\n\nThe `filter` script will be looked for in the following locations (with `.js` will be appended to the filter\nname, unless it already ends in `.js`)\n* The current directory\n* A directory named `.hokey-filters` within the current directory\n* A directory named `${HOME}/.hokey-filters`, where `${HOME}` is the current user's home directory\n* The built-in [filters directory](https://github.com/cobbzilla/hokeylization/tree/master/util/filter)\n\n#### Filter Parameters\nThe `filter` string can be multiple words. In this case, the first word is the filter name, and\nthe remaining words will be passed as arguments to the `filter` function\n\n### Help\nUse `-h` / `--help` to show help\n\n## JSON batch commands\nWith the `-j` / `--json` option, you can run multiple coordinated `hokey` commands\n\nBy convention this file is called `hokey.json`, but you can name it whatever you want\n\nIf you pass a directory as the `-j` option, `hokey` will look for a `hokey.json` in that directory\n\nThe JSON file should contain one object. Within that object, its property names are the same as\nthe command-line options, plus one additional property named `hokey`\n\nThe `hokey` property is an array of commands to run. The properties declared within these commands will\noverride any duplicate declarations in the outer object.\n\nWithin each object in the `hokey` array, you should specify a `name`, and the input and output files\n\nHere is an example of a `hokey.json`\n\n    {\n        \"inputLanguage\": \"en\",\n        \"languages\": \"es,fr,ja\",  # can also be an array of strings\n        \"force\": false,\n        \"match\": null,\n        \"processAs\": null,\n        \"excludes\": [\"exclude-1\", \"exclude-2\"],\n        \"handlebars\": false,\n        \"markdown\": false,\n        \"regular\": false,\n        \"dryRun\": false,\n        \"filter\": \"theFilter.js\",\n        \"hokey\": [\n          {\n            \"name\": \"locale names\",\n            \"infile\": \"messages/locales_en.js\",\n            \"outfile\": \"messages/locales_LANG.js\",\n            \"handlebars\": true\n          },\n          {\n            \"name\": \"CLI messages\",\n            \"infile\": \"messages/en_messages.js\",\n            \"outfile\": \"messages/LANG_messages.js\",\n            \"handlebars\": true\n          },\n          {\n            \"name\": \"README\",\n            \"infile\": \"README.md\",\n            \"outfile\": \"lang/LANG/\",\n            \"excludes\": [\"lang/\", \"node_modules/\", \"\\\\.git/\", \"tmp/\"],\n            \"filter\": \"relativizeMarkdownLinks lang\",\n            \"markdown\": true,\n            \"index\": \"lang/README.md\"\n          }\n        ]\n    }\n\n### Multiple input files\nPass an array of file paths as `infiles` instead of a single path `infile`, as in this example:\n\n    {\n      ... [\n        {\n          \"name\": \"my docs\",\n          \"infiles\": [\"README.md\", \"INSTALL.md\", \"TUTORIAL.md\"],\n          \"outfile\": \"docs/LANG/\",\n          \"markdown\": true\n      ]\n    }\n\n### Indexes\nWhen translating to many languages, `hokey` can create an index file that lists all the translations made\nand provides links to them\n\n*When generating indexes, you can have only one input source*\n\nPass the `-I` / `--index` option, the value is where the index file will be generated, which can be a file\nor a directory. If it's a directory, a default filename will be used, based on the template (see below)\n\nUse the `-A` / `--index-template` to determine how the index output is formatted. You can specify 'html',\n'markdown', 'text', or the file path to your own [HandlebarsJS](https://handlebarsjs.com/) template\n\nIf you specify your own template, you must also specify a file (not a directory) for the `-I` / `--index`\noption\n\n## Have a fun time translating languages!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcobbzilla%2Fhokeylization","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcobbzilla%2Fhokeylization","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcobbzilla%2Fhokeylization/lists"}