Skip to content

Commit 1c7e43f

Browse files
committed
Add the ability to analyse nested translation folders
1 parent c7f2fb3 commit 1c7e43f

File tree

8 files changed

+100
-36
lines changed

8 files changed

+100
-36
lines changed

README.md

+32-25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
ember-intl-analyzer
2-
==============================================================================
1+
# ember-intl-analyzer
32

43
Find unused translations in your Ember.js projects
54

@@ -9,37 +8,31 @@ Find unused translations in your Ember.js projects
98
> We offer consulting, training, and team augmentation for Ember.js – check out
109
> our [website](https://mainmatter.com/ember-consulting/) to learn more!
1110
12-
Usage
13-
------------------------------------------------------------------------------
11+
## Usage
1412

1513
```bash
1614
npx ember-intl-analyzer
1715
```
1816

19-
20-
Configuration
21-
------------------------------------------------------------------------------
17+
## Configuration
2218

2319
ember-intl-analyzer can be configured by creating a `config/ember-intl-analyzer.js`
2420
file in your app:
2521

2622
```js
2723
export default {
28-
whitelist: [
29-
/^countries\./,
30-
/^currency\./,
31-
/^validations\.errors\./,
32-
/^[^.]+\.warnings\.[^.]+$/,
33-
],
24+
whitelist: [/^countries\./, /^currency\./, /^validations\.errors\./, /^[^.]+\.warnings\.[^.]+$/],
3425
};
3526
```
3627

3728
### `whitelist`
3829

3930
If you use dynamic translations keys like this:
31+
4032
```js
41-
this.intl.t(`countries.${code}`)
33+
this.intl.t(`countries.${code}`);
4234
```
35+
4336
then ember-intl-analyzer can not easily understand what translation keys are
4437
being used here. In that case it will ignore the dynamic translation key and
4538
show the corresponding translations as unused.
@@ -64,15 +57,18 @@ export default {
6457
### `analyzeConcatExpression`
6558

6659
If your template contains translations like this:
60+
6761
```hbs
68-
{{t (concat "actions." (if @isEditing "save" "publish"))}}
62+
{{t (concat 'actions.' (if @isEditing 'save' 'publish'))}}
6963
```
64+
7065
then ember-intl-analyzer does not detect that `actions.save` and `actions.publish`
7166
are in fact used translations, so they can be incorrectly flagged as missing or
7267
unused. As the `concat` helper can make it harder to read, it's encouraged to
7368
rewrite it to for example:
69+
7470
```hbs
75-
{{if @isEditing (t "actions.save") (t "actions.publish")}}
71+
{{if @isEditing (t 'actions.save') (t 'actions.publish')}}
7672
```
7773

7874
However, if your application relies heavily on this `concat` helper, then rewriting
@@ -100,9 +96,11 @@ export default {
10096
This example will try to find translation files in `node_modules/my-addon/translations`.
10197
Patterns supported by [`globby`](https://www.npmjs.com/package/globby) are also
10298
possible here, e.g. this:
99+
103100
```js
104-
externalPaths: ['@*/*']
101+
externalPaths: ['@*/*'];
105102
```
103+
106104
will look up translations in scoped addons like `node_modules/@company/scoped-addon/translations`.
107105

108106
### `translationFiles`
@@ -122,13 +120,24 @@ This example will try to find all `en.yaml` files in the different `translations
122120
folders, but any patterns supported by [`globby`](https://www.npmjs.com/package/globby) are also
123121
possible here.
124122

123+
### `wrapTranslationsWithNamespace`
124+
125+
If you are nesting your translations with `ember-intl`s [`wrapTranslationsWithNamespace`](https://ember-intl.github.io/ember-intl/docs/advanced/configuration#wraptranslationswithnamespace) you will need to set the corresponding property within your `ember-intl-analyzer` config file.
126+
127+
```js
128+
export default {
129+
wrapTranslationsWithNamespace: true,
130+
};
131+
```
132+
125133
### `babelParserPlugins` `extensions`
126134

127135
If your application uses doesn't parse correctly because it requires a specific babel plugin you can specifiy them in the config file under the key `babelParserPlugins` a list on plugins can be found [here](https://babeljs.io/docs/en/babel-parser#plugins).
128136

129137
For example if you would like typescript support you can specify the `typescript` plugin, although please note if the plugin introduces a new file extension you will also need to specifiy that in the `extensions` property. See the examples below.
130138

131139
Typescript example
140+
132141
```js
133142
export default {
134143
babelParserPlugins: ['typescript'],
@@ -137,6 +146,7 @@ export default {
137146
```
138147

139148
Jsx example
149+
140150
```js
141151
export default {
142152
babelParserPlugins: ['jsx'],
@@ -145,6 +155,7 @@ export default {
145155
```
146156

147157
Gts example
158+
148159
```js
149160
export default {
150161
babelParserPlugins: ['typescript'],
@@ -153,6 +164,7 @@ export default {
153164
```
154165

155166
### `--fix`
167+
156168
If your application has a lot of unused translations you can run the command with
157169
the `--fix` to remove them. Remember to double check your translations as dynamic
158170
translations need to be whitelisted or they will be removed!
@@ -172,23 +184,18 @@ export default {
172184
};
173185
```
174186

175-
Caveats
176-
------------------------------------------------------------------------------
187+
## Caveats
177188

178189
There are a number of things that we do not support yet. Have a look at the
179190
[Issues](https://github.com/Mainmatter/ember-intl-analyzer/issues) before using
180191
this project.
181192

182-
183-
Related
184-
------------------------------------------------------------------------------
193+
## Related
185194

186195
- [ember-intl](https://github.com/ember-intl/ember-intl) – Internationalization
187196
addon for Ember.js
188197

189-
190-
License
191-
------------------------------------------------------------------------------
198+
## License
192199

193200
This projects is developed by and © [Mainmatter GmbH](http://mainmatter.com)
194201
and contributors. It is released under the [MIT License](LICENSE.md).

__snapshots__/test.js.snap

+14
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,20 @@ exports[`Test Fixtures missing-translations 1`] = `
172172

173173
exports[`Test Fixtures missing-translations 2`] = `Map {}`;
174174

175+
exports[`Test Fixtures nested-keys 1`] = `
176+
"[1/4] 🔍 Finding JS and HBS files...
177+
[2/4] 🔍 Searching for translations keys in JS and HBS files...
178+
[3/4] ⚙️ Checking for unused translations...
179+
[4/4] ⚙️ Checking for missing translations...
180+
181+
👏 No unused translations were found!
182+
183+
👏 No missing translations were found!
184+
"
185+
`;
186+
187+
exports[`Test Fixtures nested-keys 2`] = `Map {}`;
188+
175189
exports[`Test Fixtures no-issues 1`] = `
176190
"[1/4] 🔍 Finding JS and HBS files...
177191
[2/4] 🔍 Searching for translations keys in JS and HBS files...
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Controller from '@ember/controller';
2+
import { tracked } from '@glimmer/tracking';
3+
4+
export default class ApplicationController extends Controller {
5+
@tracked foo;
6+
foo() {
7+
return this.intl.t('foo.bar.hello');
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{{t 'foo.bar.hello'}}
2+
{{t 'foo.bar.bing.pot'}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
hello: Hallo
2+
bing:
3+
pot: wow!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
hello: Hello
2+
bing:
3+
pot: wow!

index.js

+34-11
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ async function run(rootDir, options = {}) {
3434
let userPlugins = config.babelParserPlugins || [];
3535
let userExtensions = config.extensions || [];
3636
let customHelpers = config.helpers || [];
37+
let wrapTranslationsWithNamespace = config.wrapTranslationsWithNamespace || false;
3738
let includeGtsExtension = userExtensions.includes('.gts');
3839

3940
userExtensions = userExtensions.map(extension =>
@@ -59,10 +60,15 @@ async function run(rootDir, options = {}) {
5960

6061
let ownTranslationFiles = await findOwnTranslationFiles(rootDir, config);
6162
let externalTranslationFiles = await findExternalTranslationFiles(rootDir, config);
62-
let existingOwnTranslationKeys = await analyzeTranslationFiles(rootDir, ownTranslationFiles);
63+
let existingOwnTranslationKeys = await analyzeTranslationFiles(
64+
rootDir,
65+
ownTranslationFiles,
66+
wrapTranslationsWithNamespace
67+
);
6368
let existingExternalTranslationKeys = await analyzeTranslationFiles(
6469
rootDir,
65-
externalTranslationFiles
70+
externalTranslationFiles,
71+
wrapTranslationsWithNamespace
6672
);
6773
let existingTranslationKeys = mergeMaps(
6874
existingOwnTranslationKeys,
@@ -417,21 +423,38 @@ async function analyzeHbsFile(content, { analyzeConcatExpression = false, helper
417423
return translationKeys;
418424
}
419425

420-
async function analyzeTranslationFiles(cwd, files) {
421-
let existingTranslationKeys = new Map();
426+
function extractNestedPath(filepath) {
427+
const regex = /translations\/(.+)\/(?:[a-z]{2}\.(?:ya?ml|json)|.*)/;
428+
const match = filepath.match(regex);
429+
430+
if (match) {
431+
return match[1].replace(/\//g, '.');
432+
} else {
433+
return null;
434+
}
435+
}
422436

437+
async function analyzeTranslationFiles(cwd, files, wrapTranslationsWithNamespace) {
438+
let existingTranslationKeys = new Map();
423439
for (let file of files) {
440+
let prefix = '';
441+
if (wrapTranslationsWithNamespace) {
442+
prefix = extractNestedPath(file) + '.';
443+
}
424444
let content = fs.readFileSync(`${cwd}/${file}`, 'utf8');
425445
let translations = YAML.parse(content); // json is valid yaml
426-
forEachTranslation(translations, key => {
427-
if (!existingTranslationKeys.has(key)) {
428-
existingTranslationKeys.set(key, new Set());
429-
}
446+
forEachTranslation(
447+
translations,
448+
key => {
449+
if (!existingTranslationKeys.has(key)) {
450+
existingTranslationKeys.set(key, new Set());
451+
}
430452

431-
existingTranslationKeys.get(key).add(file);
432-
});
453+
existingTranslationKeys.get(key).add(file);
454+
},
455+
prefix
456+
);
433457
}
434-
435458
return existingTranslationKeys;
436459
}
437460

test.js

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ describe('Test Fixtures', () => {
4343
'custom-t-helpers': {
4444
helpers: ['t-error'],
4545
},
46+
'nested-keys': {
47+
wrapTranslationsWithNamespace: true,
48+
},
4649
};
4750

4851
beforeEach(() => {

0 commit comments

Comments
 (0)