diff --git a/.tx/config b/.tx/config new file mode 100644 index 000000000..c365d1871 --- /dev/null +++ b/.tx/config @@ -0,0 +1,11 @@ +[main] +host = https://www.transifex.com +lang_map = zh_CN: zh-CN, zh_HK: zh-HK, zh_TW: zh-TW, ko_KR: ko-KR + +[ipfs-desktop.ipfs-desktop-json] +file_filter = src/locales/.json +minimum_perc = 95 +source_file = src/locales/en.json +source_lang = en +type = KEYVALUEJSON + diff --git a/README.md b/README.md index d9adeeb89..6d6aab4a3 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ IPFS Desktop allows you to run your IPFS Node on your machine without having to - [Install](#install) - [Contribute](#contribute) + - [Translations](#translations) ## Install @@ -40,12 +41,19 @@ npm start The IPFS Desktop app will launch and should appear in your OS menu bar. +## Translations + +The translations are stored on [./src/locales](./src/locales) and the English version is the source of truth. +Other languages are periodically pulled from [Transifex](https://www.transifex.com/ipfs/ipfs-desktop/), a web interface to help us translate IPFS Desktop and its components to another languages. + ## Contribute -[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md) +[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/#contributing-guidelines) Feel free to join in. All welcome. Open an [issue](https://github.com/ipfs-shipyard/ipfs-desktop/issues)! +If you're interested in contributing translations, go to [project page on Transifex](https://www.transifex.com/ipfs/ipfs-desktop/translate/), create an account, pick a language and start translating. + This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). ## License diff --git a/package-lock.json b/package-lock.json index 18f509571..d288c1a27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1186,7 +1186,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" }, @@ -1194,8 +1193,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" } } }, @@ -1272,6 +1270,11 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -2438,6 +2441,15 @@ "sha.js": "^2.4.8" } }, + "create-react-context": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.3.tgz", + "integrity": "sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==", + "requires": { + "fbjs": "^0.8.0", + "gud": "^1.0.0" + } + }, "cross-env": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.0.tgz", @@ -3066,6 +3078,14 @@ "env-variable": "0.0.x" } }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -3502,8 +3522,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { "version": "1.0.1", @@ -3781,6 +3800,40 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz", "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==" }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "http://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + } + } + }, "fd-slicer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", @@ -4706,6 +4759,11 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" }, + "gud": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", + "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==" + }, "gunzip-maybe": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/gunzip-maybe/-/gunzip-maybe-1.4.1.tgz", @@ -4959,6 +5017,14 @@ "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.0.3.tgz", "integrity": "sha512-TU6RyZ/XaQCTWRLrdqZZtZqwxUVr6PDMfi6MlWNURZ7A6czanQqX4pFE1mdOUQR9FdPCsZ0UzL8jI/izZ+eBSQ==" }, + "hoist-non-react-statics": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.0.1.tgz", + "integrity": "sha512-1kXwPsOi0OGQIZNVMPvgWJ9tSnGMiMfJdihqEzrPEXlHOBh9AAHXX/QYmAJTXztnz/K+PQ8ryCb4eGaN6HlGbQ==", + "requires": { + "react-is": "^16.3.2" + } + }, "home-or-tmp": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-3.0.0.tgz", @@ -4971,6 +5037,14 @@ "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", "dev": true }, + "html-parse-stringify2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz", + "integrity": "sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o=", + "requires": { + "void-elements": "^2.0.1" + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -4981,11 +5055,52 @@ "sshpk": "^1.7.0" } }, + "i18next": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-12.1.0.tgz", + "integrity": "sha512-AexmwGkKxwKfo5fGeXTWEY4xqzRPigQ1S/0InOUUVziGO54cd4fKyYK8ED1Thx9fd+WA3fRSZ+1iekvFQMbsFw==" + }, + "i18next-electron-language-detector": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/i18next-electron-language-detector/-/i18next-electron-language-detector-0.0.10.tgz", + "integrity": "sha512-l/CdtK5i6BB7h5OGKadUK+Q0q4e4EYXZSDV+Hetxjdv4C8RoYPNbqfTIpcc4RpIO3Dty05Xt8TxV+HyFd6opeA==" + }, + "i18next-icu": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/i18next-icu/-/i18next-icu-0.6.0.tgz", + "integrity": "sha512-8s/2FVdd0ZSNzrtXAStv3aMCnSr/2RttCKw03Uze3L2vLTRL6X5rgqSvCYi8hGdGeljCd3LG1SMkQ1iXejQpjg==", + "requires": { + "intl-messageformat": "2.2.0" + } + }, + "i18next-node-fs-backend": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/i18next-node-fs-backend/-/i18next-node-fs-backend-2.1.0.tgz", + "integrity": "sha512-ULb+uQYQj1njn/93cxkAtgrw9STCv1zEM8QySH24c0pFxw8ZJPCV9yq4zTqEWtKaNRd8apvzyr2euoV1k05SbA==", + "requires": { + "js-yaml": "3.12.0", + "json5": "2.0.0" + }, + "dependencies": { + "json5": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.0.0.tgz", + "integrity": "sha512-0EdQvHuLm7yJ7lyG5dp7Q3X2ku++BG5ZHaJ5FTnaXpKqDrw4pMxel5Bt3oAYMthnrthFBdnZ1FcsXTPyrQlV0w==", + "requires": { + "minimist": "^1.2.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -5116,6 +5231,19 @@ "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", "dev": true }, + "intl-messageformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-2.2.0.tgz", + "integrity": "sha1-NFvNRt5jC3aDMwwuUhd/9eq0hPw=", + "requires": { + "intl-messageformat-parser": "1.4.0" + } + }, + "intl-messageformat-parser": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz", + "integrity": "sha1-tD1FqXRoytvkQzHXS7Ho3qRPwHU=" + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -5743,6 +5871,15 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -5801,7 +5938,6 @@ "version": "3.12.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -6624,6 +6760,15 @@ } } }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, "node-forge": { "version": "0.7.6", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz", @@ -7819,6 +7964,22 @@ } } }, + "react-i18next": { + "version": "8.3.8", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-8.3.8.tgz", + "integrity": "sha512-ZcSpakSBcDxPJkl34fv/SI0TaoTDvVDrk4WpDF+WElorine+dHUjGMAA6RG5Km2KcLNW1t4GLunHprgKiqDrSw==", + "requires": { + "@babel/runtime": "^7.1.2", + "create-react-context": "0.2.3", + "hoist-non-react-statics": "3.0.1", + "html-parse-stringify2": "2.0.1" + } + }, + "react-is": { + "version": "16.6.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.3.tgz", + "integrity": "sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA==" + }, "read-config-file": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-3.2.0.tgz", @@ -8408,6 +8569,11 @@ } } }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, "sha.js": { "version": "2.4.11", "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", @@ -9597,6 +9763,11 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, + "ua-parser-js": { + "version": "0.7.19", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz", + "integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==" + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -9924,6 +10095,11 @@ } } }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" + }, "web-namespaces": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.2.tgz", @@ -9934,6 +10110,11 @@ "version": "github:dignifiedquire/webcrypto-shim#190bc9ec341375df6025b17ae12ddb2428ea49c8", "from": "github:dignifiedquire/webcrypto-shim#master" }, + "whatwg-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", + "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/package.json b/package.json index e5814094c..df16f4a2b 100644 --- a/package.json +++ b/package.json @@ -86,11 +86,16 @@ "filesize": "^3.6.1", "fs-extra": "^7.0.1", "go-ipfs-dep": "^0.4.18", + "i18next": "^12.1.0", + "i18next-electron-language-detector": "0.0.10", + "i18next-icu": "^0.6.0", + "i18next-node-fs-backend": "^2.1.0", "ipfs-css": "^0.12.0", "ipfsd-ctl": "^0.40.0", "is-ipfs": "^0.4.7", "react": "^16.6.3", "react-dom": "^16.6.3", + "react-i18next": "^8.3.8", "redux-bundler": "^22.2.0", "redux-bundler-react": "^1.1.0", "tachyons": "^4.11.1", diff --git a/src/locales/en.json b/src/locales/en.json new file mode 100644 index 000000000..6c1675c28 --- /dev/null +++ b/src/locales/en.json @@ -0,0 +1,9 @@ +{ + "turnOff": "Turn off IPFS Desktop", + "ipfsNode": "IPFS Node", + "status": "Status", + "files": "Files", + "explore": "Explore", + "peers": "Peers", + "settings": "Settings" +} diff --git a/src/locales/pl.json b/src/locales/pl.json new file mode 100644 index 000000000..6b0e1ae4a --- /dev/null +++ b/src/locales/pl.json @@ -0,0 +1,9 @@ +{ + "turnOff": "Wyłącz IPFS Desktop", + "ipfsNode": "Węzeł IPFS", + "status": "Status", + "files": "Pliki", + "explore": "Przeglądaj", + "peers": "Węzły", + "settings": "Ustawienia" +} diff --git a/src/menubar/app/App.js b/src/menubar/app/App.js index 3bab19ce5..9be0b2cfe 100644 --- a/src/menubar/app/App.js +++ b/src/menubar/app/App.js @@ -1,4 +1,5 @@ import React from 'react' +import { translate } from 'react-i18next' import { connect } from 'redux-bundler-react' import Heartbeat from './components/heartbeat/Heartbeat' import GlyphPower from '../../icons/GlyphPower' @@ -31,7 +32,7 @@ const Button = ({ children, on, ...props }) => ( ) -const Menubar = ({ ipfsIsRunning, doQuitApp, currentConfig }) => ( +const Menubar = ({ t, ipfsIsRunning, doQuitApp, currentConfig }) => (
@@ -41,18 +42,18 @@ const Menubar = ({ ipfsIsRunning, doQuitApp, currentConfig }) => ( online={ipfsIsRunning} />
IPFS
-
- Status - Files - Explore - Peers - Settings + {t('status')} + {t('files')} + {t('explore')} + {t('peers')} + {t('settings')}
) @@ -62,5 +63,5 @@ export default connect( 'doIpfsStartListening', 'selectIpfsIsRunning', 'selectCurrentConfig', - Menubar + translate()(Menubar) ) diff --git a/src/menubar/app/index.js b/src/menubar/app/index.js index dbe923b9f..e33e62ae8 100644 --- a/src/menubar/app/index.js +++ b/src/menubar/app/index.js @@ -4,10 +4,14 @@ import { Provider } from 'redux-bundler-react' import App from './App' import getStore from './bundles' import registerScreenshot from './utils/screenshot' +import { I18nextProvider } from 'react-i18next' +import i18n from '../../utils/i18n' ReactDOM.render( - + + + , document.getElementById('root')) registerScreenshot() diff --git a/src/menubar/index.js b/src/menubar/index.js index 6ac2f1679..a08eb1820 100644 --- a/src/menubar/index.js +++ b/src/menubar/index.js @@ -1,5 +1,5 @@ import { Menubar } from 'electron-menubar' -import { logo, logger } from '../utils' +import { logo, logger, i18n } from '../utils' import { app, ipcMain } from 'electron' export default async function (ctx) { @@ -7,7 +7,7 @@ export default async function (ctx) { const menubar = new Menubar({ index: `file://${__dirname}/app/index.html`, icon: logo('ice'), - tooltip: 'IPFS Node', + tooltip: i18n.t('ipfsNode'), preloadWindow: true, window: { resizable: false, diff --git a/src/utils/i18n.js b/src/utils/i18n.js new file mode 100644 index 000000000..5acee1201 --- /dev/null +++ b/src/utils/i18n.js @@ -0,0 +1,25 @@ +import { join } from 'path' +import i18n from 'i18next' +import ICU from 'i18next-icu' +import Backend from 'i18next-node-fs-backend' +import LanguageDetector from 'i18next-electron-language-detector' + +import en from 'i18next-icu/locale-data/en' + +const localeData = [en] + +i18n + .use(new ICU({ localeData: localeData })) + .use(Backend) + .use(LanguageDetector) + .init({ + fallbackLng: { + 'default': ['en'] + }, + debug: process.env.NODE_ENV !== 'production', + backend: { + loadPath: join(__dirname, '../locales/{{lng}}.json') + } + }) + +export default i18n diff --git a/src/utils/index.js b/src/utils/index.js index 3f05c5dc5..f9042054b 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -2,10 +2,12 @@ import createDaemon from './daemon' import logo from './logo' import store from './store' import logger from './logger' +import i18n from './i18n' export { createDaemon, logo, store, - logger + logger, + i18n }