diff --git a/.gitignore b/.gitignore index c54929c4..91b8519e 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,13 @@ yarn-error.log !.yarn/releases !.yarn/sdks !.yarn/versions + +# other dev tooling +repomix-output.* +repomix* +mise.toml +mise.yaml + +# temp dev scripts 2025-07-14 and beyond. will remove afterwards +key-mappings.json +replace-translation-keys.js diff --git a/__tests__/missingTranslations.js b/__tests__/missingTranslations.js index ed62dbe2..bcbf6c8c 100644 --- a/__tests__/missingTranslations.js +++ b/__tests__/missingTranslations.js @@ -1,16 +1,97 @@ const { exec } = require('child_process'); +const fs = require('fs'); +const path = require('path'); -exec('tsc --noEmit', (error, stdout, stderr) => { +console.log('TRANSLATION ANALYSIS'); +console.log('=' + '='.repeat(50)); + +// First, run the translation analysis +try { + // Read settings.json to get language configuration + const settingsPath = path.join(__dirname, '..', 'minibits.inlang', 'settings.json'); + const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); + + const defaultLanguage = settings.sourceLanguageTag; + const allLanguages = settings.languageTags; + + console.log(`Default language: ${defaultLanguage}`); + console.log(`All languages: ${allLanguages.join(', ')}`); + console.log('=' + '='.repeat(50)); + + // Load all translation files + const translations = {}; + const i18nMessagesPath = path.join(__dirname, '..', 'src', 'i18n_messages'); + + allLanguages.forEach(lang => { + const filePath = path.join(i18nMessagesPath, `${lang}.json`); + if (fs.existsSync(filePath)) { + try { + translations[lang] = JSON.parse(fs.readFileSync(filePath, 'utf8')); + } catch (error) { + console.error(`Error reading ${lang}.json:`, error.message); + translations[lang] = {}; + } + } else { + console.warn(`Translation file not found: ${lang}.json`); + translations[lang] = {}; + } + }); + + // Get keys from default language + const defaultKeys = Object.keys(translations[defaultLanguage] || {}); + const totalKeys = defaultKeys.length; + + console.log(`Total keys in default language (${defaultLanguage}): ${totalKeys}`); + console.log('=' + '='.repeat(50)); + + // Check each language + allLanguages.forEach(lang => { + if (lang === defaultLanguage) { + console.log(`\n${lang.toUpperCase()} (default language)`); + console.log(`โœ… 100% translated (${totalKeys}/${totalKeys} keys)`); + return; + } + + const currentKeys = Object.keys(translations[lang] || {}); + const currentTotal = currentKeys.length; + const missingKeys = defaultKeys.filter(key => !translations[lang].hasOwnProperty(key)); + const percentageTranslated = totalKeys > 0 ? Math.round((currentTotal / totalKeys) * 100) : 0; + + console.log(`\n${lang.toUpperCase()}`); + console.log(`๐Ÿ“Š ${percentageTranslated}% translated (${currentTotal}/${totalKeys} keys)`); + + if (missingKeys.length > 0) { + console.log(`โŒ Missing ${missingKeys.length} keys:`); + missingKeys.forEach(key => { + console.log(` - ${key}`); + }); + } else { + console.log(`โœ… No missing keys!`); + } + }); + + console.log('\n' + '=' + '='.repeat(50)); + console.log('โœ… Translation analysis complete!'); +} catch (translationError) { + console.error('โŒ Error during translation analysis:', translationError.message); +} + +console.log('\n' + '=' + '='.repeat(50)); +console.log('TYPESCRIPT ERROR CHECK'); +console.log('=' + '='.repeat(50)); + +// Now run the TypeScript error checking +exec('tsc --noEmit', (error, stdout) => { if (error) { const output = stdout; const filteredErrors = output.split('\n').filter(line => line.includes('TxKeyPath')); if (filteredErrors.length > 0) { + console.log('โŒ TypeScript errors containing TxKeyPath found:'); console.log(filteredErrors.join('\n')); } else { - console.log('No errors containing TxKeyPath found.'); + console.log('โœ… No TypeScript errors containing TxKeyPath found.'); } - // process.exit(1); // Ensure the script returns an error code if there were any errors } else { - console.log('No TypeScript errors found.'); + console.log('โœ… No TypeScript errors found.'); } }); diff --git a/minibits.inlang/settings.json b/minibits.inlang/settings.json index 18ab7a59..b57eb50b 100644 --- a/minibits.inlang/settings.json +++ b/minibits.inlang/settings.json @@ -8,7 +8,7 @@ "modules": [ "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@latest/dist/index.js", - "https://cdn.jsdelivr.net/gh/KraXen72/inlang-t-function-jsx-hybrid-matcher/dist/index.js", + "https://cdn.jsdelivr.net/gh/KraXen72/inlang-t-function-jsx-hybrid-matcher@main/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/plugin-json@latest/dist/index.js" ], "plugin.inlang.json": { @@ -26,7 +26,11 @@ "recognizedJSXAttributes": [ "tx", "subTx", - "contentTx" + "labelTx", + "titleTx", + "headingTx", + "contentTx", + "footerTx" ] } } \ No newline at end of file diff --git a/package.json b/package.json index 9a9e61ed..2513a75b 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "adb:unreverse": "adb devices && adb reverse --remove-all", "adb:inspect": "adb shell input keyevent 82", "postinstall": "patch-package --exclude 'nothing'", - "clean": "npx react-native-clean-project" + "clean": "npx react-native-clean-project", + "machine-translate": "npx @inlang/cli machine translate --project minibits.inlang" }, "dependencies": { "@cashu/cashu-ts": "minibits-cash/cashu-ts#b3ba36757d84ec03f1064029db02308eb3a09cc4", diff --git a/src/components/CollapsibleText.tsx b/src/components/CollapsibleText.tsx index 58d488df..81f4e9e9 100644 --- a/src/components/CollapsibleText.tsx +++ b/src/components/CollapsibleText.tsx @@ -46,7 +46,7 @@ export const CollapsibleText = (props: CollapsibleProps) => { {...props.textProps} /> - + diff --git a/src/components/MemoInputCard.tsx b/src/components/MemoInputCard.tsx index 7cfe6664..e0ab2ffe 100644 --- a/src/components/MemoInputCard.tsx +++ b/src/components/MemoInputCard.tsx @@ -42,7 +42,7 @@ export const MemoInputCard = forwardRef((props, memoI maxLength={maxLength} keyboardType="default" selectTextOnFocus={true} - placeholder={translate('sendScreen.memo')} + placeholder={translate('sendScreen_memo')} placeholderTextColor={placeholderTextColor} editable={!disabled} /> diff --git a/src/components/ProfileHeader.tsx b/src/components/ProfileHeader.tsx index 24ea4e7d..fcca6194 100644 --- a/src/components/ProfileHeader.tsx +++ b/src/components/ProfileHeader.tsx @@ -16,7 +16,7 @@ export const ProfileHeader = observer(function (props: ProfileHeaderProps) { const { picture, nip05 } = walletProfileStore return ( {