Skip to content

The core entry point

The comparison engine on its own, without the directory scanner, so it runs where there is no file system to read.

Each package publishes it under its own name:

PackageImport
JavaScriptimport { … } from 'chki18n/core'
Dartimport 'package:chki18n/core.dart'
Pythonfrom chki18n.core import …

How it differs from the package root

The package root reads directories, so it imports the file system modules: `node:fs`, `node:path` and `node:os``dart:io``os` and `shutil`. A build that cannot supply them either fails or pulls in a stack of polyfills for code that will never run.

The comparison itself uses none of them. The core entry point is the same engine with the file reading left out:

javascript
import { analyzeTranslations, createAnalyzer, CHECK_META } from 'chki18n/core';

A test walks the subpath's import graph on every build and fails if a Node built-in appears in it.

dart
import 'package:chki18n/core.dart';

A test walks the entry point's import graph on every run and fails if dart:io appears in it. That is what keeps the comparison usable in a Flutter web build.

python
from chki18n.core import CHECK_META, analyze_translations, create_analyzer

A test walks the module's import graph on every run and fails if os, pathlib or shutil appears in it.

What it exports

Everything the root does except the parts that read files:

ExportedNot exported
analyzeTranslations, createAnalyzercheckTranslationFiles
createSession (for translations you pass in)loadTranslations
CHECK_CODE, CHECK_META, ANALYZE_CHECK_CODES, CROSS_KEY_CHECK_CODES, FILE_FORMATscanTranslationDirectory
groupIssuesByCode, summarizeIssues, createIssue, buildResultfindUnusedKeys
resolveOptions, argsToOptions, buildUsageText, OPTION_DEFINITIONS
isLocaleCode, extractInterpolationKeys, detectInterpolationDelimiters
createPathExcluder, createFileExcluder, and every type

The root re-exports all of it, so import { createAnalyzer } from 'chki18n' works too. Reach for the subpath when the bundle must not carry the scanner.

ExportedNot exported
analyzeTranslations, createAnalyzercheckTranslationFiles
createSession (for translations you pass in)loadTranslations
Chki18nCheckCode, checkMeta, analyzeCheckCodes, crossKeyCheckCodes, Chki18nFileFormatscanTranslationDirectory
groupIssuesByCode, summarizeIssues, createIssue, buildResultfindUnusedKeys
resolveOptions, optionsFromArgs, buildUsageText, optionDefinitionsformatResult
isLocaleCode, extractInterpolationKeys, detectInterpolationDelimiters
createPathExcluder, createFileExcluder, and every type

package:chki18n/chki18n.dart re-exports all of it, so one import covers both. Reach for core.dart when the build must not pull dart:io in.

ExportedNot exported
analyze_translations, create_analyzercheck_translation_files
create_session (for translations you pass in)load_translations
CHECK_CODES, CHECK_META, ANALYZE_CHECK_CODES, CROSS_KEY_CHECK_CODES, FILE_FORMATSscan_translation_directory
group_issues_by_code, summarize_issues, create_issue, build_resultfind_unused_keys
resolve_options, options_from_args, build_usage_text, OPTION_DEFINITIONSformat_result
is_locale_code, extract_interpolation_keys, detect_interpolation_delimiters
create_path_excluder, create_file_excluder, and every type

chki18n re-exports all of it, so from chki18n import create_analyzer works too. Reach for chki18n.core when the module must not touch the disk.

Using it

Read the files with whatever the environment already uses, then hand the parsed objects over:

javascript
import { analyzeTranslations } from 'chki18n/core';

const en = await fetch('/locales/en.json').then((res) => res.json());
const ko = await fetch('/locales/ko.json').then((res) => res.json());

const result = analyzeTranslations({ locales: { en, ko } }, { target: 'en' });
dart
import 'dart:convert';

import 'package:chki18n/core.dart';
import 'package:flutter/services.dart' show rootBundle;

final en = jsonDecode(await rootBundle.loadString('locales/en.json'));
final ko = jsonDecode(await rootBundle.loadString('locales/ko.json'));

final result = analyzeTranslations(
  Chki18nInput(locales: {'en': en as Map<String, Object?>, 'ko': ko as Map<String, Object?>}),
  options: const Chki18nOptions(target: 'en'),
);
python
import json

from chki18n.core import Input, Options, analyze_translations

en = json.loads(request.files["en"].read())
ko = json.loads(request.files["ko"].read())

result = analyze_translations(Input(locales={"en": en, "ko": ko}), Options(target="en"))

In an editor

Run a full pass when a project opens, then check one key on every edit:

javascript
import { createAnalyzer } from 'chki18n/core';

const analyzer = createAnalyzer({ target: 'en' });

analyzer.analyze({ groups: everything }); // on open
analyzer.checkEntry({ key, values, locales }); // on each keystroke
dart
import 'package:chki18n/core.dart';

final analyzer = createAnalyzer(options: const Chki18nOptions(target: 'en'));

analyzer.analyze(Chki18nInput(groups: everything)); // on open
analyzer.checkEntry(Chki18nEntry(key: key, values: values, locales: locales)); // on each keystroke
python
from chki18n.core import Entry, Input, Options, create_analyzer

analyzer = create_analyzer(Options(target="en"))

analyzer.analyze(Input(groups=everything))  # on open
analyzer.check_entry(Entry(key=key, values=values, locales=locales))  # on each keystroke

See createAnalyzer for the whole pattern, and for why passing the values in works better than letting chki18n hold a second copy of them.

Dependencies

Two, both of them small and neither of them Node-specific: flat for flattening nested keys, and qsu for extracting interpolation placeholders. chalk and minimist belong to the CLI and are not reachable from here.

None. The one thing the core entry point reaches outside itself is `dart:convert`, for the `json` reporter`json`, `re` and `dataclasses`, all of them standard library.

Released under the MIT License