UNPKG

i18ntk

Version:

i18n Tool Kit - Zero-dependency internationalization toolkit for setup, scanning, analysis, validation, auto translation, fixing, reporting, and runtime translation loading.

186 lines (164 loc) 6.31 kB
const fs = require('fs'); const path = require('path'); const configManager = require('./config-manager'); const SecurityUtils = require('./security'); const packageJson = require('../package.json'); function ensureDirectory(dirPath) { if (!dirPath || typeof dirPath !== 'string') return; if (!SecurityUtils.safeExistsSync(dirPath, process.cwd())) { SecurityUtils.safeMkdirSync(dirPath, process.cwd(), { recursive: true }); } } function readJsonSafe(filePath) { try { if (!SecurityUtils.safeExistsSync(filePath)) return null; const raw = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8'); if (!raw) return null; return JSON.parse(raw); } catch { return null; } } function hasSourceLanguageFiles(sourceDir, sourceLanguage) { const baseSourceDir = path.resolve(sourceDir); const modularLanguageDir = path.join(baseSourceDir, sourceLanguage); const singleLanguageFile = path.join(baseSourceDir, `${sourceLanguage}.json`); if (SecurityUtils.safeExistsSync(modularLanguageDir, process.cwd())) { try { const stat = SecurityUtils.safeStatSync(modularLanguageDir, process.cwd()); if (stat && stat.isDirectory()) { const items = SecurityUtils.safeReaddirSync(modularLanguageDir, process.cwd(), { withFileTypes: true }); return items ? items.some(item => item.isFile() && item.name.endsWith('.json')) : false; } } catch { // Continue with single-file fallback. } } return SecurityUtils.safeExistsSync(singleLanguageFile, process.cwd()); } /** * Check if the project is properly initialized * @param {Object} options - Options for initialization check * @returns {Promise<Object>} Object containing initialization status and config */ async function checkInitialized(options = {}) { const settings = configManager.getConfig ? configManager.getConfig() : {}; const currentVersion = packageJson.version; const projectConfigPath = configManager.CONFIG_PATH || path.join(process.cwd(), '.i18ntk-config'); const configDir = path.dirname(projectConfigPath); // Ensure config directory exists ensureDirectory(configDir); const defaultConfig = { sourceDir: settings.sourceDir || './locales', sourceLanguage: settings.sourceLanguage || 'en', projectRoot: path.resolve('.'), framework: settings.framework || { detected: false, prompt: 'always' }, configDir: configDir }; // Primary source of truth in v2: project-level .i18ntk-config const projectConfig = readJsonSafe(projectConfigPath); if (projectConfig?.setup?.completed === true) { return { initialized: true, config: { ...defaultConfig, ...projectConfig, framework: projectConfig.framework || defaultConfig.framework } }; } // Backward compatibility: legacy initialization marker file. const initFilePath = path.join(configDir, 'initialization.json'); const initStatus = readJsonSafe(initFilePath); const isLegacyInitialized = Boolean(initStatus?.initialized) && ( !initStatus.version || initStatus.version.split('.')[0] === currentVersion.split('.')[0] ); if (isLegacyInitialized) { return { initialized: true, config: { ...defaultConfig, ...initStatus, framework: initStatus.framework || defaultConfig.framework } }; } // Final fallback: detect existing source language files and mark initialized. const sourceDir = options.sourceDir || defaultConfig.sourceDir; const sourceLanguage = options.sourceLanguage || defaultConfig.sourceLanguage; const hasLanguageFiles = hasSourceLanguageFiles(sourceDir, sourceLanguage); // If language files exist but no init file, create one if (hasLanguageFiles) { const initData = { initialized: true, version: currentVersion, timestamp: new Date().toISOString(), sourceDir, sourceLanguage, detectedLanguage: defaultConfig.detectedLanguage, detectedFramework: defaultConfig.detectedFramework, lastUpdated: new Date().toISOString() }; ensureDirectory(path.dirname(initFilePath)); SecurityUtils.safeWriteFileSync(initFilePath, JSON.stringify(initData, null, 2), path.dirname(initFilePath), 'utf8'); return { initialized: true, config: { ...defaultConfig, ...initData } }; } return { initialized: false, config: defaultConfig }; } /** * Mark the project as initialized * @param {Object} config - Configuration to save * @returns {Promise<void>} */ async function markAsInitialized(config) { const settings = configManager.getConfig ? configManager.getConfig() : {}; const projectConfigPath = configManager.CONFIG_PATH || path.join(process.cwd(), '.i18ntk-config'); const configDir = path.dirname(projectConfigPath); const initFilePath = path.join(configDir, 'initialization.json'); const currentVersion = packageJson.version; const now = new Date().toISOString(); const sourceDir = config?.sourceDir || settings.sourceDir || './locales'; const sourceLanguage = config?.sourceLanguage || settings.sourceLanguage || 'en'; const initData = { initialized: true, version: currentVersion, timestamp: now, sourceDir, sourceLanguage, detectedLanguage: config?.detectedLanguage || settings.detectedLanguage, detectedFramework: config?.detectedFramework || settings.detectedFramework, lastUpdated: now }; ensureDirectory(path.dirname(initFilePath)); SecurityUtils.safeWriteFileSync(initFilePath, JSON.stringify(initData, null, 2), path.dirname(initFilePath), 'utf8'); const mergedConfig = { ...settings, ...(config || {}), sourceDir, sourceLanguage, version: currentVersion, setup: { ...(settings.setup || {}), completed: true, completedAt: now, version: currentVersion, setupId: settings.setup?.setupId || `setup_${Date.now()}` } }; if (configManager.saveConfig) { await configManager.saveConfig(mergedConfig); } else { SecurityUtils.safeWriteFileSync(projectConfigPath, JSON.stringify(mergedConfig, null, 2), path.dirname(projectConfigPath), 'utf8'); } } module.exports = { checkInitialized, markAsInitialized };