diff --git a/.travis.yml b/.travis.yml index bdb5d08..2281cfd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: node_js cache: yarn node_js: + - "6" +# - "7" NOT SUPPORTED BY fs-extra - "8" - "9" - "10" diff --git a/README.md b/README.md index ca1a220..f3f0ce7 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,14 @@ - +# @zakkudo/translation-static-analyzer -## TranslationStaticAnalyzer A library for scanning javscript files to build translation mappings in json automatically. -
- -Why use this? +[![Build Status](https://travis-ci.org/zakkudo/translation-static-analyzer.svg?branch=master)](https://travis-ci.org/zakkudo/translation-static-analyzer) +[![Coverage Status](https://coveralls.io/repos/github/zakkudo/translation-static-analyzer/badge.svg?branch=master)](https://coveralls.io/github/zakkudo/translation-static-analyzer?branch=master) +[![Known Vulnerabilities](https://snyk.io/test/github/zakkudo/translation-static-analyzer/badge.svg)](https://snyk.io/test/github/zakkudo/translation-static-analyzer) +[![Node](https://img.shields.io/node/v/@zakkudo/translation-static-analyzer.svg)](https://nodejs.org/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) + +## Why use this? - You no longer have to manage hierarchies of translations - Templates are automatically generated for the translators @@ -26,40 +17,61 @@ Why use this? - Any string wrapped in `__()` or `__n()`, will be picked up as a translatable making usage extremely easy for developers -What does it do? +## What does it do? -- I generates a locales directory filled with templates where the program was run, used by humans to translate -- It generates .locale directories optimized for loading in each of the directories passed to targets -- You load those translations from .locales as you need them +- It searches your source code for translatable strings and aggregates them +- It writes human-centric translation templates in json5 showing usages, new strings and no longer used strings +- It generates developer-centric optimized json templates, stripping away any unused strings and metadata -Install with: +## Install ```console -yarn add @zakkudo/translation-static-analyzer +# Install using npm +npm install @zakkudo/translation-static-analyzer ``` -Also consider `@zakkudo/translate-webpack-plugin` which is a wrapper for this library -for webpack and `@zakkudo/translator` for a library that can read the localization with -no fuss and apply the translations. See the [Polymer 3 Starter Project](https://github.com/zakkudo/polymer-3-starter-project) -for an example of using this library. +``` console +# Install using yarn +yarn add @zakkudo/translation-static-analyzer +``` -**Example** *(Usage for just translating everything in a project)* -```js +## Setup +1. Wrap strings you want to be translated in `__('text')` or `__n('singlular', 'plural', number)` using a library like `@zakkudo/translator` +2. Initialize the analyzer in your build scripts similar to below. +``` javascript const TranslationStaticAnalyzer = require('@zakkudo/translation-static-analyzer'); const analyzer = new TransalationStaticAnalyzer({ - files: 'src/**/*.js', // Analyzes all javscript files in the src directory - debug: true, // Enables verbose output - locales: ['fr', 'en'], // generate a locales/fr.json as well as a locales/en.json - target: 'src' // Consolidate all of the localizations into src + // Analyzes all javscript files in the src directory, which is a good initial value + files: 'src/**/*.js', + // Use verbose output to see what files are parsed, what keys are extracted, and where they are being written to + debug: true, + // You do not need to add your default language (which for most people will be English) + locales: ['fr'], + // Consolidate all of the optimized localizations into `src/.locale`, good as an initial configuration + target: 'src' }); -analyzer.update(); +// Use `read` and `write` to brute force updates to the translations, avoiding optimizations that reduce disk usage. + +// Reads the source files that match `src/**/*.js` and parses out any translation keys, merging it into the database +analyzer.read(); + +// Updates the `locales` translation templates for the translators and then writes the optimized `src/.locales` templates for the developers +analyzer.write(); +``` +3. Add `.locales` to your `.gitignore` so it isn't commited. It is a dynamic source file that has no value being added to a repository. Its existance in the `src` directory is simply to facilitate importing it. +4. Add `find src -name '.locales' | xargs rm -r` to your clean scripts for an easy way to remove the auto generated `src/.locales` from your source code +5. Import locales into your source code from the `src/.locales` folder so you can merge it into the lookup of `@zakkudo/translator`. It is plain old json with the untranslated and unexisting values optimized out. +6. Have your localization team use the files from `locales` (without a period.) It's annoted with information about new, unused, and existing usages of the translations to help them audit what needs updating. + +You'll end up with a file structure similar to below. +``` File Structure -├── locales <- Your translators translate this +├── locales <- For your translators │ ├── en.json │ └── fr.json └── src - ├── .locales <- Auto generated, should probably be added to .gitignore + ├── .locales <- For your developers │ ├── en.json │ └── fr.json └── pages @@ -68,129 +80,187 @@ File Structure └── Search └── index.js ``` -**Example** *(Usage for splitting transaltions between dynamically imported pages of a web app)* -```js + +Where `locales/fr.json` will look like this for use by your translators: +``` json5 +{ + // NEW + // src/pages/AboutPage/index.js:14 + "About": "", + // UNUSED + "Search Page": "French translation", + // src/pages/AboutPage/index.js:40 + "There is one user": {"one":"French translation", "other":"French translation"}, + // src/pages/AboutPage/index.js:38 + "Welcome to the about page!": "French translation" +} +``` + +And the optimized `src/.locales/fr.json` will look like this for use by your developers: +``` json +{ + "There is one user": {"one":"French translation", "other":"French translation"}, + "Welcome to the about page!": "French translation" +} +``` + +Your developers will use the translation similarly to below: +``` javascript +import Translator from '@zakkudo/translator'; +import fr from 'src/.locales/fr.json'; +const translator = new Translator(); +const {__, __n} = translator; +const language = navigator.language.split('-')[0]; + +translator.setLocalization('fr', fr); +translator.setLocale(language); + +document.title = __('About'); +document.body.innerHTML = __n('There is one user', 'There are %d users', 2); +``` + +## Examples + +### Configure the analyzer to build a single `.locales` directory +``` javascript const TranslationStaticAnalyzer = require('@zakkudo/translation-static-analyzer'); const analyzer = new TransalationStaticAnalyzer({ - files: 'src/**/*.js', // Analyzes all javscript files in the src directory - debug: true, // Enables verbose output - locales: ['fr', 'en'], // generate a locales/fr.json as well as a locales/en.json - target: 'src/pages/*' // Each page in the folder will get it's own subset of translations + files: 'src/**/*.js', + locales: ['es', 'fr'], + target: 'src' }); -analyzer.update(); +``` +``` File Structure -├── locales <- Your translators translate this -│ ├── en.json +├── locales <- For your translators. Contains translations for everything +│ ├── es.json │ └── fr.json └── src + ├── Application.js + ├── .locales <- For your developers. Contains translations for everything + │ ├── es.json + │ └── fr.json └── pages ├── About - │ ├── .locales <- Auto generated, should probably be added to .gitignore - │ │ ├── en.json + │ └── index.js + └── Search + └── index.js +``` + +### Configure the analyzer for a split `.locales` directory +``` javascript +const TranslationStaticAnalyzer = require('@zakkudo/translation-static-analyzer'); +const analyzer = new TransalationStaticAnalyzer({ + files: 'src/**/*.js', + locales: ['es', 'fr'], + target: 'src/pages/*' +}); +``` + +``` +File Structure +├── locales <- For your translators. Contains translations for everything +│ ├── es.json +│ └── fr.json +└── src + ├── Application.js + └── pages + ├── About + │ ├── .locales <- For your developers. Contains translations for `Application.js` and `About/index.js` + │ │ ├── es.json │ │ └── fr.json │ └── index.js └── Search - ├── .locales <- Auto generated, should probably be added to .gitignore - │ ├── en.json + ├── .locales <- For your developers. Contains translations for `Application.js` and `Search/index.js` + │ ├── es.json │ └── fr.json └── index.js ``` -**Example** *(Generated translation templates)* -```js -{ - // NEW - // src/Application/pages/AboutPage/index.js:14 - "About": "", - // UNUSED - "This isn't used anymore": "So the text here doesn't really do anything", - // src/Application/pages/AboutPage/index.js:38 - "Welcome to the about page!": "ようこそ" -} -``` -**Example** *(Use the translations with @zakkudo/translator)* -```js -import Translator from '@zakkudo/translator'; -import localization = from './src/.locales/ja.json'; //Generated by the analyzer -const translator = new Translator(); -translator.mergeLocalization('ja', localization); //Load the localization -translator.setLocale('ja'); //Tell the translator to use it +## Also see -const translated = translator.__('I love fish'); //Translate! -const translated = translator.__n('There is a duck in the pond.', 'There are %d ducks in the pond', 3); //Translate! -``` +- `@zakkudo/translate-webpack-plugin` which is a wrapper for this library +for use with [webpack](https://webpack.js.org) +- `@zakkudo/translator` is a library that can read the localization with +no fuss and apply the translations. +- [Polymer 3 Starter Project](https://github.com/zakkudo/polymer-3-starter-project) +is an example project using this library. + +## API -* [TranslationStaticAnalyzer](#module_TranslationStaticAnalyzer) - * [~TranslationStaticAnalyzer](#module_TranslationStaticAnalyzer..TranslationStaticAnalyzer) - * [new TranslationStaticAnalyzer(options)](#new_module_TranslationStaticAnalyzer..TranslationStaticAnalyzer_new) - * [.templateDirectory](#module_TranslationStaticAnalyzer..TranslationStaticAnalyzer+templateDirectory) ⇒String
- * [.read([requestFiles])](#module_TranslationStaticAnalyzer..TranslationStaticAnalyzer+read) ⇒ Boolean
- * [.write()](#module_TranslationStaticAnalyzer..TranslationStaticAnalyzer+write)
- * [.update([requestFiles])](#module_TranslationStaticAnalyzer..TranslationStaticAnalyzer+update)
+
-
+
-### TranslationStaticAnalyzer~TranslationStaticAnalyzer
-Class description.
+### @zakkudo/translation-static-analyzer~TranslationStaticAnalyzer ⏏
+Class for analyzing javascript source files, extracting the translations, and converting them into
+localization templates.
-**Kind**: inner class of [TranslationStaticAnalyzer
](#module_TranslationStaticAnalyzer)
+**Kind**: Exported class
-* [~TranslationStaticAnalyzer](#module_TranslationStaticAnalyzer..TranslationStaticAnalyzer)
- * [new TranslationStaticAnalyzer(options)](#new_module_TranslationStaticAnalyzer..TranslationStaticAnalyzer_new)
- * [.templateDirectory](#module_TranslationStaticAnalyzer..TranslationStaticAnalyzer+templateDirectory) ⇒ String
- * [.read([requestFiles])](#module_TranslationStaticAnalyzer..TranslationStaticAnalyzer+read) ⇒ Boolean
- * [.write()](#module_TranslationStaticAnalyzer..TranslationStaticAnalyzer+write)
- * [.update([requestFiles])](#module_TranslationStaticAnalyzer..TranslationStaticAnalyzer+update)
+* [~TranslationStaticAnalyzer](#module_@zakkudo/translation-static-analyzer..TranslationStaticAnalyzer)
+ * [new TranslationStaticAnalyzer(options)](#new_module_@zakkudo/translation-static-analyzer..TranslationStaticAnalyzer_new)
+ * [.templateDirectory](#module_@zakkudo/translation-static-analyzer..TranslationStaticAnalyzer+templateDirectory) ⇒ String
+ * [.read([requestFiles])](#module_@zakkudo/translation-static-analyzer..TranslationStaticAnalyzer+read) ⇒ Boolean
+ * [.write()](#module_@zakkudo/translation-static-analyzer..TranslationStaticAnalyzer+write)
+ * [.update([requestFiles])](#module_@zakkudo/translation-static-analyzer..TranslationStaticAnalyzer+update)
-
+
#### new TranslationStaticAnalyzer(options)
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| options | Object
| | The modifiers for how the analyzer is run |
-| options.files | String
| | A glob of the files to pull translations from |
+| options.files | String
| | A [glob pattern](https://www.npmjs.com/package/glob) of the files to pull translations from |
| [options.debug] | Boolean
| false
| Show debugging information in the console |
| [options.locales] | Array.<String>
| []
| The locales to generate (eg fr, ja_JP, en) |
-| [options.templates] | String
| | The location to store the translator translatable templates for each language |
-| [options.target] | String
| | Where to write the final translations, which can be split between multiple directories for modularity. |
+| [options.templates] | String
| | The location to store the translator translatable templates for each language. Defaults to making a `locales` directory in the current working directory |
+| [options.target] | String
| | Where to write the final translations, which can be split between multiple directories for modularity. If there are no targets, no `.locales` directory will be generated anywhere. |
-
+
#### translationStaticAnalyzer.templateDirectory ⇒ String
-**Kind**: instance property of [TranslationStaticAnalyzer
](#module_TranslationStaticAnalyzer..TranslationStaticAnalyzer)
+**Kind**: instance property of [TranslationStaticAnalyzer
](#module_@zakkudo/translation-static-analyzer..TranslationStaticAnalyzer)
**Returns**: String
- The path to the directory which holds
the translation templates that are dynamically updated
by code changes and should be used by translators
to add the localizations.
-
+
#### translationStaticAnalyzer.read([requestFiles]) ⇒ Boolean
-Read changes from the source files and update the language templates.
+Read changes from the source files and update the database stored in the current
+analyzer instance. No changes will be written to the templates and all reads are
+accumulative for the next write. Use the `requestFiles` option if you want to hook
+this method up to a file watcher which can supply a list of files that have changed.
-**Kind**: instance method of [TranslationStaticAnalyzer
](#module_TranslationStaticAnalyzer..TranslationStaticAnalyzer)
+**Kind**: instance method of [TranslationStaticAnalyzer
](#module_@zakkudo/translation-static-analyzer..TranslationStaticAnalyzer)
**Returns**: Boolean
- True if some some of the modified files matches the
file option passed on initialization
| Param | Type | Default | Description |
| --- | --- | --- | --- |
-| [requestFiles] | Array.<String>
| []
| The files or none to update everything in the options.files glob pattern. |
+| [requestFiles] | Array.<String>
| []
| A subset of files from the `options.files` glob to read or non to reread all files. Any files that are supplied to this method that are not part of the `options.files` glob are simply ignored. |
-
+
#### translationStaticAnalyzer.write()
-Write to the targets. Use to force an update of the targets if a
-language file template in the templateDirectory is updated without
+Write the current database to the templates and targets. This method is
+useful to force an update of the targets if a
+language file template in `templateDirectory` is updated without
updating a source file.
-**Kind**: instance method of [TranslationStaticAnalyzer
](#module_TranslationStaticAnalyzer..TranslationStaticAnalyzer)
-
+**Kind**: instance method of [TranslationStaticAnalyzer
](#module_@zakkudo/translation-static-analyzer..TranslationStaticAnalyzer)
+
#### translationStaticAnalyzer.update([requestFiles])
-Updates the translations to match the source files.
+Updates the translations to match the source files, using logic to try to reduce disk writes
+if no source files changed. This method was designed to be hooked up to a file watcher for the source
+code. *There will be no changes if this method is called after there is a manual change to the translation
+templates. It only cares about source files.*
-**Kind**: instance method of [TranslationStaticAnalyzer
](#module_TranslationStaticAnalyzer..TranslationStaticAnalyzer)
+**Kind**: instance method of [TranslationStaticAnalyzer
](#module_@zakkudo/translation-static-analyzer..TranslationStaticAnalyzer)
| Param | Type | Default | Description |
| --- | --- | --- | --- |
diff --git a/package.json b/package.json
index 769cf48..bc08a89 100644
--- a/package.json
+++ b/package.json
@@ -15,14 +15,19 @@
"files": [
"*"
],
+ "engines": {
+ "node": ">=6.0.0 <7.0.0 || >=8.0.0"
+ },
"repository": "github:zakkudo/translation-static-analyzer",
"license": "BSD-3-Clause",
"devDependencies": {
"@babel/cli": "^7.0.0-beta.56",
"@babel/core": "^7.0.0-beta.56",
+ "@babel/plugin-transform-runtime": "^7.0.0",
"@babel/preset-env": "^7.0.0-beta.56",
"babel-core": "^7.0.0-0",
"babel-jest": "^23.4.2",
+ "babel-plugin-transform-undefined-to-void": "^6.9.4",
"eslint": "^4.19.1",
"eslint-plugin-jasmine": "^2.10.1",
"eslint-plugin-jest": "^21.21.0",
@@ -30,10 +35,10 @@
"jest": "^23.4.2",
"jest-cli": "^23.4.2",
"jsdoc": "^3.5.5",
- "jsdoc-to-markdown": "^4.0.1",
- "regenerator-runtime": "^0.12.1"
+ "jsdoc-to-markdown": "^4.0.1"
},
"dependencies": {
+ "@babel/runtime-corejs2": "^7.0.0",
"deep-equal": "^1.0.1",
"fs-extra": "^7.0.0",
"glob": "^7.1.2",
@@ -49,8 +54,5 @@
"lint": "scripts/lint.sh",
"deploy": "scripts/deploy.sh",
"test": "scripts/test.sh"
- },
- "engines": {
- "node": ">=8.0.0"
}
}
diff --git a/scripts/build.sh b/scripts/build.sh
index aae5062..d767af8 100755
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -2,4 +2,25 @@
set -e
-./node_modules/.bin/babel src --out-dir build --source-maps inline --ignore "src/*.test.js" --ignore "src/test.js"
+export NODE_ENV="build"
+
+CURRENT_DIR=$(pwd)
+PROJECT_DIR=$(git rev-parse --show-toplevel)
+
+cd $PROJECT_DIR
+
+./scripts/clean.sh
+./scripts/document.sh
+
+mkdir build
+
+cp package.json build/package.json
+cp README.md build/README.md
+
+./node_modules/.bin/babel src \
+ --out-dir build \
+ --source-maps inline \
+ --ignore "src/test.js" \
+ --ignore "src/*.test.js" \
+ --verbose \
+ "$@"
diff --git a/scripts/clean.sh b/scripts/clean.sh
index 74a1c92..88169a1 100755
--- a/scripts/clean.sh
+++ b/scripts/clean.sh
@@ -2,15 +2,13 @@
set -e
+CURRENT_DIR=$(pwd)
PROJECT_DIR=$(git rev-parse --show-toplevel)
-BIN_DIR=$(npm bin)
-JSDOC="$BIN_DIR/jsdoc"
-rm -rf $PROJECT_DIR/build
-rm -rf $PROJECT_DIR/coverage
-rm -rf $PROJECT_DIR/documentation
-rm -rf $PROJECT_DIR/demo
-rm -f $PROJECT_DIR/.karma-test-results.json
-rm -f $PROJECT_DIR/.jest-test-results.json
-rm -f $PROJECT_DIR/jsdoc.*.conf.tmp
-rm -f $PROJECT_DIR/yarn-error.log
+cd $PROJECT_DIR
+
+rm -rf build
+rm -rf coverage
+rm -rf documentation
+rm -f jsdoc.*.conf.tmp
+rm -f yarn-error.log
diff --git a/scripts/cover.sh b/scripts/cover.sh
index faadd02..36805ef 100755
--- a/scripts/cover.sh
+++ b/scripts/cover.sh
@@ -1,3 +1,11 @@
-#!/bin/sh
+#!/bin/bash
-./node_modules/.bin/jest --coverage --config jest.config.js
+set -e
+
+CURRENT_DIR=$(pwd)
+PROJECT_DIR=$(git rev-parse --show-toplevel)
+
+cd $PROJECT_DIR
+
+./scripts/clean.sh
+./scripts/test.sh --coverage --coveragePathIgnorePatterns '.*TestHelper.js' "$@"
diff --git a/scripts/deploy.sh b/scripts/deploy.sh
index 5a5ee94..f765a35 100755
--- a/scripts/deploy.sh
+++ b/scripts/deploy.sh
@@ -2,11 +2,17 @@
set -e
-yarn build
-yarn document
-yarn cover
+CURRENT_DIR=$(pwd)
+PROJECT_DIR=$(git rev-parse --show-toplevel)
-cp README.md build/README.md
-cp package.json build/package.json
+cd $PROJECT_DIR
+
+./scripts/cover.sh
+./scripts/document.sh
+./scripts/build.sh
+
+yarn version
+
+./scripts/build.sh
yarn publish --access public --cwd build --no-git-tag-version
diff --git a/scripts/document.sh b/scripts/document.sh
index 2bf9927..b1b2bec 100755
--- a/scripts/document.sh
+++ b/scripts/document.sh
@@ -2,10 +2,23 @@
set -e
+export NODE_ENV="document"
+
+CURRENT_DIR=$(pwd)
PROJECT_DIR=$(git rev-parse --show-toplevel)
BIN_DIR=$(npm bin)
JSDOC="$BIN_DIR/jsdoc"
+OPTIONS="--module-index-format none --global-index-format none --example-lang js --heading-depth 3"
+
+cd $PROJECT_DIR
+
+$JSDOC -c jsdoc.config.json "$@"
+cat src/README.md > README.md
+
+echo "" >> README.md
+echo "## API" >> README.md
+echo "" >> README.md
+
+./node_modules/.bin/jsdoc2md src/index.js $OPTIONS >> README.md
-$JSDOC -c $PROJECT_DIR/jsdoc.config.json "$@"
-./node_modules/.bin/jsdoc2md src/*.js > README.md
-sed -i '' 's/\\\//\//g' README.md
+./scripts/postProcessReadme.js README.md
diff --git a/scripts/lint.sh b/scripts/lint.sh
index bfc6de9..e8a6b43 100755
--- a/scripts/lint.sh
+++ b/scripts/lint.sh
@@ -1,3 +1,12 @@
-#!/bin/sh
+#!/bin/bash
-./node_modules/.bin/eslint src
+set -e
+
+export NODE_ENV="lint"
+
+CURRENT_DIR=$(pwd)
+PROJECT_DIR=$(git rev-parse --show-toplevel)
+
+cd $PROJECT_DIR
+
+./node_modules/.bin/eslint src "$@"
diff --git a/scripts/postProcessReadme.js b/scripts/postProcessReadme.js
new file mode 100755
index 0000000..95015e3
--- /dev/null
+++ b/scripts/postProcessReadme.js
@@ -0,0 +1,54 @@
+#!/usr/bin/env node
+
+const fs = require('fs');
+
+const arguments = process.argv.slice(2);;
+const filename = arguments[0];
+const contents = String(fs.readFileSync(filename));
+const lines = contents.split('\n');
+
+let isIndex = false;
+let isApiSection = false;
+
+const newContents = lines.filter((l) => {
+ if (l.startsWith('* [@')) {
+ isIndex = true;
+ } else if (l === '') {
+ isIndex = false;
+ }
+
+ return !isIndex;
+}).filter((l) => {
+ return !l.startsWith('### @');
+}).map((l, index, lines) => {
+ const innerClassOfPrefix = '**Kind**: inner class of';
+ const innerMethodOfPrefix = '**Kind**: inner method of';
+
+ if (l.startsWith('#### @')) {
+ return l + ' ⏏';
+ }
+
+ if (l.startsWith(innerClassOfPrefix)) {
+ return '\n**Kind**: Exported class\n';
+ }
+
+ if (l.startsWith(innerMethodOfPrefix)) {
+ return '\n**Kind**: Exported function\n';
+ }
+
+ return l;
+}).map((l, index, lines) => {
+ if (l === '## API') {
+ isApiSection = true;
+ return l;
+ }
+
+ if (isApiSection && l.startsWith('##')) {
+ return l.slice(1);
+ }
+
+ return l;
+}).join('\n').replace(/\n\n+/gm, '\n\n').replace('@', '@').replace('/', '/');
+
+fs.writeFileSync(filename, newContents);
+
diff --git a/scripts/test.sh b/scripts/test.sh
index 040cf71..a4e1107 100755
--- a/scripts/test.sh
+++ b/scripts/test.sh
@@ -1,3 +1,13 @@
-#!/bin/sh
+#!/bin/bash
-./node_modules/.bin/jest --runInBand
+set -e
+
+export NODE_ENV="test"
+
+CURRENT_DIR=$(pwd)
+PROJECT_DIR=$(git rev-parse --show-toplevel)
+
+cd $PROJECT_DIR
+
+./scripts/clean.sh
+./node_modules/.bin/jest --runInBand "$@"
diff --git a/src/.babelrc b/src/.babelrc
deleted file mode 100644
index 309070e..0000000
--- a/src/.babelrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "presets": ["@babel/env"]
-}
diff --git a/src/.babelrc.js b/src/.babelrc.js
new file mode 100644
index 0000000..f3de39d
--- /dev/null
+++ b/src/.babelrc.js
@@ -0,0 +1,20 @@
+module.exports = {
+ "presets": [
+ [
+ "@babel/env", {
+ "debug": process.env.NODE_ENV === 'build',
+ "targets": {"browsers": [
+ "last 1 version",
+ "> 1%",
+ "not dead"
+ ], "node": "6"}
+ }
+ ]
+ ],
+ "plugins": [
+ ["@babel/transform-runtime", {"corejs": 2}],
+ "transform-undefined-to-void"
+ ],
+ minified: true,
+ comments: false
+}
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 0000000..6599020
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,189 @@
+# @zakkudo/translation-static-analyzer
+
+A library for scanning javscript files to build translation mappings in json automatically.
+
+[![Build Status](https://travis-ci.org/zakkudo/translation-static-analyzer.svg?branch=master)](https://travis-ci.org/zakkudo/translation-static-analyzer)
+[![Coverage Status](https://coveralls.io/repos/github/zakkudo/translation-static-analyzer/badge.svg?branch=master)](https://coveralls.io/github/zakkudo/translation-static-analyzer?branch=master)
+[![Known Vulnerabilities](https://snyk.io/test/github/zakkudo/translation-static-analyzer/badge.svg)](https://snyk.io/test/github/zakkudo/translation-static-analyzer)
+[![Node](https://img.shields.io/node/v/@zakkudo/translation-static-analyzer.svg)](https://nodejs.org/)
+[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
+
+## Why use this?
+
+- You no longer have to manage hierarchies of translations
+- Templates are automatically generated for the translators
+- The translations are noted if they are new, unused and what files
+- It allows splitting the translations easily for dynamic imports to allow sliced loading
+- Any string wrapped in `__()` or `__n()`, will be picked up as a
+ translatable making usage extremely easy for developers
+
+## What does it do?
+
+- It searches your source code for translatable strings and aggregates them
+- It writes human-centric translation templates in json5 showing usages, new strings and no longer used strings
+- It generates developer-centric optimized json templates, stripping away any unused strings and metadata
+
+## Install
+
+```console
+# Install using npm
+npm install @zakkudo/translation-static-analyzer
+```
+
+``` console
+# Install using yarn
+yarn add @zakkudo/translation-static-analyzer
+```
+
+## Setup
+1. Wrap strings you want to be translated in `__('text')` or `__n('singlular', 'plural', number)` using a library like `@zakkudo/translator`
+2. Initialize the analyzer in your build scripts similar to below.
+``` javascript
+const TranslationStaticAnalyzer = require('@zakkudo/translation-static-analyzer');
+const analyzer = new TransalationStaticAnalyzer({
+ // Analyzes all javscript files in the src directory, which is a good initial value
+ files: 'src/**/*.js',
+ // Use verbose output to see what files are parsed, what keys are extracted, and where they are being written to
+ debug: true,
+ // You do not need to add your default language (which for most people will be English)
+ locales: ['fr'],
+ // Consolidate all of the optimized localizations into `src/.locale`, good as an initial configuration
+ target: 'src'
+});
+
+// Use `read` and `write` to brute force updates to the translations, avoiding optimizations that reduce disk usage.
+
+// Reads the source files that match `src/**/*.js` and parses out any translation keys, merging it into the database
+analyzer.read();
+
+// Updates the `locales` translation templates for the translators and then writes the optimized `src/.locales` templates for the developers
+analyzer.write();
+```
+3. Add `.locales` to your `.gitignore` so it isn't commited. It is a dynamic source file that has no value being added to a repository. Its existance in the `src` directory is simply to facilitate importing it.
+4. Add `find src -name '.locales' | xargs rm -r` to your clean scripts for an easy way to remove the auto generated `src/.locales` from your source code
+5. Import locales into your source code from the `src/.locales` folder so you can merge it into the lookup of `@zakkudo/translator`. It is plain old json with the untranslated and unexisting values optimized out.
+6. Have your localization team use the files from `locales` (without a period.) It's annoted with information about new, unused, and existing usages of the translations to help them audit what needs updating.
+
+You'll end up with a file structure similar to below.
+```
+File Structure
+├── locales <- For your translators
+│ ├── en.json
+│ └── fr.json
+└── src
+ ├── .locales <- For your developers
+ │ ├── en.json
+ │ └── fr.json
+ └── pages
+ ├── About
+ │ └── index.js
+ └── Search
+ └── index.js
+```
+
+Where `locales/fr.json` will look like this for use by your translators:
+``` json5
+{
+ // NEW
+ // src/pages/AboutPage/index.js:14
+ "About": "",
+ // UNUSED
+ "Search Page": "French translation",
+ // src/pages/AboutPage/index.js:40
+ "There is one user": {"one":"French translation", "other":"French translation"},
+ // src/pages/AboutPage/index.js:38
+ "Welcome to the about page!": "French translation"
+}
+```
+
+And the optimized `src/.locales/fr.json` will look like this for use by your developers:
+``` json
+{
+ "There is one user": {"one":"French translation", "other":"French translation"},
+ "Welcome to the about page!": "French translation"
+}
+```
+
+Your developers will use the translation similarly to below:
+``` javascript
+import Translator from '@zakkudo/translator';
+import fr from 'src/.locales/fr.json';
+const translator = new Translator();
+const {__, __n} = translator;
+const language = navigator.language.split('-')[0];
+
+translator.setLocalization('fr', fr);
+translator.setLocale(language);
+
+document.title = __('About');
+document.body.innerHTML = __n('There is one user', 'There are %d users', 2);
+```
+
+## Examples
+
+### Configure the analyzer to build a single `.locales` directory
+``` javascript
+const TranslationStaticAnalyzer = require('@zakkudo/translation-static-analyzer');
+const analyzer = new TransalationStaticAnalyzer({
+ files: 'src/**/*.js',
+ locales: ['es', 'fr'],
+ target: 'src'
+});
+```
+
+```
+File Structure
+├── locales <- For your translators. Contains translations for everything
+│ ├── es.json
+│ └── fr.json
+└── src
+ ├── Application.js
+ ├── .locales <- For your developers. Contains translations for everything
+ │ ├── es.json
+ │ └── fr.json
+ └── pages
+ ├── About
+ │ └── index.js
+ └── Search
+ └── index.js
+```
+
+
+### Configure the analyzer for a split `.locales` directory
+``` javascript
+const TranslationStaticAnalyzer = require('@zakkudo/translation-static-analyzer');
+const analyzer = new TransalationStaticAnalyzer({
+ files: 'src/**/*.js',
+ locales: ['es', 'fr'],
+ target: 'src/pages/*'
+});
+```
+
+```
+File Structure
+├── locales <- For your translators. Contains translations for everything
+│ ├── es.json
+│ └── fr.json
+└── src
+ ├── Application.js
+ └── pages
+ ├── About
+ │ ├── .locales <- For your developers. Contains translations for `Application.js` and `About/index.js`
+ │ │ ├── es.json
+ │ │ └── fr.json
+ │ └── index.js
+ └── Search
+ ├── .locales <- For your developers. Contains translations for `Application.js` and `Search/index.js`
+ │ ├── es.json
+ │ └── fr.json
+ └── index.js
+```
+
+## Also see
+
+- `@zakkudo/translate-webpack-plugin` which is a wrapper for this library
+for use with [webpack](https://webpack.js.org)
+- `@zakkudo/translator` is a library that can read the localization with
+no fuss and apply the translations.
+- [Polymer 3 Starter Project](https://github.com/zakkudo/polymer-3-starter-project)
+is an example project using this library.
diff --git a/src/index.js b/src/index.js
index 654d83b..895528b 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,122 +1,5 @@
/**
- * A library for scanning javscript files to build translation mappings in json automatically.
- *
- *
- *
- * Why use this?
- *
- * - You no longer have to manage hierarchies of translations
- * - Templates are automatically generated for the translators
- * - The translations are noted if they are new, unused and what files
- * - It allows splitting the translations easily for dynamic imports to allow sliced loading
- * - Any string wrapped in `__()` or `__n()`, will be picked up as a
- * translatable making usage extremely easy for developers
- *
- * What does it do?
- *
- * - I generates a locales directory filled with templates where the program was run, used by humans to translate
- * - It generates .locale directories optimized for loading in each of the directories passed to targets
- * - You load those translations from .locales as you need them
- *
- * Install with:
- *
- * ```console
- * yarn add @zakkudo/translation-static-analyzer
- * ```
- *
- * Also consider `@zakkudo/translate-webpack-plugin` which is a wrapper for this library
- * for webpack and `@zakkudo/translator` for a library that can read the localization with
- * no fuss and apply the translations. See the [Polymer 3 Starter Project]{@link https://github.com/zakkudo/polymer-3-starter-project}
- * for an example of using this library.
- *
- * @example