Skip to content

Commit

Permalink
Added the lesson to take apart files for their translation.
Browse files Browse the repository at this point in the history
  • Loading branch information
martinheidegger committed Nov 30, 2016
1 parent 804296d commit 62945eb
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 3 deletions.
15 changes: 15 additions & 0 deletions exercises/05_take_me_apart/exercise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
var exercise = require('../../lib/exercise')
var path = require('path')

module.exports = exercise(__dirname, {
createSampleSets: function createSampleSets() {
var samples = []
samples.push({
data: path.join(__dirname, 'templates')
})
return samples
},
handleResult: function (input, output, __) {
return output.join('\n')
}
})
44 changes: 44 additions & 0 deletions exercises/05_take_me_apart/problem.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Write a script that gets all the translation strings within a folder.
You will receive a folder like:

```javascript
module.exports = function (folderName) {
var translationKeys = []
// Fill translationKeys with all the translations in this folder
return translationKeys
}
```

The folder will contain multiple `.js` files in the folder and subfolders.
You will need to look out for translation strings like `__` or `.__`.

---

## Hints

### Processing the code

You can write your own code to process the code or you can use one of the many
modules in NPM. The package `xgettext-js` should come in handy.
The version `1.1.0` is bundled with this workshopper:

```javascript
require('{rootdir}/node_modules/xgettext-js')
```

The readme is available here:

{rootdir}/node_modules/xgettext-js/README.md

### Finding in Files

Instead of using Node.js's `fs.readdir` it is easier to use the npm module `glob`.
Version `^7.1.1` is bundled with this workshopper:

```javascript
require('{rootdir}/node_modules/glob')
```

The readme is available here:

{rootdir}/node_modules/glob/README.md
51 changes: 51 additions & 0 deletions exercises/05_take_me_apart/review.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
## Review

You can use the code you wrote already in the production pipeline. You might
want to improve the performance by writing the code in an async fashion.

### Frontend & Backend

This technique is very important _(and standard)_ if your repository contains both
Frontend and Backend code. As developer, you usually want to be able to maintain
the same translations for both sides.

For performance reasons you might not want to send all the translations to the
Frontend.

Also, for security reasons you might want to separate security sensitive
translations (welcome mails for private guests, pricing documents, etc.) to
be only loaded to the Frontend once the permission for this data is given.

### Dynamic Keys

Using scripts to look for the usage of translation is usually a good idea.
However, if you have dynamic use of translation like the following can undercut
your efforts:

```javascript
__('exercise.' + exercise.name)
```

In this case your parser has one of two options:

1. **Ignore this statement** (or give a warning). This is problematic because it would be really good to know which keys are needed.
2. **Treat this use as an error.** This is problematic because you might actually need to use translations like this sometimes.

With a bit of work at the parser it would be possible to create patterns like:

```
exercise.*
*.exercise
*.exercise.*.error
```

... and check extract the available translation fields for all possible exercise
combinations.

### XGetText

`gettext` is the is the standard tool for translation in C. The original
`xgettext` is a tool that extracts the `gettext` usage in C and C++ files.
In NPM there is a list of other `xgettext` variants for other file formats:

https://www.npmjs.com/search?q=xgettext
19 changes: 19 additions & 0 deletions exercises/05_take_me_apart/solution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var glob = require('glob')
var fs = require('fs')
var path = require('path')
var xgettextJS = new (require('xgettext-js'))({
keywords: {'__': 1}
})

module.exports = function (folder) {
var files = glob.sync('**/*.js', {cwd: folder})
return Object.keys(files.reduce(function (allKeys, file) {
file = path.join(folder, file)
return xgettextJS
.getMatches(fs.readFileSync(file, 'utf8'))
.reduce(function (allKeys, key) {
allKeys[key.string] = true
return allKeys
}, allKeys)
}, {}))
}
12 changes: 12 additions & 0 deletions exercises/05_take_me_apart/templates/a.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
__('$key: regular')

// Twice to make sure that duplicates are removed
__('$key: regular')

function hi() {
__('$key: unexported function')
}

module.exports = () => {
__('$key: exported function')
}
19 changes: 19 additions & 0 deletions exercises/05_take_me_apart/templates/b.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
__('$key: Regular key with a property', 'a')

someObj.__('$key: usage as an object property')

someObj.

__('$key: usage as an object property over several lines')

function hi() {
__('$key: inside a unexported, unused method scope', 'b')
var a = '$key: '
__(a + 'advanced usage where the key is split')
}

module.exports = () => {
__('$key: regular within an exported method')
__;
('$key: looks like a key is just a string')
}
5 changes: 5 additions & 0 deletions exercises/05_take_me_apart/templates/c.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This is a markdown file to mark that you shouldn't use markdown files

```javascript
__('$key: Do not look in markdown files')
```
3 changes: 2 additions & 1 deletion i18n-it-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ i18nItAll.addAll([
'01_dynamic_output',
'02_i_my_me_mine',
'03_rocket_science',
'04_negotiate_with_everyone'
'04_negotiate_with_everyone',
'05_take_me_apart'
])

module.exports = i18nItAll
3 changes: 2 additions & 1 deletion i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"01_dynamic_output": "Dynamic Output",
"02_i_my_me_mine": "I my me mine",
"03_rocket_science": "Rocket Science",
"04_negotiate_with_everyone": "Negotiate with everyone"
"04_negotiate_with_everyone": "Negotiate with everyone",
"05_take_me_apart": "Take me apart"
},
"negotiate_with_everyone": {
"headers": "Process the request headers.",
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@
"diff": "^3.0.1",
"exec-module": "^1.0.0",
"gettext": "0.0.1",
"glob": "^7.1.1",
"js-quantities": "1.6.3",
"leftpad": "0.0.0",
"locale": "0.1.0",
"lodash.sample": "^4.2.1",
"lodash.samplesize": "^4.2.0",
"lodash.zipwith": "^4.2.0",
"workshopper-adventure": "^5.1.1"
"workshopper-adventure": "^5.1.1",
"xgettext-js": "^1.1.0"
},
"repository": {
"type": "git",
Expand Down

0 comments on commit 62945eb

Please sign in to comment.