Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ build/Release
node_modules/
jspm_packages/

# Include node modules used in tests
!fixtures/**/node_modules

# TypeScript v1 declaration files
typings/

Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,26 @@ To prevent that from happening you can configure a `whitelist`, which accepts an
array of regular expressions that will be checked when looking for unused
translations.

### `externalPaths`

If your application uses translations provided by (external) addons, then those
translations will show up as missing by default. In order to include such translations,
you can define `externalPaths` in the configuration file as follows:

```js
export default {
externalPaths: ['my-addon'],
};
```

This example will try to find translation files in `node_modules/my-addon/translations`.
Patterns supported by [`globby`](https://www.npmjs.com/package/globby) are also
possible here, e.g. this:
```js
externalPaths: ['@*/*']
```
will look up translations in scoped addons like `node_modules/@company/scoped-addon/translations`.

### `--fix`
If your application has a lot of unused translations you can run the command with
the `--fix` to remove them. Remember to double check your translations as dynamic
Expand Down
16 changes: 16 additions & 0 deletions __snapshots__/test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,22 @@ exports[`Test Fixtures emblem 1`] = `

exports[`Test Fixtures emblem 2`] = `Map {}`;

exports[`Test Fixtures external-addon-translations 1`] = `
"[1/4] 🔍 Finding JS and HBS files...
[2/4] 🔍 Searching for translations keys in JS and HBS files...
[3/4] ⚙️ Checking for unused translations...
[4/4] ⚙️ Checking for missing translations...

👏 No unused translations were found!

⚠️ Found 1 missing translations!

- other-external-addon.used-by-app-translation (used in app/templates/application.hbs)
"
`;

exports[`Test Fixtures external-addon-translations 2`] = `Map {}`;

exports[`Test Fixtures in-repo-translations 1`] = `
"[1/4] 🔍 Finding JS and HBS files...
[2/4] 🔍 Searching for translations keys in JS and HBS files...
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Controller from '@ember/controller';

export default class ApplicationController extends Controller {
get foo() {
return this.intl.t('js-translation');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{{t "hbs-translation"}}
{{t "external-addon.used-by-app-translation"}}
{{t "other-external-addon.used-by-app-translation"}}
{{t "company.scoped-addon.used-by-app-translation"}}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions fixtures/external-addon-translations/translations/en.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
hbs-translation: HBS!
js-translation: JS!
49 changes: 42 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ async function run(rootDir, options = {}) {
const NUM_STEPS = 4;
const step = num => chalk.dim(`[${num}/${NUM_STEPS}]`);

let config = readConfig(rootDir);
let config = options.config || readConfig(rootDir);

log(`${step(1)} 🔍 Finding JS and HBS files...`);
let appFiles = await findAppFiles(rootDir);
Expand All @@ -37,12 +37,21 @@ async function run(rootDir, options = {}) {

log(`${step(3)} ⚙️ Checking for unused translations...`);

let translationFiles = await findTranslationFiles(rootDir);
let existingTranslationKeys = await analyzeTranslationFiles(rootDir, translationFiles);
let ownTranslationFiles = await findOwnTranslationFiles(rootDir);
let externalTranslationFiles = await findExternalTranslationFiles(rootDir, config);
let existingOwnTranslationKeys = await analyzeTranslationFiles(rootDir, ownTranslationFiles);
let existingExternalTranslationKeys = await analyzeTranslationFiles(
rootDir,
externalTranslationFiles
);
let existingTranslationKeys = mergeMaps(
existingOwnTranslationKeys,
existingExternalTranslationKeys
);
let whitelist = config.whitelist || [];

let unusedTranslations = findDifferenceInTranslations(
existingTranslationKeys,
existingOwnTranslationKeys,
usedTranslationKeys,
whitelist
);
Expand Down Expand Up @@ -82,7 +91,7 @@ async function run(rootDir, options = {}) {
let totalErrors = missingTranslations.size + unusedTranslations.size;

if (shouldFix) {
removeUnusedTranslations(writeToFile, rootDir, translationFiles, unusedTranslations);
removeUnusedTranslations(writeToFile, rootDir, ownTranslationFiles, unusedTranslations);
log();
log(' 👏 All unused translations were removed');
}
Expand Down Expand Up @@ -113,8 +122,19 @@ async function findInRepoFiles(cwd) {
return globby(joinPaths(inRepoFolders, ['**/*.js', '**/*.hbs', '**/*.emblem']), { cwd });
}

async function findTranslationFiles(cwd) {
let inputFolders = ['', ...findInRepoPaths(cwd)];
async function findOwnTranslationFiles(cwd) {
return findTranslationFiles(cwd, ['', ...findInRepoPaths(cwd)]);
}

async function findExternalTranslationFiles(cwd, config) {
if (!config.externalPaths) {
return [];
}

return findTranslationFiles(cwd, joinPaths('node_modules', config.externalPaths));
}

async function findTranslationFiles(cwd, inputFolders) {
let translationPaths = joinPaths(inputFolders, ['translations']);

return globby(joinPaths(translationPaths, ['**/*.json', '**/*.yaml', '**/*.yml']), {
Expand Down Expand Up @@ -388,4 +408,19 @@ function getNestedAttribute(parent, keys) {
return attribute;
}

function mergeMaps(mapA, mapB) {
let resultMap = new Map([...mapA]);

for (let [key, bFiles] of mapB) {
if (!resultMap.has(key)) {
resultMap.set(key, bFiles);
} else {
let aFiles = resultMap.get(key);
resultMap.set(key, new Set([...aFiles, ...bFiles]));
}
}

return resultMap;
}

module.exports = { run, generateFileList };
7 changes: 7 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@ describe('Test Fixtures', () => {
'missing-translations',
'unused-translations',
'in-repo-translations',
'external-addon-translations',
];
let fixturesWithFix = ['remove-unused-translations', 'remove-unused-translations-nested'];
let fixturesWithConfig = {
'external-addon-translations': {
externalPaths: ['@*/*', 'external-addon'],
},
};

beforeEach(() => {
output = '';
Expand All @@ -34,6 +40,7 @@ describe('Test Fixtures', () => {
fix: fixturesWithFix.includes(fixture),
color: false,
writeToFile,
config: fixturesWithConfig[fixture],
});

let expectedReturnValue = fixturesWithErrors.includes(fixture) ? 1 : 0;
Expand Down