mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(packages): i18n (#1708)
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
* 4. Package metadata — non-private packages have required fields
|
||||
* 5. Release-please config — every versioned package is registered
|
||||
* 6. Define imports — no bare side-effect imports from relative paths
|
||||
* 7. i18n locales — tag lists match locale files and generated stubs
|
||||
*/
|
||||
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
@@ -324,6 +325,132 @@ function checkDefineImports() {
|
||||
return { ok: warnings.length === 0, warnings };
|
||||
}
|
||||
|
||||
// ── Check 7: i18n locale consistency ─────────────────────────────────────────
|
||||
|
||||
const GENERATED_I18N_HEADER = '/** Generated by packages/core/scripts/generate-i18n-locales.ts — do not edit. */';
|
||||
|
||||
function parseLocaleTagArray(source, exportName) {
|
||||
const match = source.match(new RegExp(`export const ${exportName} = \\[([\\s\\S]*?)\\] as const`));
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
return [...match[1].matchAll(/'([^']+)'/g)].map((m) => m[1]);
|
||||
}
|
||||
|
||||
function localeAliases(tags) {
|
||||
const counts = new Map();
|
||||
for (const tag of tags) {
|
||||
if (!tag.includes('-')) continue;
|
||||
const lang = tag.split('-')[0];
|
||||
counts.set(lang, (counts.get(lang) ?? 0) + 1);
|
||||
}
|
||||
return [...counts].filter(([, count]) => count > 1).map(([lang]) => lang);
|
||||
}
|
||||
|
||||
function checkI18nLocales() {
|
||||
const warnings = [];
|
||||
const builtInPath = join(PACKAGES_DIR, 'core/src/core/i18n/locales.ts');
|
||||
const builtInSource = readText(builtInPath);
|
||||
const locales = parseLocaleTagArray(builtInSource, 'LOCALES');
|
||||
if (locales === undefined) {
|
||||
warnings.push('Could not parse LOCALES from packages/core/src/core/i18n/locales.ts');
|
||||
return { ok: false, warnings };
|
||||
}
|
||||
const localeFiles = [...locales, ...localeAliases(locales)];
|
||||
|
||||
const coreLocalesDir = join(PACKAGES_DIR, 'core/src/core/i18n/locales');
|
||||
if (!existsSync(coreLocalesDir)) {
|
||||
warnings.push('Missing generated locale directory packages/core/src/core/i18n/locales');
|
||||
return { ok: false, warnings };
|
||||
}
|
||||
|
||||
const coreFiles = readdirSync(coreLocalesDir)
|
||||
.filter((file) => file.endsWith('.ts'))
|
||||
.map((file) => file.slice(0, -3));
|
||||
|
||||
const expectedCore = new Set(['all', 'en', ...localeFiles]);
|
||||
|
||||
for (const tag of localeFiles) {
|
||||
if (!coreFiles.includes(tag)) {
|
||||
warnings.push(`LOCALES tag "${tag}" has no packages/core/src/core/i18n/locales/${tag}.ts`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const file of coreFiles) {
|
||||
if (!expectedCore.has(file)) {
|
||||
warnings.push(`Unexpected locale file packages/core/src/core/i18n/locales/${file}.ts (not in locales.ts)`);
|
||||
}
|
||||
}
|
||||
|
||||
const allPath = join(coreLocalesDir, 'all.ts');
|
||||
if (!existsSync(allPath)) {
|
||||
warnings.push('Missing generated locale bundle packages/core/src/core/i18n/locales/all.ts');
|
||||
} else if (!readText(allPath).startsWith(GENERATED_I18N_HEADER)) {
|
||||
warnings.push(
|
||||
'packages/core/src/core/i18n/locales/all.ts is not generated — run pnpm -F @videojs/core generate:locales'
|
||||
);
|
||||
}
|
||||
|
||||
const loadLocalePath = join(PACKAGES_DIR, 'core/src/core/i18n/load-locale.ts');
|
||||
if (!existsSync(loadLocalePath)) {
|
||||
warnings.push('Missing generated locale loader packages/core/src/core/i18n/load-locale.ts');
|
||||
} else {
|
||||
const loadLocaleSource = readText(loadLocalePath);
|
||||
if (!loadLocaleSource.startsWith(GENERATED_I18N_HEADER)) {
|
||||
warnings.push(
|
||||
'packages/core/src/core/i18n/load-locale.ts is not generated — run pnpm -F @videojs/core generate:locales'
|
||||
);
|
||||
} else {
|
||||
const loaderTags = new Set(
|
||||
[...loadLocaleSource.matchAll(/import\('\.\/locales\/([^']+)'\)/g)].map((match) => match[1])
|
||||
);
|
||||
|
||||
for (const tag of localeFiles) {
|
||||
if (!loaderTags.delete(tag)) {
|
||||
warnings.push(`LOCALES tag "${tag}" has no lazy importer in packages/core/src/core/i18n/load-locale.ts`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const tag of loaderTags) {
|
||||
warnings.push(`Unexpected lazy importer packages/core/src/core/i18n/load-locale.ts for "${tag}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const pkg of ['html', 'react']) {
|
||||
const localesDir = join(PACKAGES_DIR, `${pkg}/src/i18n/locales`);
|
||||
const expectedPlatform = new Set(['all', 'en', ...localeFiles]);
|
||||
|
||||
if (!existsSync(localesDir)) {
|
||||
warnings.push(`Missing generated re-export directory packages/${pkg}/src/i18n/locales`);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const tag of expectedPlatform) {
|
||||
const filePath = join(localesDir, `${tag}.ts`);
|
||||
if (!existsSync(filePath)) {
|
||||
warnings.push(`Missing generated re-export packages/${pkg}/src/i18n/locales/${tag}.ts`);
|
||||
continue;
|
||||
}
|
||||
if (!readText(filePath).startsWith(GENERATED_I18N_HEADER)) {
|
||||
warnings.push(
|
||||
`packages/${pkg}/src/i18n/locales/${tag}.ts is not generated — run pnpm -F @videojs/core generate:locales`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const file of readdirSync(localesDir)) {
|
||||
if (!file.endsWith('.ts')) continue;
|
||||
const tag = file.slice(0, -3);
|
||||
if (!expectedPlatform.has(tag)) {
|
||||
warnings.push(`Unexpected locale re-export packages/${pkg}/src/i18n/locales/${file}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { ok: warnings.length === 0, warnings };
|
||||
}
|
||||
|
||||
// ── Main ────────────────────────────────────────────────────────────────────
|
||||
|
||||
const checks = [
|
||||
@@ -334,6 +461,7 @@ const checks = [
|
||||
{ name: 'Release-please config', fn: checkReleasePleaseConfig },
|
||||
{ name: 'Bundled docs publishing', fn: checkBundledDocs },
|
||||
{ name: 'Define imports', fn: checkDefineImports },
|
||||
{ name: 'i18n locales', fn: checkI18nLocales },
|
||||
];
|
||||
|
||||
let failed = 0;
|
||||
|
||||
Reference in New Issue
Block a user