From 2b5360ffaaca395374c7949f3d155437cb41b35a Mon Sep 17 00:00:00 2001 From: Gertjan Reynaert Date: Fri, 16 Dec 2016 16:54:55 +0100 Subject: [PATCH 1/6] refactor(JSON): translationManager now takes a jsonOptions object BREAKING CHANGE: Dropped `jsonSpaceIndentation` and `jsonTrailingNewLine` options in favor of `jsonOptions` object that can have a `space` key and `trailingNewline` key --- src/manageTranslations.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/manageTranslations.js b/src/manageTranslations.js index 734405f..502df30 100644 --- a/src/manageTranslations.js +++ b/src/manageTranslations.js @@ -12,6 +12,12 @@ import stringify from './stringify'; import core from './core'; + +const defaultJSONOptions = { + space: 2, + trailingNewline: false, +}; + export default ({ messagesDirectory, translationsDirectory, @@ -21,18 +27,18 @@ export default ({ detectDuplicateIds = true, sortKeys = true, printers = {}, - jsonSpaceIndentation = 2, - jsonTrailingNewline = false, + jsonOptions = {}, }) => { if (!messagesDirectory || !translationsDirectory) { throw new Error('messagesDirectory and translationsDirectory are required'); } const stringifyOpts = { - space: jsonSpaceIndentation, - trailingNewline: jsonTrailingNewline, + ...defaultJSONOptions, + ...jsonOptions, sortKeys, }; + core(languages, { provideExtractedMessages: () => readMessageFiles(messagesDirectory), outputSingleFile: combinedFiles => { From 2216c4236c8cc46971f8a5242a745afc8a0fd5f2 Mon Sep 17 00:00:00 2001 From: Gertjan Reynaert Date: Fri, 16 Dec 2016 17:30:09 +0100 Subject: [PATCH 2/6] refactor(Printers): Cleanup of printers logic, change for noFile printers BREAKING CHANGE: printLanguageReport, printNoLanguageFile and printNoLanguageWhitelistFile now receive the full languageReport, instead of a few paramters. The parameters from the previous version are available on this languageReport. --- src/manageTranslations.js | 90 ++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 38 deletions(-) diff --git a/src/manageTranslations.js b/src/manageTranslations.js index 502df30..59a441c 100644 --- a/src/manageTranslations.js +++ b/src/manageTranslations.js @@ -26,13 +26,51 @@ export default ({ singleMessagesFile = false, detectDuplicateIds = true, sortKeys = true, - printers = {}, jsonOptions = {}, + overridePrinters = {}, }) => { if (!messagesDirectory || !translationsDirectory) { throw new Error('messagesDirectory and translationsDirectory are required'); } + const defaultPrinters = { + printDuplicateIds(duplicateIds) { + header('Duplicate ids:'); + if (duplicateIds.length) { + duplicateIds.forEach(id => { + console.log(' ', `Duplicate message id: ${red(id)}`); + }); + } else { + console.log(green(' No duplicate ids found, great!')); + } + footer(); + }, + + printLanguageReport(langResults) { + header(`Maintaining ${yellow(langResults.languageFilename)}:`); + printResults({ ...langResults.report, sortKeys }); + } + + printNoLanguageFile(langResults) { + subheader(` + No existing ${langResults.languageFilename} translation file found. + A new one is created. + `); + } + + printNoLanguageWhitelistFile(langResults) { + subheader(``` + No existing ${langResults} file found. + A new one is created. + ```); + } + }; + + const printers = { + ...defaultPrinters, + ...overridePrinters, + } + const stringifyOpts = { ...defaultJSONOptions, ...jsonOptions, @@ -41,6 +79,7 @@ export default ({ core(languages, { provideExtractedMessages: () => readMessageFiles(messagesDirectory), + outputSingleFile: combinedFiles => { if (singleMessagesFile) { createSingleMessagesFile({ @@ -50,27 +89,18 @@ export default ({ }); } }, + outputDuplicateKeys: duplicateIds => { - if (detectDuplicateIds) { - if (typeof printers.printDuplicateIds === 'function') { - printers.printDuplicateIds(duplicateIds); - } else { - header('Duplicate ids:'); - if (duplicateIds.length) { - duplicateIds.forEach(id => { - console.log(' ', `Duplicate message id: ${red(id)}`); - }); - } else { - console.log(green(' No duplicate ids found, great!')); - } - footer(); - } - } + if (detectDuplicateIds) return; + + printers.printDuplicateIds(duplicateIds); }, + beforeReporting: () => { mkdirpSync(translationsDirectory); mkdirpSync(whitelistsDirectory); }, + provideLangTemplate: lang => { const languageFilename = `${lang}.json`; const languageFilepath = Path.join(translationsDirectory, languageFilename); @@ -85,24 +115,22 @@ export default ({ whitelistFilepath, }; }, + provideTranslationsFile: lang => { const filePath = Path.join(translationsDirectory, `${lang}.json`); const jsonFile = readFile(filePath); return jsonFile ? JSON.parse(jsonFile) : undefined; }, + provideWhitelistFile: lang => { const filePath = Path.join(whitelistsDirectory, `whitelist_${lang}.json`); const jsonFile = readFile(filePath); return jsonFile ? JSON.parse(jsonFile) : undefined; }, + reportLanguage: langResults => { if (!langResults.report.noTranslationFile && !langResults.report.noWhitelistFile) { - if (typeof printers.printLanguageReport === 'function') { - printers.printLanguageReport(langResults.languageFilename, langResults.report); - } else { - header(`Maintaining ${yellow(langResults.languageFilename)}:`); - printResults({ ...langResults.report, sortKeys }); - } + printers.printLanguageReport(langResults); writeFileSync( langResults.languageFilepath, @@ -114,26 +142,12 @@ export default ({ ); } else { if (langResults.report.noTranslationFile) { - if (typeof printers.printNoLanguageFile === 'function') { - printers.printNoLanguageFile(langResults.lang); - } else { - subheader(``` - No existing ${langResults.languageFilename} translation file found. - A new one is created. - ```); - } + printers.printNoLanguageFile(langResults); writeFileSync(langResults, stringify(langResults.report.fileOutput, stringifyOpts)); } if (langResults.report.noWhitelistFile) { - if (typeof printers.printNoLanguageWhitelistFile === 'function') { - printers.printNoLanguageWhitelistFile(langResults.lang); - } else { - subheader(``` - No existing ${langResults.whitelistFilename} file found. - A new one is created. - ```); - } + printers.printNoLanguageWhitelistFile(langResults); writeFileSync(langResults.whitelistFilepath, stringify([], stringifyOpts)); } } From 19e36c36844678773e301eae2f0db6ce2140f307 Mon Sep 17 00:00:00 2001 From: Gertjan Reynaert Date: Fri, 16 Dec 2016 17:38:02 +0100 Subject: [PATCH 3/6] feat(overrideCoreMethods): add ability to override single core methods It is now possible to override a single core method without needing to add additional options, or without having to implement your own translationManager based on the core. Now just pass the overrides to `overrideHooks` as an object of the same shape as the core it's hooks accept. --- src/manageTranslations.js | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/manageTranslations.js b/src/manageTranslations.js index 59a441c..44d39f0 100644 --- a/src/manageTranslations.js +++ b/src/manageTranslations.js @@ -12,7 +12,6 @@ import stringify from './stringify'; import core from './core'; - const defaultJSONOptions = { space: 2, trailingNewline: false, @@ -28,13 +27,14 @@ export default ({ sortKeys = true, jsonOptions = {}, overridePrinters = {}, + overrideCoreMethods = {}, }) => { if (!messagesDirectory || !translationsDirectory) { throw new Error('messagesDirectory and translationsDirectory are required'); } const defaultPrinters = { - printDuplicateIds(duplicateIds) { + printDuplicateIds: duplicateIds => { header('Duplicate ids:'); if (duplicateIds.length) { duplicateIds.forEach(id => { @@ -46,30 +46,30 @@ export default ({ footer(); }, - printLanguageReport(langResults) { + printLanguageReport: langResults => { header(`Maintaining ${yellow(langResults.languageFilename)}:`); printResults({ ...langResults.report, sortKeys }); - } + }, - printNoLanguageFile(langResults) { - subheader(` + printNoLanguageFile: langResults => { + subheader(` No existing ${langResults.languageFilename} translation file found. A new one is created. `); - } + }, - printNoLanguageWhitelistFile(langResults) { + printNoLanguageWhitelistFile: langResults => { subheader(``` No existing ${langResults} file found. A new one is created. ```); - } + }, }; const printers = { ...defaultPrinters, ...overridePrinters, - } + }; const stringifyOpts = { ...defaultJSONOptions, @@ -77,7 +77,7 @@ export default ({ sortKeys, }; - core(languages, { + const defaultCoreMethods = { provideExtractedMessages: () => readMessageFiles(messagesDirectory), outputSingleFile: combinedFiles => { @@ -152,5 +152,12 @@ export default ({ } } }, + + afterReporting: () => {}, + }; + + core(languages, { + ...defaultCoreMethods, + ...overrideCoreMethods, }); }; From 0df38219334ebf6c2c30db5540c73dc1a89bf1df Mon Sep 17 00:00:00 2001 From: Gertjan Reynaert Date: Fri, 16 Dec 2016 18:05:02 +0100 Subject: [PATCH 4/6] docs(Update): Make docs up to date with latest changes --- README.md | 94 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index e55bc2c..7a49612 100644 --- a/README.md +++ b/README.md @@ -33,28 +33,29 @@ now you know what messages you still need to update. ## Installing ``` -npm install --save-dev react-intl-translations-manager +yarn add --dev react-intl-translations-manager ``` ## Setup ### Basic -Since you need the `babel-plugin-react-intl` to extract the messages, I'll assume you're using babel in your project. - This is an example of the most basic usage of this plugin, in the API documentation below you can find more options. Create a script in your package.json ```json { "scripts": { - "manage:translations": "babel-node ./translationRunner.js" + "manage:translations": "node ./translationRunner.js" } } ``` Create a file with your config you can run with the npm script ```js // translationRunner.js -import manageTranslations from 'react-intl-translations-manager'; +const manageTranslations = require('react-intl-translations-manager'); + +// es2015 import +// import manageTranslations from 'react-intl-translations-manager'; manageTranslations({ messagesDirectory: 'src/translations/extractedMessages', @@ -69,11 +70,6 @@ Run the translation manager with your new npm script npm run manage:translations ``` -### Advanced - -Build your own translationManager based on the core of this package, or it's -exposed helper methods. - ## Usage Now you can check the status of your translations by just running the script. Then @@ -89,9 +85,13 @@ checking the translations status. ### manageTranslations -This will maintain all translation files. Based on your config you will get output for duplicate ids, and per specified language you will get the deleted translations, added messages (new messages that need to be translated), and not yet translated messages. It will also maintain a whitelist file per language where you can specify translation keys where the translation is identical to the default message. This way you can avoid untranslated message warnings for these messages. - -You can optionally pass a printer object to this method. This way you can override the console logging with your own logging logic. If you want custom file writing logic, it is advised to roll your own translationManager based on the core. +This will maintain all translation files. Based on your config you will get +output for duplicate ids, and per specified language you will get the deleted +translations, added messages (new messages that need to be translated), and not +yet translated messages. It will also maintain a whitelist file per language +where you can specify translation keys where the translation is identical to +the default message. This way you can avoid untranslated message warnings for +these messages. #### Config @@ -104,7 +104,8 @@ You can optionally pass a printer object to this method. This way you can overri - example: `src/locales/lang` - `whitelistsDirectory` (optional, default: `translationsDirectory`) - Directory of the whitelist files the translation manager needs to maintain. - These files contain the key of translations that have the exact same text in a specific language as the defaultMessage. Specifying this key will suppress + These files contain the key of translations that have the exact same text in + a specific language as the defaultMessage. Specifying this key will suppress `unmaintained translation` warnings. - example: `Dashboard` in english is also accepted as a valid translation for dutch. @@ -112,7 +113,8 @@ You can optionally pass a printer object to this method. This way you can overri - What languages the translation manager needs to maintain. Specifying no languages actually doesn't make sense, but won't break the translationManager either. - - example: for `['nl', 'fr']` the translation manager will maintain a `nl.json`, `fr.json`, `whitelist_nl.json` and a `whitelist_fr.json` file + - example: for `['nl', 'fr']` the translation manager will maintain a + `nl.json`, `fr.json`, `whitelist_nl.json` and a `whitelist_fr.json` file - `singleMessagesFile` (optional, default: `false`) - Option to output a single JSON file containing the aggregate of all extracted messages, grouped by the file they were extracted from. @@ -135,7 +137,8 @@ You can optionally pass a printer object to this method. This way you can overri - If you want the translationManager to log duplicate message ids or not - `sortKeys` (optional, default: `true`) - If you want the translationManager to sort it's output, both json and console output -- `printers` (optional, default: {}) +- `jsonOptions` (optional, default: { space: 2, trailingNewline: false }) +- `overridePrinters` (optional, default: {}) - Here you can specify custom logging methods. If not specified a default printer is used. - Possible printers to configure: ```js @@ -146,6 +149,65 @@ You can optionally pass a printer object to this method. This way you can overri printNoLanguageWhitelistFile: ( lang ) => { console.log(`No existing ${lang} file found. A new one is created.`) }, }; ``` +- `overrideCoreMethods` (optional, default: {}) + - Here you can specify overrides for the core hooks. If not specified, the + default methods will be used. + - Possible overrides to configure: + ```js + const overrideCoreMethods = { + provideExtractedMessages: () => {}, + outputSingleFile: () => {}, + outputDuplicateKeys: () => {}, + beforeReporting: () => {}, + provideLangTemplate: () => {}, + provideTranslationsFile: () => {}, + provideWhitelistFile: () => {}, + reportLanguage: () => {}, + afterReporting: () => {}, + } + ``` + +#### Fully configured + +This is the config with all options applied: + +```js +// import manageTranslations from 'react-intl-translations-manager'; + +manageTranslations({ + messagesDirectory: 'src/translations/extractedMessages', + translationsDirectory: 'src/translations/locales/', + whitelistsDirectory: 'src/translations/locales/whitelists/', + languages: ['nl'], // any language you need + singleMessagesFile: true, + detectDuplicateIds: false, + sortKeys: false, + jsonOptions: { + space: 4, + trailingNewline: true, + }, + overridePrinters: { + printDuplicateIds: ( duplicateIds ) => { console.log(`You have ${duplicateIds.length } duplicate IDs`) }, + printLanguageReport: ( report ) => { console.log('Log report for a language') }, + printNoLanguageFile: ( lang ) => { console.log(`No existing ${lang} translation file found. A new one is created.`) }, + printNoLanguageWhitelistFile: ( lang ) => { console.log(`No existing ${lang} file found. A new one is created.`) }, + }, + overrideCoreMethods: { + provideExtractedMessages: () => {}, + outputSingleFile: () => {}, + outputDuplicateKeys: () => {}, + beforeReporting: () => {}, + provideLangTemplate: () => {}, + provideTranslationsFile: () => {}, + provideWhitelistFile: () => {}, + reportLanguage: () => {}, + afterReporting: () => {}, + }, +}); +``` + +*This config is only as illustration for all possible options, these arent +recommended configuration options. ### core ```js From ed771cbcfaf53965a769ebe42f5307320893d392 Mon Sep 17 00:00:00 2001 From: Gertjan Reynaert Date: Fri, 16 Dec 2016 19:47:49 +0100 Subject: [PATCH 5/6] docs(Core): Remove core from readme --- README.md | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 7a49612..e022cb2 100644 --- a/README.md +++ b/README.md @@ -209,28 +209,10 @@ manageTranslations({ *This config is only as illustration for all possible options, these arent recommended configuration options. -### core -```js -core(languages, hooks); -``` - -This is the core of the translationManager. It just takes a list of languages -and an object with all kinds of hooks it will execute when running. Below you -can find all hooks. +### CoreMethods -```js -const hooks = { - provideExtractedMessages, - outputSingleFile, - outputDuplicateKeys, - beforeReporting, - provideLangTemplate, - provideTranslationsFile, - provideWhitelistFile, - reportLanguage, - afterReporting, -}; -``` +These are the core methods of the translationManager and what purpose they +serve. #### provideExtractedMessages ```js From 438041ab58c88fe913e92dc722063bcaffb357e6 Mon Sep 17 00:00:00 2001 From: Gertjan Reynaert Date: Fri, 16 Dec 2016 19:53:51 +0100 Subject: [PATCH 6/6] refactor(Core): Remove Core from exposed api BREAKING CHANGE: Since the full potential of core is now exposed through the default translationManager, it is no longer necessary to expose this method --- src/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.js b/src/index.js index 6ca0297..4f5578c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,5 @@ export { default } from './manageTranslations'; -export { default as core } from './core'; export { default as readMessageFiles } from './readMessageFiles'; export { default as createSingleMessagesFile } from './createSingleMessagesFile'; export { default as getDefaultMessages } from './getDefaultMessages';