{"id":20301899,"url":"https://github.com/medic/translation-checker","last_synced_at":"2025-04-11T13:36:50.548Z","repository":{"id":45591321,"uuid":"285946650","full_name":"medic/translation-checker","owner":"medic","description":"Check translations from .properties files with Mustache placeholders markup or messageformat syntax.","archived":false,"fork":false,"pushed_at":"2022-09-15T10:21:28.000Z","size":71,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-05-01T11:35:55.254Z","etag":null,"topics":["library","messageformat","mustache","nodejs","properties"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@medic/translation-checker","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/medic.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}},"created_at":"2020-08-08T00:47:15.000Z","updated_at":"2023-01-20T16:22:53.000Z","dependencies_parsed_at":"2023-01-18T08:30:33.139Z","dependency_job_id":null,"html_url":"https://github.com/medic/translation-checker","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/medic%2Ftranslation-checker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/medic%2Ftranslation-checker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/medic%2Ftranslation-checker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/medic%2Ftranslation-checker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/medic","download_url":"https://codeload.github.com/medic/translation-checker/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248410213,"owners_count":21098772,"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":["library","messageformat","mustache","nodejs","properties"],"created_at":"2024-11-14T16:28:20.428Z","updated_at":"2025-04-11T13:36:50.522Z","avatar_url":"https://github.com/medic.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# translation-checker\n\nCheck translations from [.properties](https://en.wikipedia.org/wiki/.properties)\nfiles with [Mustache](http://mustache.github.io/) placeholders markup\nor [messageformat](https://messageformat.github.io/messageformat/) syntax.\n\n## Getting started\n\nNode 8.10+ required. Install with:\n\n    $ npm install @medic/translation-checker\n\nHere is how you can use it:\n\n```js\nconst {\n  checkTranslations,\n  TranslationException\n} = require('@medic/translation-checker');\n\nconst dir = `${somePath}/translations`;\nlet fileNames;\ntry {\n  fileNames = await checkTranslations(dir);\n  // Your files passed the validations!\n  // fileNames = ['messages-en.properties', 'messages-es.properties', ...]\n} catch (err) {\n    if (err instanceof TranslationException) {\n      // Oops, translation errors !!\n      fileNames = err.fileNames;  // ['messages-en.properties', 'messages-es.properties', ...]\n      if (!err.errors) {\n        return log.error('Exception checking translations:', err.message);\n      }\n      for (const error of err.errors) {\n        switch (error.error) {\n          case 'cannot-access-dir':\n            return log.warn('Could not find custom translations dir:', dir);\n          case 'missed-placeholder':\n          case 'wrong-placeholder':\n            log.error(error.message);\n            break;\n          case 'missing-key':\n          case 'empty-message':\n            // less severe, lets log it with WARN severity instead of ERROR\n            log.warn(error.message);\n            break;\n          default:  // 'wrong-messageformat', 'wrong-placeholder'\n            // This are more severe errors\n            log.error(error.message);\n            break;\n        }\n      }\n    } else {\n      throw err;  // unexpected ;(\n    }\n}\n```\n\nLogs from the example above will look like:\n\n```\nWARN Empty message found for key 'report.pregnancy.u_lmp_date' in 'en' translation \nWARN Empty message found for key 'report.pregnancy.method_lmp' in 'en' translation \nERROR Cannot compile 'es' translation with key 'Number in month' has placeholders that do not match any in the base translation provided \nERROR Cannot compile 'es' translation n.month = '{MONTHS, plural one{1 mes} other{# meses}}' : Expected \",\" but \"o\" found.\n```\n\n## Arguments\n\n`checkTranslations(dir, options)` (async)\n\n- `dir`: the directory to scan\n- `options.languages`: Array of lowercase ISO 639-1 code languages to be checked. Defaults to all valid files found in `dir`.\n- `options.checkPlaceholders`: Whether to check missed placeholders or not. Defaults to `true`.\n- `options.checkMessageformat`: Whether to check wrong Messageformat or not. Defaults to `true`.\n- `options.checkEmpties`: Whether to check empty messages or not. Defaults to `true`.\n- `options.checkMissing`: Whether to ensure all keys in \"en\" file also exist in other languages. Defaults to `false`.\n\n## Error details\n\nAll the errors raised by `checkTranslations()` are instances of `TranslationException`. The exception\nobject has an `errors` array with one object for each error detected as following:\n\n```javascript\n{\n  \"lang\": \"en\",                     // The language where the error was found\n  \"error\": \"wrong-messageformat\",   // The error code\n  \"key\": \"n.month2\",                // The translation key\n  \"message\": \"Cannot compile ...\"   // A message with more details about the error\n}\n```\n\nThese are the `error` codes and what they mean:\n\n - **`cannot-access-dir`** - the directory passed doesn't exist or is not accessible\n - **`wrong-file-name`** - one of the translation files has a wrong name or has\n   an unknown ISO 639 language code on it, eg. `messages-e$.properties`\n - **`missed-placeholder`** - a mustache placeholder eg. `{{firstName}}` was found\n   in a translation, but main translations ('en' or 'ex') don't have\n   placeholders for that translation at all\n - **`wrong-placeholder`** - a mustache placeholder eg. `{{firstName}}` was found\n   in a translation that do not match any in the base translations ('en' or 'ex')\n   placeholders\n - **`empty-message`** - empty message found in a translation\n - **`missing-key`** - a key was found in the English translation file but not\n   found in one of the other languages\n\n### Placeholders check\n\nThe mustache placeholder check works as following:\n\n - You need to call `checkTranslations(dir, options)` with\n   `options.checkPlaceholders = true` (default).\n - `options.languages` needs to be unset (default) or set at least with\n   one or both of the two codes considered \"template\" languages: 'en', 'ex'\n - 'ex' is not a real ISO language code, but if you have a\n   `messages-ex.properties` file, you can place there placeholders\n   that can be used for a specific key,\n   eg. `hello = {{firstName}} {{lastName}} {{username}}`\n - 'en' language (`messages-en.properties`) is considered the \"template\"\n   or base language, and any placeholder used in any other translation\n   file other than 'en' or 'ex' needs to be present in one of these\n   two templates. Eg. if you have in the 'en' translation the following\n   translation `records.total = {{count}} records`, this is a valid 'es'\n   translation: `records.total = {{count}} registros` but this not:\n   `records.total = {{count}} registros de {{max}}`. You can use the\n   `{{max}}` placeholder either adding it to the 'en' translation or\n   adding an entry in the `messages-ex.properties` file like this:\n   `records.total = {{max}}` (or `records.total = {{count}} {{max}}`)\n\nCheck the JSDoc of the source code to see more options.\n\n### Messageformat check\n\n\"Messageformat\" check are also performed by default, you can\ndisable it with `options.checkMessageformat = false`.\n\nA common error is by mistake translate the messageformat\nkeywords when using online translator tools, eg. this\ntranslation that is OK in English:\n\n```properties\nn.month = {MONTHS, plural, one{1 month} other{\\# months}}\n```\n\nWrong translated to Spanish:\n\n```properties\nn.month = {MONTHS, plural, uno{1 mes} otros{\\# meses}}\n```\n\nWill raise an error with a the message:\n\n```\nInvalid key `uno` for argument `MONTHS`. Valid plural keys for this locale are `one`,\n`other`, and explicit keys like `=0`\n```\n\nThe right translations is:\n\n```properties\nn.month = {MONTHS, plural, one{1 mes} other{\\# meses}}\n```\n\n### Empty message checks\n\nValidations fail if a key is found like this:\n\n```properties\nn.month = \n```\n\nBut some times empty keys are acceptable because\nthe software that use the translations fallback\nto the default language, so to disable empties\ncheck set `options.checkEmpties` to `false`\n(default is `true`).\n\n\n## Publishing\n\n    $ npm publish --access=public\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmedic%2Ftranslation-checker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmedic%2Ftranslation-checker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmedic%2Ftranslation-checker/lists"}