chki18n 1.2.0
chki18n: ^1.2.0 copied to clipboard
Checks that your i18n translation files agree with each other — missing keys, untranslated values, broken placeholders. No dependencies, pure Dart.
Changelog #
1.2.0 - 2026-09-05 #
A scan can be pointed at an application's root rather than at a folder of locales. It skips the configuration and lock files that root is full of, an exclude can name a path rather than a bare directory name, and it reports which interpolation delimiters the files it read appear to use.
Added #
excludeFilesnames the files a scan never reads, as patterns where*stands for any run of characters and case is ignored. It defaults to the configuration and lock files no project keeps translations in —package.json,tsconfig.json,tsconfig.*.json,eslintrc.json,*-lock.json,*-config.jsonand*.config.json— which a scan of an application root used to read and parse in full on every run. The default list is exported asdefaultExcludeFiles, and--exclude-filesis the flagdetectInterpolationDelimitersguesses which delimiters a text writes its interpolation keys with:{{name}}before{name}, and[[ ]],(( ))and<< >>after them.Chki18nScanResult.detectedInterpolationreports what a whole scan saw, whichloadTranslationscarries assession.detectedInterpolation. It is a suggestion for a project being set up, never what the run compared with — that staysinterpolationPrefix. The pairs it knows are exported in order asinterpolationDelimiterscreatePathExcluderandcreateFileExcluderbuild the two tests a scan applies, so an application that shows a user which folders are excluded can ask the same question the scan asks
Changed #
excludeaccepts a path as well as a name. One segment still names a directory at any depth, sonode_modulesmeans everynode_modulesthere is; an entry with a separator names a path from the scanned root, sosrc/legacyexcludes that folder and everything under it without touching alegacybelonging to something else- Both exclude lists apply to the source tree
sourcenames as well as to the translation directory
1.1.0 - 2026-09-04 #
The target language is checked too. Everything else is compared against it, so it sat outside every check, and a mistake typed into the source language stayed there however often the files were checked.
Changed #
- The target language is now checked by everything that reads one value on its own:
EMPTY_VALUE,SURROUNDING_WHITESPACE,INVISIBLE_CHARACTER,INVALID_VALUE_TYPEandUNTRANSLATED_SCRIPT. It was skipped outright before, so an empty string, a trailing space or a zero width character inen.jsonwas never reported. The comparison checks still say nothing about it, since it is what they compare against. None of the five reports aterror, so a run that passed still passes; switch one off withignoreChecksthe way you would for any other language - An issue about the target language's own value carries no
targetValue. What a report quotes beside a finding is the value it would be compared to, and for the target language that is the value already shown
Fixed #
- The
githubreporter writes the annotation'sfile=path with forward slashes on every platform. On a Windows runner it wrote the platform's own separator, which GitHub matches against nothing, so the annotation silently attached to no file
1.0.0 - 2026-09-03 #
The first release. A port of the JavaScript package at the same version: the same twenty-five checks, the same option names, the same report, and the same exit code from the command line.
Added #
checkTranslationFilesreads a directory of translation files and compares every language against the target language, in one callanalyzeTranslationscompares translations passed in directly, with no file system workcreateAnalyzerreturns a reusable analyzer whosecheckEntryre-checks a single keyloadTranslationsreads a directory once and returns a session that holds the parsed translations:analyze,checkKey,get,set,remove,keys,translationsandreloadall work on what is already in memory.createSessionis the same for translations passed in directly- Twenty-five checks.
NO_KEY,NO_LOCALE,DUPLICATE_KEY,NO_INTERPOLATION_KEY,EXTRA_INTERPOLATION_KEYandINTERPOLATION_COUNTreport aterrorand fail a run;DUMMY_KEY,EMPTY_VALUE,INVALID_VALUE_TYPE,TAG_MISMATCH,NOT_TRANSLATED_VALUE,UNTRANSLATED_SCRIPT,DUPLICATE_VALUE,INCONSISTENT_VALUE,SURROUNDING_WHITESPACE,INVISIBLE_CHARACTER,MISSING_NUMBER,NUMBER_MISMATCH,NO_PLURAL_FORM,KEY_NAMING,KEY_DEPTHandUNDEFINED_KEYatwarn;UNUSED_KEYandSUSPICIOUS_LENGTHatinfo - Every on-disk layout: one file per locale (
en.json), one folder per locale (en/common.json), and one file holding every locale ({"en": ...}). Files holding the same keys are compared as a group - Five reporters —
pretty,list,json,markdownandgithub— and five grouping axes:locale,code,group,fileandnone.outputwrites a copy to a file, in the shape its extension implies - The
chki18ncommand, which takes the same options as the library and exits with1when anerrorlevel issue was found. Install it withdart pub global activate chki18n package:chki18n/core.dart, the comparison on its own. It imports nodart:io, so it runs in a Flutter web build or anywhere else the file system is not there
Notes on the port #
- Options are one object with named parameters —
Chki18nOptions(target: 'en')— rather than the twenty-four named parameters each entry point would otherwise carry. Every field is optional and every one has the JavaScript default - Enums replace the string unions:
Chki18nCheckCode.noKey,Chki18nLevel.error,Chki18nFileFormat.single. Each carries the spelling the CLI and the JSON reporter share, soChki18nCheckCode.noKey.codeisNO_KEYin every package - The loose text forms a command line writes —
'NO_KEY,EMPTY_VALUE','EMPTY_VALUE=error'— live onChki18nTextOptionsrather than being a second accepted type on every field.resolveOptionsreads both, and a typed field wins over the text one - A translation file whose JSON is not an object is reported as
INVALID_FILErather than read as an empty one - No dependencies. The argument parser, the ANSI colours and the path helpers are written out, because
args,chalkandpackage:pathwould be the only three