@localheroai/cli 0.0.73 → 0.0.74-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,104 @@
1
+ import path from 'path';
2
+ import { promises as fs } from 'fs';
3
+ import { glob } from 'glob';
4
+ import { parseFile, flattenTranslations } from './files.js';
5
+ import { findTargetFile } from './translation-utils.js';
6
+ import { detectProjectType } from './project-detection.js';
7
+ import { detectLocalesFromPaths, looksLikeGettextSource, chooseSourceLocale } from './locale-detection.js';
8
+ const DEFAULT_PATTERN = '**/*.{json,yml,yaml,po,pot}';
9
+ export async function listTranslationFiles(paths, pattern, ignore) {
10
+ const found = await Promise.all(paths.map((dir) => glob(path.join(dir, pattern), { ignore, nodir: true, follow: true })));
11
+ return found.flat().sort();
12
+ }
13
+ export const defaultConfigDetectionDeps = {
14
+ detectProjectType,
15
+ listFiles: listTranslationFiles,
16
+ readFile: (filePath) => fs.readFile(filePath, 'utf8')
17
+ };
18
+ async function countKeys(files, readFile) {
19
+ let total = 0;
20
+ for (const file of files) {
21
+ try {
22
+ const format = path.extname(file).slice(1);
23
+ total += Object.keys(flattenTranslations(parseFile(await readFile(file), format, file), '', format)).length;
24
+ }
25
+ catch {
26
+ continue;
27
+ }
28
+ }
29
+ return total;
30
+ }
31
+ async function gettextSourceLocales(locales, readFile) {
32
+ const sources = [];
33
+ for (const [locale, files] of Object.entries(locales)) {
34
+ const catalogs = files.filter((file) => path.extname(file) === '.po');
35
+ if (catalogs.length === 0)
36
+ continue;
37
+ const contents = await Promise.all(catalogs.map(readFile));
38
+ if (contents.every(looksLikeGettextSource))
39
+ sources.push(locale);
40
+ }
41
+ return sources;
42
+ }
43
+ // init's generic detection writes **/*.po, which would hide a .pot source template.
44
+ function withTemplates(pattern) {
45
+ if (pattern.includes('pot'))
46
+ return pattern;
47
+ if (pattern.endsWith('*.po'))
48
+ return pattern.replace(/\*\.po$/, '*.{po,pot}');
49
+ return pattern.replace(/\{([^}]*\bpo\b[^}]*)\}$/, '{$1,pot}');
50
+ }
51
+ // Vendored files (config/locales/rails-i18n/fr.yml) share no file with the
52
+ // source, so their languages are not targets unless --locales names them.
53
+ function hasCounterpart(targetFiles, locale, sourceFiles, sourceLocale) {
54
+ const targets = targetFiles.map((file) => ({ path: file, locale, format: path.extname(file).slice(1) }));
55
+ return sourceFiles.some((file) => findTargetFile(targets, locale, { path: file, locale: sourceLocale, format: path.extname(file).slice(1) }, sourceLocale));
56
+ }
57
+ // Ignore entries are joined onto the working directory later, so they must be relative.
58
+ function relativeDir(dir) {
59
+ const relative = path.isAbsolute(dir) ? path.relative(process.cwd(), dir) : dir;
60
+ return relative.endsWith('/') ? relative : `${relative}/`;
61
+ }
62
+ async function scanScope(options, deps) {
63
+ if (options.path) {
64
+ return { paths: [relativeDir(options.path)], pattern: options.pattern ?? DEFAULT_PATTERN, ignore: [] };
65
+ }
66
+ const { defaults } = await deps.detectProjectType();
67
+ return {
68
+ paths: [defaults.translationPath],
69
+ pattern: options.pattern ?? withTemplates(defaults.filePattern),
70
+ ignore: defaults.ignorePaths ?? []
71
+ };
72
+ }
73
+ export async function detectCheckConfig(options, deps = defaultConfigDetectionDeps) {
74
+ const { paths, pattern, ignore: scopeIgnore } = await scanScope(options, deps);
75
+ const { locales, templates, unrecognized } = detectLocalesFromPaths(await deps.listFiles(paths, pattern, scopeIgnore));
76
+ const localeCodes = Object.keys(locales);
77
+ const hasTemplate = templates.length > 0;
78
+ const gettextSources = options.source ? [] : await gettextSourceLocales(locales, deps.readFile);
79
+ const needsKeyCounts = !options.source && gettextSources.length !== 1 && !hasTemplate && !localeCodes.includes('en');
80
+ const keyCounts = {};
81
+ if (needsKeyCounts) {
82
+ for (const [locale, files] of Object.entries(locales))
83
+ keyCounts[locale] = await countKeys(files, deps.readFile);
84
+ }
85
+ const choice = chooseSourceLocale({ explicit: options.source, locales: localeCodes, keyCounts, gettextSources, hasTemplate });
86
+ if (!choice)
87
+ return null;
88
+ const ignore = [...scopeIgnore, ...unrecognized];
89
+ // A source catalog and a .pot describe the same strings; comparing against both doubles every finding.
90
+ if (hasTemplate && locales[choice.locale])
91
+ ignore.push(...templates);
92
+ const candidates = localeCodes.filter((locale) => locale !== choice.locale);
93
+ const sourceFiles = locales[choice.locale] ?? [];
94
+ const matching = sourceFiles.length > 0
95
+ ? candidates.filter((locale) => hasCounterpart(locales[locale], locale, sourceFiles, choice.locale))
96
+ : candidates;
97
+ const outputLocales = options.locales?.length ? options.locales : matching;
98
+ const excluded = options.locales?.length ? [] : candidates.filter((locale) => !matching.includes(locale));
99
+ return {
100
+ config: { sourceLocale: choice.locale, outputLocales, translationFiles: { paths, pattern, ignore } },
101
+ detected: { source: choice.locale, reason: choice.reason, paths, pattern, excluded }
102
+ };
103
+ }
104
+ //# sourceMappingURL=check-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-config.js","sourceRoot":"","sources":["../../src/utils/check-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ import { execFileSync } from 'child_process';
2
+ import path from 'path';
3
+ export const runGit = (args) => execFileSync('git', args, { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024, stdio: ['ignore', 'pipe', 'pipe'] });
4
+ const SHORT_SHA_LENGTH = 7;
5
+ const MISSING_AT_REF = /does not exist in|exists on disk, but not in/;
6
+ function tryGit(git, args) {
7
+ try {
8
+ return git(args).trim();
9
+ }
10
+ catch {
11
+ return null;
12
+ }
13
+ }
14
+ function hasCommit(git, sha) {
15
+ return tryGit(git, ['cat-file', '-e', `${sha}^{commit}`]) !== null;
16
+ }
17
+ /**
18
+ * On a pull request GitHub checks out a merge commit whose first parent is the base branch tip, so
19
+ * diffing against that parent shows exactly what merging the PR changes. A depth-1 checkout has the
20
+ * parent's SHA but not the commit, so it is fetched.
21
+ */
22
+ function pullRequestBase(git, pullRequest) {
23
+ const head = tryGit(git, ['rev-parse', 'HEAD']);
24
+ if (pullRequest.sha && head !== pullRequest.sha)
25
+ return null;
26
+ const headObject = tryGit(git, ['cat-file', '-p', 'HEAD']) ?? '';
27
+ const parents = [...headObject.matchAll(/^parent (\S+)$/gm)].map((match) => match[1]);
28
+ if (parents.length < 2)
29
+ return null;
30
+ const base = parents[0];
31
+ const short = base.slice(0, SHORT_SHA_LENGTH);
32
+ if (!hasCommit(git, base)) {
33
+ tryGit(git, ['fetch', '--no-tags', '--depth=1', 'origin', base]);
34
+ if (!hasCommit(git, base))
35
+ return { error: `the base commit ${short} is not in this checkout and could not be fetched` };
36
+ }
37
+ return { ref: base, label: `${pullRequest.baseRef} (${short})` };
38
+ }
39
+ function resolveBranch(git, branch) {
40
+ for (const ref of [branch, `origin/${branch}`, `refs/remotes/origin/${branch}`]) {
41
+ if (tryGit(git, ['rev-parse', '--verify', '--quiet', `${ref}^{commit}`]))
42
+ return ref;
43
+ }
44
+ return null;
45
+ }
46
+ /** Unlike translate's resolveCompareRef, no fallback to the branch tip: that would blame the PR for the base's later commits. */
47
+ function branchBase(git, branches) {
48
+ for (const branch of branches) {
49
+ const ref = resolveBranch(git, branch);
50
+ if (!ref)
51
+ continue;
52
+ const mergeBase = tryGit(git, ['merge-base', ref, 'HEAD']);
53
+ if (mergeBase)
54
+ return { ref: mergeBase, label: ref };
55
+ return { error: `this checkout has no commit in common with ${ref}, likely a shallow clone` };
56
+ }
57
+ return { error: `the base branch ${branches.join(' or ')} was not found` };
58
+ }
59
+ export function resolveChangeBase(git, options) {
60
+ if (tryGit(git, ['rev-parse', '--git-dir']) === null)
61
+ return { error: 'this is not a git repository' };
62
+ const fromPullRequest = options.pullRequest ? pullRequestBase(git, options.pullRequest) : null;
63
+ if (fromPullRequest && 'ref' in fromPullRequest)
64
+ return fromPullRequest;
65
+ const fromBranch = branchBase(git, options.baseBranches);
66
+ if ('ref' in fromBranch || !fromPullRequest)
67
+ return fromBranch;
68
+ return fromPullRequest;
69
+ }
70
+ export function relativeToCwd(filePath) {
71
+ return path.relative(process.cwd(), path.resolve(filePath)).split(path.sep).join('/');
72
+ }
73
+ /** Files renamed since `ref`, as a map from the current path to the path at `ref`, both relative to the working directory. */
74
+ export function renamesSince(git, ref) {
75
+ const fields = git(['diff', '--name-status', '-z', '--find-renames', '--diff-filter=R', '--relative', ref]).split('\0');
76
+ const renames = new Map();
77
+ for (let i = 0; i + 2 < fields.length; i += 3)
78
+ renames.set(fields[i + 2], fields[i + 1]);
79
+ return renames;
80
+ }
81
+ /** The file's content at `ref`, or null when it did not exist there. Throws when git itself fails. */
82
+ export function readFileAtRef(git, ref, filePath) {
83
+ try {
84
+ return git(['show', `${ref}:./${relativeToCwd(filePath)}`]);
85
+ }
86
+ catch (error) {
87
+ const { message, stderr } = error;
88
+ if (MISSING_AT_REF.test(`${message}\n${String(stderr ?? '')}`))
89
+ return null;
90
+ throw error;
91
+ }
92
+ }
93
+ //# sourceMappingURL=check-git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-git.js","sourceRoot":"","sources":["../../src/utils/check-git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ const MAX_LISTED_PROBLEMS = 50;
2
+ const COLUMNS = [
3
+ ['missing', 'Missing'],
4
+ ['placeholders', 'Placeholders'],
5
+ ['plurals', 'Plural forms'],
6
+ ['structure', 'Structure'],
7
+ ['conflicts', 'Conflicts'],
8
+ ['orphans', 'Orphans']
9
+ ];
10
+ export function plural(count, word) {
11
+ return `${count} ${word}${count === 1 ? '' : 's'}`;
12
+ }
13
+ function escapeMarkdown(text) {
14
+ return text.replace(/[\\`*_[\]<>|]/g, '\\$&');
15
+ }
16
+ function statusLine(input) {
17
+ const { changes, problems } = input;
18
+ if (changes === null)
19
+ return problems.length ? `${plural(problems.length, 'problem')} found.` : 'No problems found.';
20
+ if ('error' in changes) {
21
+ return [
22
+ '> [!WARNING]',
23
+ `> Could not compare with the base branch: ${escapeMarkdown(changes.error)}.`,
24
+ '> Checked every key instead. These problems do not fail the check.',
25
+ '> Check out with `fetch-depth: 0` in actions/checkout if this keeps happening.'
26
+ ].join('\n');
27
+ }
28
+ const found = problems.length ? `${plural(problems.length, 'new problem')}.` : 'No new problems.';
29
+ return `Compared with \`${changes.base}\`: ${found}`;
30
+ }
31
+ function problemList(problems) {
32
+ const shown = problems.slice(0, MAX_LISTED_PROBLEMS);
33
+ const lines = [];
34
+ for (const locale of new Set(shown.map((p) => p.locale))) {
35
+ lines.push(`#### ${locale}`, '');
36
+ for (const problem of shown.filter((p) => p.locale === locale)) {
37
+ lines.push(`- ${escapeMarkdown(problem.message)} in \`${problem.file}\``);
38
+ }
39
+ lines.push('');
40
+ }
41
+ if (problems.length > shown.length) {
42
+ lines.push(`${problems.length - shown.length} more not shown. Run check --json for the full report.`, '');
43
+ }
44
+ return lines;
45
+ }
46
+ function countsTable(counts) {
47
+ const rows = counts.filter(({ counts: c }) => COLUMNS.some(([column]) => c[column] > 0));
48
+ if (rows.length === 0)
49
+ return [];
50
+ return [
51
+ `| Locale | ${COLUMNS.map(([, title]) => title).join(' | ')} |`,
52
+ `| --- | ${COLUMNS.map(() => '---').join(' | ')} |`,
53
+ ...rows.map(({ locale, counts: c }) => `| ${locale} | ${COLUMNS.map(([column]) => c[column]).join(' | ')} |`),
54
+ ''
55
+ ];
56
+ }
57
+ export function buildStepSummary(input) {
58
+ const lines = ['## Translation check', '', statusLine(input), '', ...problemList(input.problems)];
59
+ const table = countsTable(input.counts);
60
+ if (table.length && input.changes && 'base' in input.changes) {
61
+ lines.push('### Existing problems', '', 'Already on the base branch. They do not fail the check.', '', ...table);
62
+ }
63
+ else if (table.length && input.changes) {
64
+ lines.push('### Problems found', '', ...table);
65
+ }
66
+ if (input.missingTranslations) {
67
+ lines.push('Localhero.ai can fill missing translations automatically: https://localhero.ai', '');
68
+ }
69
+ return lines.join('\n');
70
+ }
71
+ //# sourceMappingURL=check-summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-summary.js","sourceRoot":"","sources":["../../src/utils/check-summary.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,306 @@
1
+ import { placeholderMultiset, isStrftimeFormat } from './placeholders.js';
2
+ /** Unwraps a .po-style {value, context?, metadata?} leaf down to its string. */
3
+ export function toStringValue(entry) {
4
+ if (entry === null || entry === undefined)
5
+ return null;
6
+ if (typeof entry === 'string')
7
+ return entry;
8
+ if (typeof entry === 'number' || typeof entry === 'boolean')
9
+ return String(entry);
10
+ if (Array.isArray(entry))
11
+ return null;
12
+ if (typeof entry === 'object' && 'value' in entry)
13
+ return toStringValue(entry.value);
14
+ return null;
15
+ }
16
+ function shapeOf(entry) {
17
+ if (entry === null || entry === undefined)
18
+ return 'empty';
19
+ if (Array.isArray(entry))
20
+ return 'array';
21
+ if (typeof entry === 'object' && !('value' in entry))
22
+ return 'map';
23
+ return 'string';
24
+ }
25
+ export function findOrphanKeys(sourceKeys, targetKeys) {
26
+ const orphans = [];
27
+ const sourcePluralBases = gettextPluralBases(Object.keys(sourceKeys));
28
+ const sourcePluralGroups = railsPluralGroups(sourceKeys);
29
+ for (const key of Object.keys(targetKeys)) {
30
+ // Arabic has six plural forms where English has two; the extra ones are not leftovers.
31
+ const extraPluralForm = GETTEXT_PLURAL_SUFFIX.test(key) && sourcePluralBases.has(key.replace(GETTEXT_PLURAL_SUFFIX, ''));
32
+ // Polish `few`/`many` under an English `one`/`other` group are what the language needs.
33
+ const railsPluralForm = RAILS_PLURAL_LEAVES.has(leafOf(key)) && sourcePluralGroups.has(parentOf(key));
34
+ if (!(key in sourceKeys) && !extraPluralForm && !railsPluralForm) {
35
+ orphans.push({ key, target: toStringValue(targetKeys[key]) });
36
+ }
37
+ }
38
+ return orphans;
39
+ }
40
+ /**
41
+ * A `zero`/`one`/`two` form may omit a placeholder ("1 språk" for "%{count} language"), as may any
42
+ * numbered gettext form. Such omissions are hints; anything else, including an added placeholder, is a mismatch.
43
+ */
44
+ export function findPlaceholderMismatches(sourceKeys, targetKeys) {
45
+ const mismatches = [];
46
+ const pluralBases = gettextPluralBases([...Object.keys(sourceKeys), ...Object.keys(targetKeys)]);
47
+ for (const key of Object.keys(sourceKeys)) {
48
+ const source = toStringValue(sourceKeys[key]);
49
+ if (source === null || source === '')
50
+ continue;
51
+ const target = toStringValue(targetKeys[key]);
52
+ if (target === null || target === '')
53
+ continue;
54
+ if (isStrftimeFormat(source) || isStrftimeFormat(target))
55
+ continue;
56
+ const sourceCounts = placeholderMultiset(source);
57
+ const targetCounts = placeholderMultiset(target);
58
+ if (sourceCounts.size === 0 && targetCounts.size === 0)
59
+ continue;
60
+ const missingInTarget = [];
61
+ const unexpectedInTarget = [];
62
+ for (const [token, count] of sourceCounts) {
63
+ if ((targetCounts.get(token) ?? 0) < count)
64
+ missingInTarget.push(token);
65
+ }
66
+ const pluralForm = isPluralFormKey(key) || pluralBases.has(key);
67
+ for (const [token, count] of targetCounts) {
68
+ if (pluralForm && token.endsWith(':count'))
69
+ continue;
70
+ if ((sourceCounts.get(token) ?? 0) < count)
71
+ unexpectedInTarget.push(token);
72
+ }
73
+ if (missingInTarget.length === 0 && unexpectedInTarget.length === 0)
74
+ continue;
75
+ // A gettext singular is compared against whichever form the target language puts first.
76
+ const omissionInPluralForm = unexpectedInTarget.length === 0 && (mayOmitPlaceholder(key) || pluralBases.has(key));
77
+ mismatches.push({
78
+ key,
79
+ source,
80
+ target,
81
+ missingInTarget,
82
+ unexpectedInTarget,
83
+ ...(omissionInPluralForm ? { hint: true } : {})
84
+ });
85
+ }
86
+ return mismatches;
87
+ }
88
+ /**
89
+ * Flattening turns a target map into child keys, so a source string that is only a parent in the
90
+ * target became a map. A target that pluralizes a flat string is allowed, as Rails supports that.
91
+ */
92
+ export function findStructureMismatches(sourceKeys, targetKeys) {
93
+ const mismatches = [];
94
+ const targetChildren = childLeavesByParent(Object.keys(targetKeys));
95
+ for (const key of Object.keys(sourceKeys)) {
96
+ const sourceShape = shapeOf(sourceKeys[key]);
97
+ if (!(key in targetKeys)) {
98
+ const children = targetChildren.get(key);
99
+ if (sourceShape === 'string' && children && !isPluralLeafSet(children)) {
100
+ mismatches.push({ key, sourceShape, targetShape: 'map' });
101
+ }
102
+ continue;
103
+ }
104
+ const targetShape = shapeOf(targetKeys[key]);
105
+ if (sourceShape === 'empty' || targetShape === 'empty')
106
+ continue;
107
+ if (sourceShape !== targetShape) {
108
+ mismatches.push({ key, sourceShape, targetShape });
109
+ }
110
+ }
111
+ return mismatches;
112
+ }
113
+ export function findPluralizedFlatKeys(sourceKeys, targetKeys) {
114
+ const targetChildren = childLeavesByParent(Object.keys(targetKeys));
115
+ return Object.keys(sourceKeys).filter((key) => {
116
+ const children = targetChildren.get(key);
117
+ return !(key in targetKeys) && shapeOf(sourceKeys[key]) === 'string' && children && isPluralLeafSet(children);
118
+ });
119
+ }
120
+ function childLeavesByParent(keys) {
121
+ const children = new Map();
122
+ for (const key of keys) {
123
+ let end = key.lastIndexOf('.');
124
+ let child = key.slice(end + 1);
125
+ while (end !== -1) {
126
+ const parent = key.slice(0, end);
127
+ if (!children.has(parent))
128
+ children.set(parent, new Set());
129
+ children.get(parent).add(child);
130
+ child = parent.slice(parent.lastIndexOf('.') + 1);
131
+ end = parent.lastIndexOf('.');
132
+ }
133
+ }
134
+ return children;
135
+ }
136
+ function isPluralLeafSet(leaves) {
137
+ return [...leaves].every((leaf) => RAILS_PLURAL_LEAVES.has(leaf));
138
+ }
139
+ /** Mirrors po-utils.ts's PLURAL_PREFIX; not imported so this module stays free of parser dependencies. */
140
+ const GETTEXT_PLURAL_SUFFIX = /__plural_\d+$/;
141
+ const COUNT_OPTIONAL_CATEGORIES = ['zero', 'one', 'two'];
142
+ // Rails and i18next always pass `count` to a plural form, so a form may add it.
143
+ function isPluralFormKey(key) {
144
+ if (GETTEXT_PLURAL_SUFFIX.test(key))
145
+ return true;
146
+ const leaf = leafOf(key);
147
+ return RAILS_PLURAL_LEAVES.has(leaf) || I18NEXT_PLURAL_SUFFIXES.some((suffix) => leaf.endsWith(suffix));
148
+ }
149
+ function mayOmitPlaceholder(key) {
150
+ if (GETTEXT_PLURAL_SUFFIX.test(key))
151
+ return true;
152
+ const leaf = leafOf(key);
153
+ return COUNT_OPTIONAL_CATEGORIES.some((category) => leaf === category || leaf.endsWith(`_${category}`));
154
+ }
155
+ function gettextPluralBases(keys) {
156
+ const bases = new Set();
157
+ for (const key of keys) {
158
+ if (GETTEXT_PLURAL_SUFFIX.test(key))
159
+ bases.add(key.replace(GETTEXT_PLURAL_SUFFIX, ''));
160
+ }
161
+ return bases;
162
+ }
163
+ const RAILS_PLURAL_LEAVES = new Set(['zero', 'one', 'two', 'few', 'many', 'other']);
164
+ const I18NEXT_PLURAL_SUFFIXES = ['_zero', '_one', '_two', '_few', '_many', '_other', '_plural'];
165
+ // A lone `status.other` is not a plural group (see translation-utils.ts's isPluralForm);
166
+ // it takes at least two CLDR-category siblings under one parent.
167
+ function pluralParentsOf(keys) {
168
+ const railsCandidates = new Map();
169
+ const i18nextParents = new Set();
170
+ for (const key of keys) {
171
+ const lastDot = key.lastIndexOf('.');
172
+ const leaf = leafOf(key);
173
+ const parent = lastDot === -1 ? '' : key.slice(0, lastDot);
174
+ if (RAILS_PLURAL_LEAVES.has(leaf)) {
175
+ if (!railsCandidates.has(parent))
176
+ railsCandidates.set(parent, new Set());
177
+ railsCandidates.get(parent).add(leaf);
178
+ continue;
179
+ }
180
+ for (const suffix of I18NEXT_PLURAL_SUFFIXES) {
181
+ if (leaf.endsWith(suffix)) {
182
+ const base = leaf.slice(0, -suffix.length);
183
+ i18nextParents.add(parent ? `${parent}.${base}` : base);
184
+ }
185
+ }
186
+ }
187
+ const parents = new Set(i18nextParents);
188
+ for (const [parent, categories] of railsCandidates) {
189
+ if (categories.size >= 2)
190
+ parents.add(parent);
191
+ }
192
+ return parents;
193
+ }
194
+ /** A source plural group the target has content for but no plural forms; with no content it is simply missing. */
195
+ export function findPluralShapeMismatches(sourceKeys, targetKeys) {
196
+ const sourceParents = pluralParentsOf(Object.keys(sourceKeys));
197
+ const targetParents = pluralParentsOf(Object.keys(targetKeys));
198
+ const targetKeyList = Object.keys(targetKeys);
199
+ const mismatches = [];
200
+ for (const parent of sourceParents) {
201
+ if (targetParents.has(parent))
202
+ continue;
203
+ const hasAnyTargetKeyUnderParent = targetKeyList.some((k) => k === parent || k.startsWith(`${parent}.`));
204
+ if (hasAnyTargetKeyUnderParent) {
205
+ mismatches.push({ key: parent });
206
+ }
207
+ }
208
+ return mismatches;
209
+ }
210
+ /** Identical text is only a hint, never an error: short words and proper nouns are often the same across languages. */
211
+ export function findEmptyAndIdentical(sourceKeys, targetKeys) {
212
+ const empty = [];
213
+ const identical = [];
214
+ for (const key of Object.keys(sourceKeys)) {
215
+ const source = toStringValue(sourceKeys[key]);
216
+ if (source === null || source === '')
217
+ continue;
218
+ if (!(key in targetKeys))
219
+ continue;
220
+ const target = toStringValue(targetKeys[key]);
221
+ if (target === '') {
222
+ empty.push({ key, source });
223
+ }
224
+ else if (target === source) {
225
+ identical.push({ key, value: source });
226
+ }
227
+ }
228
+ return { empty, identical };
229
+ }
230
+ /** E.g. Polish without `few`/`many`: Rails silently falls back to `other`, rendering fluent but wrong text. */
231
+ export function findMissingPluralCategories(targetKeys, locale) {
232
+ const required = usedPluralCategories(locale);
233
+ if (!required)
234
+ return [];
235
+ const findings = [];
236
+ for (const [key, present] of railsPluralGroups(targetKeys)) {
237
+ const missing = required.filter((category) => !present.has(category));
238
+ if (missing.length)
239
+ findings.push({ key, missing });
240
+ }
241
+ return findings;
242
+ }
243
+ /**
244
+ * Rails merges every YAML file of a locale into one namespace, so a key defined twice with
245
+ * different values silently takes whichever file loads last. `values[i]` is the value in `files[i]`.
246
+ */
247
+ export function findConflictingKeys(files) {
248
+ const definitions = new Map();
249
+ for (const { path, keys } of files) {
250
+ for (const [key, entry] of Object.entries(keys)) {
251
+ const value = Array.isArray(entry) ? JSON.stringify(entry) : toStringValue(entry);
252
+ if (value === null)
253
+ continue;
254
+ if (!definitions.has(key))
255
+ definitions.set(key, { files: [], values: [] });
256
+ const definition = definitions.get(key);
257
+ definition.files.push(path);
258
+ definition.values.push(value);
259
+ }
260
+ }
261
+ const conflicts = [];
262
+ for (const [key, definition] of definitions) {
263
+ if (new Set(definition.values).size > 1)
264
+ conflicts.push({ key, ...definition });
265
+ }
266
+ return conflicts;
267
+ }
268
+ function leafOf(key) {
269
+ return key.slice(key.lastIndexOf('.') + 1);
270
+ }
271
+ function parentOf(key) {
272
+ return key.slice(0, key.lastIndexOf('.'));
273
+ }
274
+ /** Rails plural groups: a parent with `other` plus at least one more plural leaf. */
275
+ function railsPluralGroups(keys) {
276
+ const groups = new Map();
277
+ for (const key of Object.keys(keys)) {
278
+ const leaf = leafOf(key);
279
+ if (!key.includes('.') || !RAILS_PLURAL_LEAVES.has(leaf))
280
+ continue;
281
+ const parent = parentOf(key);
282
+ if (!groups.has(parent))
283
+ groups.set(parent, new Set());
284
+ groups.get(parent).add(leaf);
285
+ }
286
+ for (const [parent, leaves] of groups) {
287
+ if (!leaves.has('other') || leaves.size < 2)
288
+ groups.delete(parent);
289
+ }
290
+ return groups;
291
+ }
292
+ /**
293
+ * Null for an unknown locale. Only categories reached by 0..1000 count: CLDR gives es/fr/it/pt
294
+ * a `many` for 1,000,000 that rails-i18n does not implement and no one writes.
295
+ */
296
+ export function usedPluralCategories(locale) {
297
+ try {
298
+ const rules = new Intl.PluralRules(locale);
299
+ const reached = new Set(Array.from({ length: 1001 }, (_, n) => rules.select(n)));
300
+ return rules.resolvedOptions().pluralCategories.filter((category) => reached.has(category));
301
+ }
302
+ catch {
303
+ return null;
304
+ }
305
+ }
306
+ //# sourceMappingURL=check-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-utils.js","sourceRoot":"","sources":["../../src/utils/check-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export function detectCiContext(env) {
2
+ const githubActions = env.GITHUB_ACTIONS === 'true';
3
+ const baseRef = env.GITHUB_BASE_REF;
4
+ return {
5
+ githubActions,
6
+ pullRequest: githubActions && baseRef ? { baseRef, ...(env.GITHUB_SHA ? { sha: env.GITHUB_SHA } : {}) } : null,
7
+ stepSummaryPath: env.GITHUB_STEP_SUMMARY || null
8
+ };
9
+ }
10
+ //# sourceMappingURL=ci-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci-context.js","sourceRoot":"","sources":["../../src/utils/ci-context.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,eAAe,CAAC,GAAQ;IACtC,MAAM,aAAa,GAAG,GAAG,CAAC,cAAc,KAAK,MAAM,CAAC;IACpD,MAAM,OAAO,GAAG,GAAG,CAAC,eAAe,CAAC;IACpC,OAAO;QACL,aAAa;QACb,WAAW,EAAE,aAAa,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI;QAC9G,eAAe,EAAE,GAAG,CAAC,mBAAmB,IAAI,IAAI;KACjD,CAAC;AACJ,CAAC"}