From 7428384b55e6089dbe45e6b216eee0b670dff576 Mon Sep 17 00:00:00 2001 From: Priscila Oliveira Date: Sun, 8 Mar 2020 16:45:07 +0100 Subject: [PATCH] feat(i18n): added i18next for user interface translations (#432) --- i18n/config.ts | 32 +++ i18n/translations/en-US.json | 138 +++++++++ i18n/translations/pt-BR.json | 138 +++++++++ package.json | 4 +- partials/storage/jquery/package.json | 10 +- src/App/App.tsx | 13 +- src/App/AppRoute.tsx | 66 +++-- src/App/__snapshots__/App.test.tsx.snap | 263 ++++++------------ src/App/load-dayjs-locale.ts | 40 +++ src/components/ActionBar/ActionBarAction.tsx | 8 +- src/components/Author/Author.tsx | 8 +- .../Author/__snapshots__/Author.test.tsx.snap | 4 +- src/components/Author/styles.ts | 1 - src/components/AutoComplete/AutoComplete.tsx | 15 +- .../CopyToClipBoard/CopyToClipBoard.tsx | 17 +- .../CopyToClipBoard.test.tsx.snap | 2 +- src/components/Dependencies/Dependencies.tsx | 17 +- .../DetailContainer/DetailContainer.tsx | 21 +- .../DetailContainer/DetailContainerTabs.tsx | 41 ++- src/components/DetailContainer/tabs.ts | 8 +- .../DetailSidebar/DetailSidebarFundButton.tsx | 4 +- .../DetailSidebar/DetailSidebarTitle.tsx | 20 +- src/components/Developers/Developers.test.tsx | 3 +- src/components/Developers/Developers.tsx | 20 +- src/components/Developers/DevelopersTitle.tsx | 30 ++ .../__snapshots__/Developers.test.tsx.snap | 202 +++++++------- src/components/Developers/index.ts | 3 +- src/components/Developers/types.ts | 4 + src/components/Dist/Dist.tsx | 10 +- .../Dist/__snapshots__/Dist.test.tsx.snap | 6 +- src/components/Engines/Engines.tsx | 6 +- .../__snapshots__/Engines.test.tsx.snap | 2 +- src/components/Footer/Footer.tsx | 69 ++--- .../Footer/__snapshots__/Footer.test.tsx.snap | 4 +- src/components/Header/Header.test.tsx | 9 +- src/components/Header/Header.tsx | 6 +- src/components/Header/HeaderGreetings.tsx | 16 +- src/components/Header/HeaderMenu.tsx | 64 +++-- src/components/Header/HeaderRight.tsx | 10 +- src/components/Help/Help.tsx | 14 +- .../Help/__snapshots__/Help.test.tsx.snap | 4 +- src/components/Install/Install.tsx | 6 +- src/components/Install/InstallListItem.tsx | 15 +- .../__snapshots__/Install.test.tsx.snap | 6 +- src/components/Link/Link.tsx | 3 +- .../LoginDialog/LoginDialog.test.tsx | 7 +- src/components/LoginDialog/LoginDialog.tsx | 6 +- .../LoginDialog/LoginDialogCloseButton.tsx | 18 +- .../LoginDialog/LoginDialogForm.tsx | 21 +- .../LoginDialog/LoginDialogHeader.tsx | 7 +- src/components/NotFound/NotFound.tsx | 14 +- src/components/NotFound/Notfound.test.tsx | 10 +- .../__snapshots__/Notfound.test.tsx.snap | 1 + src/components/NotFound/index.ts | 2 +- src/components/Package/Package.tsx | 38 +-- .../RegistryInfoDialog/RegistryInfoDialog.tsx | 28 +- src/components/Repository/Repository.tsx | 4 +- src/components/Search/Search.tsx | 5 +- src/components/UpLinks/UpLinks.tsx | 6 +- .../__snapshots__/UpLinks.test.tsx.snap | 4 +- src/components/Versions/Versions.test.tsx | 11 +- src/components/Versions/Versions.tsx | 11 +- .../Versions/VersionsHistoryList.tsx | 39 +-- src/icons/GitHub.tsx | 14 - src/icons/index.ts | 1 - src/pages/Version/Version.test.tsx | 6 +- src/pages/home/Home.tsx | 6 +- src/utils/constants.ts | 4 - src/utils/login.test.ts | 16 ++ src/utils/login.ts | 5 +- src/utils/package.ts | 5 + src/utils/test-enzyme.ts | 1 + src/utils/test-react-testing-library.tsx | 9 +- tools/dev.server.js | 2 +- types/index.ts | 1 + yarn.lock | 150 ++++++---- 76 files changed, 1114 insertions(+), 720 deletions(-) create mode 100644 i18n/config.ts create mode 100644 i18n/translations/en-US.json create mode 100644 i18n/translations/pt-BR.json create mode 100644 src/App/load-dayjs-locale.ts create mode 100644 src/components/Developers/DevelopersTitle.tsx create mode 100644 src/components/Developers/types.ts delete mode 100644 src/icons/GitHub.tsx delete mode 100644 src/icons/index.ts diff --git a/i18n/config.ts b/i18n/config.ts new file mode 100644 index 0000000..150bd07 --- /dev/null +++ b/i18n/config.ts @@ -0,0 +1,32 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; + +import translationEN from './translations/en-US.json'; +import translationPT from './translations/pt-BR.json'; + +i18n + // pass the i18n instance to react-i18next. + .use(initReactI18next) + // init i18next + // for all options read: https://www.i18next.com/overview/configuration-options + .init({ + // in case window.VEDACCIO_LANGUAGE is undefined,it will fall back to 'en-US' + lng: window?.__VERDACCIO_BASENAME_UI_OPTIONS?.language, + fallbackLng: 'en-US', + whitelist: ['en-US', 'pt-BR'], + load: 'currentOnly', + resources: { + 'en-US': { + translation: translationEN, + }, + 'pt-BR': { + translation: translationPT, + }, + }, + debug: false, + interpolation: { + escapeValue: false, // react already safes from xss + }, + }); + +export default i18n; diff --git a/i18n/translations/en-US.json b/i18n/translations/en-US.json new file mode 100644 index 0000000..4720cc3 --- /dev/null +++ b/i18n/translations/en-US.json @@ -0,0 +1,138 @@ +{ + "copy-to-clipboard": "Copy to clipboard", + "author-anonymous": "Anonymous", + "action-bar-action": { + "visit-home-page": "Visit homepage", + "open-an-issue": "Open an issue", + "download-tarball": "Download tarball" + }, + "dialog": { + "registry-info": { + "title": "Register Info" + } + }, + "header": { + "documentation": "Documentation", + "registry-info": "Registry Information", + "greetings": "Hi " + }, + "search": { + "packages": "Search Packages" + }, + "auto-complete": { + "loading": "Loading...", + "no-results-found": "No results found." + }, + "tab": { + "uplinks": "Uplinks", + "versions": "Versions", + "dependencies": "Dependencies", + "readme": "Readme" + }, + "uplinks": { + "title": "Uplinks", + "no-items": "{{name}} has no uplinks." + }, + "versions": { + "current-tags": "Current Tags", + "version-history": "Version history", + "not-available": "Not available" + }, + "package": { + "published-on": "Published on {{time}} •", + "version": "v{{version}}", + "visit-home-page": "Visit homepage", + "homepage": "Homepage", + "open-an-issue": "Open an issue", + "bugs": "Bugs", + "download": "Download {{what}}", + "the-tar-file": "the tar file", + "tarball": "Tarball" + }, + "dependencies": { + "has-no-dependencies": "{{package}} has no dependencies.", + "dependency-block": "{{package}}@{{version}}" + }, + "form": { + "username": "Username", + "password": "Password" + }, + "form-placeholder": { + "username": "Your username", + "password": "Your strong password" + }, + "form-validation": { + "required-field": "This field is required", + "required-min-length": "This field required the min length of {{length}}", + "unable-to-sign-in": "Unable to sign in", + "username-or-password-cant-be-empty": "Username or password can't be empty!" + }, + "help": { + "title": "No Package Published Yet.", + "sub-title": "To publish your first package just:", + "first-step": "1. Login", + "first-step-command-line": "npm adduser --registry {{registryUrl}}", + "second-step": "2. Publish", + "second-step-command-line": "npm publish --registry {{registryUrl}}", + "third-step": "3. Refresh this page." + }, + "sidebar": { + "detail": { + "latest-version": "Latest v{{version}}", + "version": "v{{version}}" + }, + "installation": { + "title": "Installation", + "install-using-yarn": "Install using yarn", + "install-using-yarn-command": "yarn add {{packageName}}", + "install-using-npm": "Install using npm", + "install-using-npm-command": "npm install {{packageName}}", + "install-using-pnpm": "Install using pnpm", + "install-using-pnpm-command": "pnpm install {{packageName}}" + }, + "repository": { + "title": "Repository" + }, + "author": { + "title": "Author" + }, + "distribution": { + "title": "Latest Distribution", + "license": "License", + "size": "Size", + "file-count": "file count" + }, + "maintainers": { + "title": "Maintainers" + }, + "contributors": { + "title": "Contributors" + }, + "engines": { + "npm-version": "NPM Version", + "node-js": "NODE JS" + } + }, + "footer": { + "powered-by": "Powered by", + "made-with-love-on": "Made with <0>♥ on" + }, + "button": { + "close": "Close", + "cancel": "Cancel", + "login": "Login", + "logout": "Logout", + "go-to-the-home-page": "Go to the home page", + "learn-more": "Learn More", + "fund-this-package": "<0>Fund this package" + }, + "error": { + "unspecific": "Something went wrong.", + "404": { + "page-not-found": "404 - Page not found", + "sorry-we-could-not-find-it": "Sorry, we couldn't find it..." + }, + "app-context-not-correct-used": "The app context was not correct used", + "package-meta-is-required-at-detail-context": "packageMeta is required at DetailContext" + } +} \ No newline at end of file diff --git a/i18n/translations/pt-BR.json b/i18n/translations/pt-BR.json new file mode 100644 index 0000000..8434f1c --- /dev/null +++ b/i18n/translations/pt-BR.json @@ -0,0 +1,138 @@ +{ + "copy-to-clipboard": "Copiar para área de transferência", + "author-anonymous": "Anônimo(a)", + "action-bar-action": { + "visit-home-page": "Visitar a página inicial", + "open-an-issue": "Criar um incidente", + "download-tarball": "Baixar Tarball" + }, + "dialog": { + "registry-info": { + "title": "Informações do Registro" + } + }, + "header": { + "documentation": "Documentação", + "registry-info": "Informações do Registro", + "greetings": "Oi " + }, + "search": { + "packages": "Pesquisar Pacotes" + }, + "auto-complete": { + "loading": "Carregando...", + "no-results-found": "Nenhum resultado encontrado." + }, + "tab": { + "uplinks": "Uplinks", + "versions": "Versões", + "dependencies": "Dependências", + "readme": "Leia-me" + }, + "uplinks": { + "title": "Uplinks", + "no-items": "{{name}} não tem uplinks." + }, + "versions": { + "current-tags": "Tags atuais", + "version-history": "Histórico de versões", + "not-available": "Não disponível" + }, + "package": { + "published-on": "Publicado em {{time}} •", + "version": "v{{version}}", + "visit-home-page": "Visitar a página inicial", + "homepage": "Página inicial", + "open-an-issue": "Criar um incidente", + "bugs": "Erros", + "download": "Baixar {{what}}", + "the-tar-file": "o arquivo tar", + "tarball": "Tarball" + }, + "dependencies": { + "has-no-dependencies": "{{package}} não tem dependências.", + "dependency-block": "{{package}}@{{version}}" + }, + "form": { + "username": "Nome do usuário", + "password": "Senha" + }, + "form-placeholder": { + "username": "O seu nome", + "password": "A sua senha forte" + }, + "form-validation": { + "required-field": "Este campo é obrigatório", + "required-min-length": "Este campo requer o mínimo de {{length}} caracteres", + "unable-to-sign-in": "Não foi possível fazer login", + "username-or-password-cant-be-empty": "Nome de usuário ou senha não podem estar vazios!" + }, + "help": { + "title": "Nenhum pacote publicado ainda.", + "sub-title": "Para publicar seu primeiro pacote apenas:", + "first-step": "1. Faça login", + "first-step-command-line": "npm adduser --registry {{registryUrl}}", + "second-step": "2. Publique", + "second-step-command-line": "npm publish --registry {{registryUrl}}", + "third-step": "3. Atualize esta página." + }, + "sidebar": { + "detail": { + "latest-version": "Última versão: v{{version}}", + "version": "v{{version}}" + }, + "installation": { + "title": "Instalação", + "install-using-yarn": "Instale usando yarn", + "install-using-yarn-command": "yarn add {{packageName}}", + "install-using-npm": "Instale usando npm", + "install-using-npm-command": "npm install {{packageName}}", + "install-using-pnpm": "Instale usando pnpm", + "install-using-pnpm-command": "pnpm install {{packageName}}" + }, + "repository": { + "title": "Repositório" + }, + "author": { + "title": "Autor(a)" + }, + "distribution": { + "title": "Distribuição mais recente", + "license": "Licença", + "size": "Tamanho", + "file-count": "Contagem de arquivos" + }, + "maintainers": { + "title": "Mantenedores(as)" + }, + "contributors": { + "title": "Contribuidores(as)" + }, + "engines": { + "npm-version": "Versão NPM", + "node-js": "NODE JS" + } + }, + "footer": { + "powered-by": "Distribuído por", + "made-with-love-on": "Feito com amor <0>♥ no(a)" + }, + "button": { + "close": "Fechar", + "cancel": "Cancelar", + "login": "Conectar", + "logout": "Desconectar", + "go-to-the-home-page": "Ir para a página inicial", + "learn-more": "Leia mais", + "fund-this-package": "<0>Financie este pacote" + }, + "error": { + "unspecific": "Algo deu errado.", + "404": { + "page-not-found": "404 - Página não encontrada", + "sorry-we-could-not-find-it": "Desculpe, não conseguimos encontrar..." + }, + "app-context-not-correct-used": "O contexto do aplicativo não foi usado corretamente", + "package-meta-is-required-at-detail-context": "packageMeta é requerido em DetailContext" + } +} \ No newline at end of file diff --git a/package.json b/package.json index 4185e80..a275fe8 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "github-markdown-css": "3.0.1", "html-webpack-plugin": "3.2.0", "husky": "3.1.0", + "i18next": "19.1.0", "identity-obj-proxy": "3.0.0", "in-publish": "2.0.0", "jest": "24.9.0", @@ -99,6 +100,7 @@ "react-dom": "16.13.0", "react-hook-form": "3.29.4", "react-hot-loader": "4.12.18", + "react-i18next": "11.3.1", "react-router-dom": "5.1.2", "request": "2.88.0", "resolve-url-loader": "3.1.1", @@ -138,7 +140,7 @@ "bundlesize": [ { "path": "./static/vendors.*.js", - "maxSize": "185 kB" + "maxSize": "200 kB" }, { "path": "./static/main.*.js", diff --git a/partials/storage/jquery/package.json b/partials/storage/jquery/package.json index e01aea4..9293c80 100644 --- a/partials/storage/jquery/package.json +++ b/partials/storage/jquery/package.json @@ -4976,8 +4976,12 @@ "_attachments": { "jquery-1.5.1.tgz": { "shasum": "2ae2d661e906c1a01e044a71bb5b2743942183e5" + }, + "jquery-3.3.1.tgz": { + "shasum": "958ce29e81c9790f31be7792df5d4d95fc57fbca" } }, - "_rev": "60-fed4915c27b9c1e6", - "readme": "# jQuery\n\n> jQuery is a fast, small, and feature-rich JavaScript library.\n\nFor information on how to get started and how to use jQuery, please see [jQuery's documentation](http://api.jquery.com/).\nFor source files and issues, please visit the [jQuery repo](https://github.com/jquery/jquery).\n\nIf upgrading, please see the [blog post for 3.3.1](https://blog.jquery.com/2017/03/20/jquery-3.3.1-now-available/). This includes notable differences from the previous version and a more readable changelog.\n\n## Including jQuery\n\nBelow are some of the most common ways to include jQuery.\n\n### Browser\n\n#### Script tag\n\n```html\n\n```\n\n#### Babel\n\n[Babel](http://babeljs.io/) is a next generation JavaScript compiler. One of the features is the ability to use ES6/ES2015 modules now, even though browsers do not yet support this feature natively.\n\n```js\nimport $ from \"jquery\";\n```\n\n#### Browserify/Webpack\n\nThere are several ways to use [Browserify](http://browserify.org/) and [Webpack](https://webpack.github.io/). For more information on using these tools, please refer to the corresponding project's documention. In the script, including jQuery will usually look like this...\n\n```js\nvar $ = require(\"jquery\");\n```\n\n#### AMD (Asynchronous Module Definition)\n\nAMD is a module format built for the browser. For more information, we recommend [require.js' documentation](http://requirejs.org/docs/whyamd.html).\n\n```js\ndefine([\"jquery\"], function($) {\n\n});\n```\n\n### Node\n\nTo include jQuery in [Node](nodejs.org), first install with npm.\n\n```sh\nnpm install jquery\n```\n\nFor jQuery to work in Node, a window with a document is required. Since no such window exists natively in Node, one can be mocked by tools such as [jsdom](https://github.com/tmpvar/jsdom). This can be useful for testing purposes.\n\n```js\nrequire(\"jsdom\").env(\"\", function(err, window) {\n\tif (err) {\n\t\tconsole.error(err);\n\t\treturn;\n\t}\n\n\tvar $ = require(\"jquery\")(window);\n});\n```" -} + "_rev": "61-e6be890a78963127", + "readme": "# jQuery\n\n> jQuery is a fast, small, and feature-rich JavaScript library.\n\nFor information on how to get started and how to use jQuery, please see [jQuery's documentation](http://api.jquery.com/).\nFor source files and issues, please visit the [jQuery repo](https://github.com/jquery/jquery).\n\nIf upgrading, please see the [blog post for 3.3.1](https://blog.jquery.com/2017/03/20/jquery-3.3.1-now-available/). This includes notable differences from the previous version and a more readable changelog.\n\n## Including jQuery\n\nBelow are some of the most common ways to include jQuery.\n\n### Browser\n\n#### Script tag\n\n```html\n\n```\n\n#### Babel\n\n[Babel](http://babeljs.io/) is a next generation JavaScript compiler. One of the features is the ability to use ES6/ES2015 modules now, even though browsers do not yet support this feature natively.\n\n```js\nimport $ from \"jquery\";\n```\n\n#### Browserify/Webpack\n\nThere are several ways to use [Browserify](http://browserify.org/) and [Webpack](https://webpack.github.io/). For more information on using these tools, please refer to the corresponding project's documention. In the script, including jQuery will usually look like this...\n\n```js\nvar $ = require(\"jquery\");\n```\n\n#### AMD (Asynchronous Module Definition)\n\nAMD is a module format built for the browser. For more information, we recommend [require.js' documentation](http://requirejs.org/docs/whyamd.html).\n\n```js\ndefine([\"jquery\"], function($) {\n\n});\n```\n\n### Node\n\nTo include jQuery in [Node](nodejs.org), first install with npm.\n\n```sh\nnpm install jquery\n```\n\nFor jQuery to work in Node, a window with a document is required. Since no such window exists natively in Node, one can be mocked by tools such as [jsdom](https://github.com/tmpvar/jsdom). This can be useful for testing purposes.\n\n```js\nrequire(\"jsdom\").env(\"\", function(err, window) {\n\tif (err) {\n\t\tconsole.error(err);\n\t\treturn;\n\t}\n\n\tvar $ = require(\"jquery\")(window);\n});\n```", + "_id": "jquery" +} \ No newline at end of file diff --git a/src/App/App.tsx b/src/App/App.tsx index c2ba219..2b21059 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -1,18 +1,22 @@ -import React, { useState, useEffect } from 'react'; +/* eslint-disable react/jsx-max-depth */ +import React, { useState, useEffect, Suspense } from 'react'; import styled from '@emotion/styled'; import isNil from 'lodash/isNil'; import { Router } from 'react-router-dom'; +import '../../i18n/config'; import storage from '../utils/storage'; import { isTokenExpire } from '../utils/login'; import Header from '../components/Header'; import Footer from '../components/Footer'; +import Loading from '../components/Loading'; import Box from '../muiComponents/Box'; import StyleBaseline from '../design-tokens/StyleBaseline'; import { Theme } from '../design-tokens/theme'; import AppContextProvider from './AppContextProvider'; import AppRoute, { history } from './AppRoute'; +import loadDayJSLocale from './load-dayjs-locale'; const StyledBox = styled(Box)<{ theme?: Theme }>(({ theme }) => ({ backgroundColor: theme && theme.palette.white, @@ -31,7 +35,6 @@ const StyledBoxContent = styled(Box)<{ theme?: Theme }>(({ theme }) => ({ /* eslint-disable react-hooks/exhaustive-deps */ const App: React.FC = () => { const [user, setUser] = useState(); - /** * Logout user * Required by:
@@ -57,10 +60,11 @@ const App: React.FC = () => { useEffect(() => { checkUserAlreadyLoggedIn(); + loadDayJSLocale(); }, []); return ( - <> + }> <> @@ -68,7 +72,6 @@ const App: React.FC = () => {
- {/* eslint-disable-next-line react/jsx-max-depth */} @@ -76,7 +79,7 @@ const App: React.FC = () => {
- + ); }; diff --git a/src/App/AppRoute.tsx b/src/App/AppRoute.tsx index 0fce9f0..8535cec 100644 --- a/src/App/AppRoute.tsx +++ b/src/App/AppRoute.tsx @@ -1,8 +1,7 @@ -import React, { lazy, useContext, Suspense } from 'react'; +import React, { lazy, useContext } from 'react'; import { Route as ReactRouterDomRoute, Switch, Router } from 'react-router-dom'; import { createBrowserHistory } from 'history'; - -import Loading from '../components/Loading'; +import { useTranslation } from 'react-i18next'; import AppContext from './AppContext'; @@ -25,9 +24,10 @@ export const history = createBrowserHistory({ const AppRoute: React.FC = () => { const appContext = useContext(AppContext); + const { t } = useTranslation(); if (!appContext) { - throw Error('The app Context was not correct used'); + throw Error(t('app-context-not-correct-used')); } const { user } = appContext; @@ -36,36 +36,34 @@ const AppRoute: React.FC = () => { return ( - }> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); }; diff --git a/src/App/__snapshots__/App.test.tsx.snap b/src/App/__snapshots__/App.test.tsx.snap index ae9c1dd..057a7c3 100644 --- a/src/App/__snapshots__/App.test.tsx.snap +++ b/src/App/__snapshots__/App.test.tsx.snap @@ -152,53 +152,6 @@ exports[` should display the Header component 1`] = ` } } -.emotion-34 { - -webkit-transform: translate(-50%,-50%); - -ms-transform: translate(-50%,-50%); - transform: translate(-50%,-50%); - top: 50%; - left: 50%; - position: absolute; -} - -.emotion-28 { - margin: 0 0 30px 0; - border-radius: 25px; - box-shadow: 0 10px 20px 0 rgba(69,58,100,0.2); - background: #f7f8f6; -} - -.emotion-26 { - display: inline-block; - vertical-align: middle; - box-sizing: border-box; - background-position: center; - background-size: contain; - background-image: url([object Object]); - background-repeat: no-repeat; - width: 90px; - height: 90px; -} - -.emotion-32 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; - -ms-flex-pack: center; - justify-content: center; -} - -.emotion-30 { - color: #4b5e40; -} - .emotion-76 { background: #f9f9f9; border-top: 1px solid #e3e3e3; @@ -356,6 +309,53 @@ exports[` should display the Header component 1`] = ` height: auto; } +.emotion-34 { + -webkit-transform: translate(-50%,-50%); + -ms-transform: translate(-50%,-50%); + transform: translate(-50%,-50%); + top: 50%; + left: 50%; + position: absolute; +} + +.emotion-28 { + margin: 0 0 30px 0; + border-radius: 25px; + box-shadow: 0 10px 20px 0 rgba(69,58,100,0.2); + background: #f7f8f6; +} + +.emotion-26 { + display: inline-block; + vertical-align: middle; + box-sizing: border-box; + background-position: center; + background-size: contain; + background-image: url([object Object]); + background-repeat: no-repeat; + width: 90px; + height: 90px; +} + +.emotion-32 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; +} + +.emotion-30 { + color: #4b5e40; +} +
@@ -545,6 +545,7 @@ exports[` should display the Header component 1`] = ` >
should display the Header component 1`] = `
- Made with + Made with - on + on @@ -700,7 +701,7 @@ exports[` should display the Header component 1`] = ` `; exports[` should display the Loading component at the beginning 1`] = ` -.emotion-78 { +.emotion-68 { background-color: #fff; } @@ -843,7 +844,7 @@ exports[` should display the Loading component at the beginning 1`] = ` } @media screen and (min-width:1240px) { - .emotion-36 { + .emotion-26 { max-width: 1240px; width: 100%; margin-left: auto; @@ -851,54 +852,7 @@ exports[` should display the Loading component at the beginning 1`] = ` } } -.emotion-34 { - -webkit-transform: translate(-50%,-50%); - -ms-transform: translate(-50%,-50%); - transform: translate(-50%,-50%); - top: 50%; - left: 50%; - position: absolute; -} - -.emotion-28 { - margin: 0 0 30px 0; - border-radius: 25px; - box-shadow: 0 10px 20px 0 rgba(69,58,100,0.2); - background: #f7f8f6; -} - -.emotion-26 { - display: inline-block; - vertical-align: middle; - box-sizing: border-box; - background-position: center; - background-size: contain; - background-image: url([object Object]); - background-repeat: no-repeat; - width: 90px; - height: 90px; -} - -.emotion-32 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; - -ms-flex-pack: center; - justify-content: center; -} - -.emotion-30 { - color: #4b5e40; -} - -.emotion-76 { +.emotion-66 { background: #f9f9f9; border-top: 1px solid #e3e3e3; color: #999999; @@ -906,7 +860,7 @@ exports[` should display the Loading component at the beginning 1`] = ` padding: 20px; } -.emotion-74 { +.emotion-64 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -923,7 +877,7 @@ exports[` should display the Loading component at the beginning 1`] = ` } @media (min-width:768px) { - .emotion-74 { + .emotion-64 { min-width: 400px; max-width: 800px; margin: auto; @@ -935,12 +889,12 @@ exports[` should display the Loading component at the beginning 1`] = ` } @media (min-width:1024px) { - .emotion-74 { + .emotion-64 { max-width: 1240px; } } -.emotion-65 { +.emotion-55 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -949,7 +903,7 @@ exports[` should display the Loading component at the beginning 1`] = ` } @media (min-width:768px) { - .emotion-65 { + .emotion-55 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -957,21 +911,21 @@ exports[` should display the Loading component at the beginning 1`] = ` } } -.emotion-38 { +.emotion-28 { color: #e25555; padding: 0 5px; } -.emotion-63 { +.emotion-53 { position: relative; height: 18px; } -.emotion-63:hover .emotion-62 { +.emotion-53:hover .emotion-52 { visibility: visible; } -.emotion-41 { +.emotion-31 { box-sizing: initial; display: inline-block; cursor: default; @@ -980,7 +934,7 @@ exports[` should display the Loading component at the beginning 1`] = ` padding: 0 10px; } -.emotion-61 { +.emotion-51 { position: absolute; background: #d3dddd; padding: 1px 4px; @@ -998,7 +952,7 @@ exports[` should display the Loading component at the beginning 1`] = ` top: -2px; } -.emotion-61:before { +.emotion-51:before { content: ''; position: absolute; top: 29%; @@ -1011,7 +965,7 @@ exports[` should display the Loading component at the beginning 1`] = ` transform: rotate(90deg); } -.emotion-44 { +.emotion-34 { box-sizing: initial; display: inline-block; cursor: default; @@ -1020,7 +974,7 @@ exports[` should display the Loading component at the beginning 1`] = ` padding: 0 5px; } -.emotion-72 { +.emotion-62 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -1033,7 +987,7 @@ exports[` should display the Loading component at the beginning 1`] = ` } @media (min-width:768px) { - .emotion-72 { + .emotion-62 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -1041,7 +995,7 @@ exports[` should display the Loading component at the beginning 1`] = ` } } -.emotion-70 { +.emotion-60 { box-sizing: initial; display: inline-block; cursor: pointer; @@ -1050,13 +1004,14 @@ exports[` should display the Loading component at the beginning 1`] = ` padding: 0 5px; } -.emotion-67 { +.emotion-57 { width: 100%; height: auto; }
-
-
-
-
-
-
- - - -
-
-
-
+ class="MuiBox-root MuiBox-root-195 emotion-26 emotion-27" + />
- Made with + Made with - on + on Earth @@ -1308,10 +1227,10 @@ exports[` should display the Loading component at the beginning 1`] = ` /> Spain @@ -1321,7 +1240,7 @@ exports[` should display the Loading component at the beginning 1`] = ` /> Nicaragua @@ -1331,7 +1250,7 @@ exports[` should display the Loading component at the beginning 1`] = ` /> India @@ -1341,7 +1260,7 @@ exports[` should display the Loading component at the beginning 1`] = ` /> Brazil @@ -1351,7 +1270,7 @@ exports[` should display the Loading component at the beginning 1`] = ` /> China @@ -1361,7 +1280,7 @@ exports[` should display the Loading component at the beginning 1`] = ` /> Austria @@ -1374,16 +1293,16 @@ exports[` should display the Loading component at the beginning 1`] = `
Powered by Verdaccio diff --git a/src/App/load-dayjs-locale.ts b/src/App/load-dayjs-locale.ts new file mode 100644 index 0000000..3a20136 --- /dev/null +++ b/src/App/load-dayjs-locale.ts @@ -0,0 +1,40 @@ +import dayjs from 'dayjs'; +import i18n from 'i18next'; + +function getFallFackLanguage(): string | undefined { + const fallbackLanguage = i18n.options.fallbackLng; + + if (Array.isArray(fallbackLanguage)) { + return fallbackLanguage[0]; + } + + if (typeof fallbackLanguage === 'string') { + return fallbackLanguage; + } + + return undefined; +} + +function loadDayJSLocale() { + const fallbackLanguage = getFallFackLanguage(); + const locale = i18n.language || fallbackLanguage; + + // dayjs loades en-US by default + if (!locale || locale === 'en-US') { + return; + } + + switch (locale.toLowerCase()) { + // At the moment we only support pt-BR, please see: i18n/translations/* + case 'pt-br': + { + require('dayjs/locale/pt-br'); + dayjs.locale('pt-br'); + } + break; + default: + break; + } +} + +export default loadDayJSLocale; diff --git a/src/components/ActionBar/ActionBarAction.tsx b/src/components/ActionBar/ActionBarAction.tsx index 1192f16..6f46b63 100644 --- a/src/components/ActionBar/ActionBarAction.tsx +++ b/src/components/ActionBar/ActionBarAction.tsx @@ -3,6 +3,7 @@ import styled from '@emotion/styled'; import BugReportIcon from '@material-ui/icons/BugReport'; import DownloadIcon from '@material-ui/icons/CloudDownload'; import HomeIcon from '@material-ui/icons/Home'; +import { useTranslation } from 'react-i18next'; import Tooltip from '../../muiComponents/Tooltip'; import Link from '../Link'; @@ -26,10 +27,11 @@ export interface ActionBarActionProps { /* eslint-disable react/jsx-no-bind */ const ActionBarAction: React.FC = ({ type, link }) => { + const { t } = useTranslation(); switch (type) { case 'VISIT_HOMEPAGE': return ( - + @@ -39,7 +41,7 @@ const ActionBarAction: React.FC = ({ type, link }) => { ); case 'OPEN_AN_ISSUE': return ( - + @@ -49,7 +51,7 @@ const ActionBarAction: React.FC = ({ type, link }) => { ); case 'DOWNLOAD_TARBALL': return ( - + diff --git a/src/components/Author/Author.tsx b/src/components/Author/Author.tsx index 103be62..ef6b0bb 100644 --- a/src/components/Author/Author.tsx +++ b/src/components/Author/Author.tsx @@ -1,14 +1,17 @@ import React, { FC, useContext } from 'react'; +import { useTranslation } from 'react-i18next'; import { DetailContext } from '../../pages/Version'; import { isEmail } from '../../utils/url'; import Avatar from '../../muiComponents/Avatar'; import List from '../../muiComponents/List'; +import { getAuthorName } from '../../utils/package'; import { StyledText, AuthorListItem, AuthorListItemText } from './styles'; const Author: FC = () => { const { packageMeta } = useContext(DetailContext); + const { t } = useTranslation(); if (!packageMeta) { return null; @@ -25,7 +28,7 @@ const Author: FC = () => { const avatarComponent = ; return ( - {'Author'}}> + {t('sidebar.author.title')}}> {!email || !isEmail(email) ? ( avatarComponent @@ -34,8 +37,7 @@ const Author: FC = () => { {avatarComponent} )} - - + {name && } ); diff --git a/src/components/Author/__snapshots__/Author.test.tsx.snap b/src/components/Author/__snapshots__/Author.test.tsx.snap index 25a3e0f..f061be7 100644 --- a/src/components/Author/__snapshots__/Author.test.tsx.snap +++ b/src/components/Author/__snapshots__/Author.test.tsx.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` component should render the component in default state 1`] = `"
    Author
    \\"verdaccio
    verdaccio user
"`; +exports[` component should render the component in default state 1`] = `"
    sidebar.author.title
    \\"verdaccio
    verdaccio user
"`; -exports[` component should render the component when there is no author email 1`] = `"
    Author
    \\"verdaccio
    verdaccio user
"`; +exports[` component should render the component when there is no author email 1`] = `"
    sidebar.author.title
    \\"verdaccio
    verdaccio user
"`; diff --git a/src/components/Author/styles.ts b/src/components/Author/styles.ts index 40a8cae..de8b118 100644 --- a/src/components/Author/styles.ts +++ b/src/components/Author/styles.ts @@ -7,7 +7,6 @@ import { Theme } from '../../design-tokens/theme'; export const StyledText = styled(Text)<{ theme?: Theme }>(props => ({ fontWeight: props.theme && props.theme.fontWeight.bold, - textTransform: 'capitalize', })); export const AuthorListItem = styled(ListItem)({ diff --git a/src/components/AutoComplete/AutoComplete.tsx b/src/components/AutoComplete/AutoComplete.tsx index 977cc91..5414e8c 100644 --- a/src/components/AutoComplete/AutoComplete.tsx +++ b/src/components/AutoComplete/AutoComplete.tsx @@ -3,6 +3,7 @@ import styled from '@emotion/styled'; import Autosuggest, { SuggestionSelectedEventData, InputProps, ChangeEvent } from 'react-autosuggest'; import match from 'autosuggest-highlight/match'; import parse from 'autosuggest-highlight/parse'; +import { useTranslation } from 'react-i18next'; import MenuItem from '../../muiComponents/MenuItem'; import { Theme } from '../../design-tokens/theme'; @@ -83,12 +84,6 @@ const renderMessage = (message): JSX.Element => { ); }; -const SUGGESTIONS_RESPONSE = { - LOADING: 'Loading...', - FAILURE: 'Something went wrong.', - NO_RESULT: 'No results found.', -}; - const AutoComplete = memo( ({ suggestions, @@ -106,6 +101,8 @@ const AutoComplete = memo( suggestionsLoaded = false, suggestionsError = false, }: Props) => { + const { t } = useTranslation(); + const autosuggestProps = { renderInputComponent, suggestions, @@ -130,9 +127,9 @@ const AutoComplete = memo( function renderSuggestionsContainer({ containerProps, children, query }): JSX.Element { return ( - {suggestionsLoaded && children === null && query && renderMessage(SUGGESTIONS_RESPONSE.NO_RESULT)} - {suggestionsLoading && query && renderMessage(SUGGESTIONS_RESPONSE.LOADING)} - {suggestionsError && renderMessage(SUGGESTIONS_RESPONSE.FAILURE)} + {suggestionsLoaded && children === null && query && renderMessage(t('auto-complete.no-results-found'))} + {suggestionsLoading && query && renderMessage(t('auto-complete.loading'))} + {suggestionsError && renderMessage(t('error.unspecific'))} {children} ); diff --git a/src/components/CopyToClipBoard/CopyToClipBoard.tsx b/src/components/CopyToClipBoard/CopyToClipBoard.tsx index cf8b95a..100dcfd 100644 --- a/src/components/CopyToClipBoard/CopyToClipBoard.tsx +++ b/src/components/CopyToClipBoard/CopyToClipBoard.tsx @@ -1,8 +1,8 @@ import FileCopy from '@material-ui/icons/FileCopy'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { copyToClipBoardUtility } from '../../utils/cli-utils'; -import { TEXT } from '../../utils/constants'; import Tooltip from '../../muiComponents/Tooltip'; import { ClipBoardCopy, ClipBoardCopyText, CopyIcon } from './styles'; @@ -20,19 +20,16 @@ const renderText = (text: string, children: React.ReactNode): JSX.Element => { return {text}; }; -const renderToolTipFileCopy = (text: string): React.ReactElement => ( - - - - - -); - const CopyToClipBoard: React.FC = ({ text, children }) => { + const { t } = useTranslation(); return ( {renderText(text, children)} - {renderToolTipFileCopy(text)} + + + + + ); }; diff --git a/src/components/CopyToClipBoard/__snapshots__/CopyToClipBoard.test.tsx.snap b/src/components/CopyToClipBoard/__snapshots__/CopyToClipBoard.test.tsx.snap index bbcb88c..331c216 100644 --- a/src/components/CopyToClipBoard/__snapshots__/CopyToClipBoard.test.tsx.snap +++ b/src/components/CopyToClipBoard/__snapshots__/CopyToClipBoard.test.tsx.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` component should load the component in default state 1`] = `"
copy text
"`; +exports[` component should load the component in default state 1`] = `"
copy text
"`; diff --git a/src/components/Dependencies/Dependencies.tsx b/src/components/Dependencies/Dependencies.tsx index 7e3481b..798b5c3 100644 --- a/src/components/Dependencies/Dependencies.tsx +++ b/src/components/Dependencies/Dependencies.tsx @@ -1,5 +1,6 @@ import React, { useContext } from 'react'; import { useHistory } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; import CardContent from '../../muiComponents/CardContent'; import { PackageDependencies } from '../../../types/packageMeta'; @@ -16,6 +17,7 @@ interface DependencyBlockProps { const DependencyBlock: React.FC = ({ title, dependencies }) => { const { enableLoading } = useContext(DetailContext); const history = useHistory(); + const { t } = useTranslation(); const deps = Object.entries(dependencies); @@ -31,8 +33,14 @@ const DependencyBlock: React.FC = ({ title, dependencies } {`${title} (${deps.length})`} {deps.map(([name, version]) => ( - // eslint-disable-next-line - handleClick(name)} /> + handleClick(name)} + /> ))} @@ -46,9 +54,10 @@ function hasKeys(object?: { [key: string]: any }): boolean { const Dependencies: React.FC<{}> = () => { const { packageMeta } = useContext(DetailContext); + const { t } = useTranslation(); if (!packageMeta) { - throw new Error('packageMeta is required at DetailContext'); + throw new Error(t('error.package-meta-is-required-at-detail-context')); } const { latest } = packageMeta; @@ -72,7 +81,7 @@ const Dependencies: React.FC<{}> = () => { ); } - return ; + return ; }; export default Dependencies; diff --git a/src/components/DetailContainer/DetailContainer.tsx b/src/components/DetailContainer/DetailContainer.tsx index fe586e0..0faff5d 100644 --- a/src/components/DetailContainer/DetailContainer.tsx +++ b/src/components/DetailContainer/DetailContainer.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState, ChangeEvent, useContext } from 'react'; +import React, { useState, useContext } from 'react'; import { DetailContext } from '../../pages/Version'; import Box from '../../muiComponents/Box'; @@ -8,24 +8,19 @@ import DetailContainerContent from './DetailContainerContent'; import { TabPosition } from './tabs'; const DetailContainer: React.FC = () => { - const [tabPosition, setTabPosition] = useState(TabPosition.README); + const tabs = Object.values(TabPosition); + const [tabPosition, setTabPosition] = useState(0); const detailContext = useContext(DetailContext); const { readMe } = detailContext; - const handleChangeTabPosition = useCallback( - (event: ChangeEvent<{}>) => { - event.preventDefault(); - const eventTarget = event.target as HTMLSpanElement; - const chosentab = eventTarget.innerText as TabPosition; - setTabPosition(TabPosition[chosentab]); - }, - [setTabPosition] - ); + const handleChange = (event, newValue) => { + setTabPosition(newValue); + }; return ( - - + + ); }; diff --git a/src/components/DetailContainer/DetailContainerTabs.tsx b/src/components/DetailContainer/DetailContainerTabs.tsx index fdaa009..097136c 100644 --- a/src/components/DetailContainer/DetailContainerTabs.tsx +++ b/src/components/DetailContainer/DetailContainerTabs.tsx @@ -1,44 +1,35 @@ -import React, { ChangeEvent, useState, useEffect } from 'react'; +import React from 'react'; import styled from '@emotion/styled'; +import { useTranslation } from 'react-i18next'; import { default as MuiTabs } from '../../muiComponents/Tabs'; import Tab from '../../muiComponents/Tab'; -import { TabPosition } from './tabs'; - interface Props { - tabPosition: TabPosition; - onChangeTabPosition: (event: ChangeEvent<{}>) => void; + onChange: (event, newValue) => void; + tabPosition: number; } -const Tabs = styled(MuiTabs)({ - marginBottom: 16, -}); - -const getTabIndex = (tabPosition: TabPosition): number => - Object.keys(TabPosition).findIndex(position => position === String(tabPosition).toUpperCase()); - -const DetailContainerTabs: React.FC = ({ tabPosition, onChangeTabPosition }) => { - const [tabPositionIndex, setTabPositionIndex] = useState(0); - - useEffect(() => { - const tabIndex = getTabIndex(tabPosition); - setTabPositionIndex(tabIndex); - }, [tabPosition]); +const DetailContainerTabs: React.FC = ({ tabPosition, onChange }) => { + const { t } = useTranslation(); return ( - - - - + + + + ); }; export default DetailContainerTabs; + +const Tabs = styled(MuiTabs)({ + marginBottom: 16, +}); diff --git a/src/components/DetailContainer/tabs.ts b/src/components/DetailContainer/tabs.ts index 26470f8..565f1c6 100644 --- a/src/components/DetailContainer/tabs.ts +++ b/src/components/DetailContainer/tabs.ts @@ -1,6 +1,6 @@ export enum TabPosition { - README = 'Readme', - DEPENDENCIES = 'Dependencies', - VERSIONS = 'Versions', - UPLINKS = 'Uplinks', + README = 'readme', + DEPENDENCIES = 'dependencies', + VERSIONS = 'versions', + UPLINKS = 'uplinks', } diff --git a/src/components/DetailSidebar/DetailSidebarFundButton.tsx b/src/components/DetailSidebar/DetailSidebarFundButton.tsx index 9e343aa..fb1e87f 100644 --- a/src/components/DetailSidebar/DetailSidebarFundButton.tsx +++ b/src/components/DetailSidebar/DetailSidebarFundButton.tsx @@ -1,6 +1,7 @@ import React, { useContext } from 'react'; import styled from '@emotion/styled'; import Favorite from '@material-ui/icons/Favorite'; +import { Trans } from 'react-i18next'; import Button from '../../muiComponents/Button'; import Link from '../Link'; @@ -38,8 +39,7 @@ const DetailSidebarFundButton: React.FC = () => { return ( ); diff --git a/src/components/DetailSidebar/DetailSidebarTitle.tsx b/src/components/DetailSidebar/DetailSidebarTitle.tsx index dd5a816..f04569a 100644 --- a/src/components/DetailSidebar/DetailSidebarTitle.tsx +++ b/src/components/DetailSidebar/DetailSidebarTitle.tsx @@ -1,5 +1,6 @@ import React from 'react'; import styled from '@emotion/styled'; +import { useTranslation } from 'react-i18next'; import Box from '../../muiComponents/Box'; import Heading from '../../muiComponents/Heading'; @@ -21,12 +22,17 @@ const StyledBoxVersion = styled(Box)<{ theme?: Theme }>(({ theme }) => ({ color: theme && theme.palette.text.secondary, })); -const DetailSidebarTitle: React.FC = ({ description, packageName, version, isLatest }) => ( - - {packageName} - {description &&
{description}
} - {`${isLatest ? 'Latest v' : 'v'}${version}`} -
-); +const DetailSidebarTitle: React.FC = ({ description, packageName, version, isLatest }) => { + const { t } = useTranslation(); + return ( + + {packageName} + {description &&
{description}
} + + {isLatest ? t('sidebar.detail.latest-version', { version }) : t('sidebar.detail.version', { version })} + +
+ ); +}; export default DetailSidebarTitle; diff --git a/src/components/Developers/Developers.test.tsx b/src/components/Developers/Developers.test.tsx index 8913ebc..509ce55 100644 --- a/src/components/Developers/Developers.test.tsx +++ b/src/components/Developers/Developers.test.tsx @@ -3,7 +3,8 @@ import React from 'react'; import { mount } from '../../utils/test-enzyme'; import { DetailContextProvider } from '../../pages/Version'; -import Developers, { DeveloperType, Fab } from './Developers'; +import Developers, { Fab } from './Developers'; +import { DeveloperType } from './types'; describe('test Developers', () => { const packageMeta = { diff --git a/src/components/Developers/Developers.tsx b/src/components/Developers/Developers.tsx index 3e69462..b09899a 100644 --- a/src/components/Developers/Developers.tsx +++ b/src/components/Developers/Developers.tsx @@ -1,38 +1,29 @@ import React, { useState, useCallback, useContext, useEffect, useMemo } from 'react'; import Add from '@material-ui/icons/Add'; import styled from '@emotion/styled'; +import { useTranslation } from 'react-i18next'; import { DetailContext } from '../../pages/Version'; import Tooltip from '../../muiComponents/Tooltip'; import Avatar from '../../muiComponents/Avatar'; import Box from '../../muiComponents/Box'; -import Text from '../../muiComponents/Text'; import FloatingActionButton from '../../muiComponents/FloatingActionButton'; import { Theme } from '../../design-tokens/theme'; import getUniqueDeveloperValues from './get-unique-developer-values'; +import DevelopersTitle from './DevelopersTitle'; +import { DeveloperType } from './types'; export const Fab = styled(FloatingActionButton)<{ theme?: Theme }>(props => ({ backgroundColor: props.theme && props.theme.palette.primary.main, color: props.theme && props.theme.palette.white, })); -export enum DeveloperType { - CONTRIBUTORS = 'contributors', - MAINTAINERS = 'maintainers', -} - interface Props { type: DeveloperType; visibleMax?: number; } -export const StyledText = styled(Text)<{ theme?: Theme }>(({ theme }) => ({ - fontWeight: theme && theme.fontWeight.bold, - marginBottom: '10px', - textTransform: 'capitalize', -})); - const StyledBox = styled(Box)({ '> *': { margin: 5, @@ -43,9 +34,10 @@ export const VISIBLE_MAX = 6; const Developers: React.FC = ({ type, visibleMax = VISIBLE_MAX }) => { const detailContext = useContext(DetailContext); + const { t } = useTranslation(); if (!detailContext) { - throw Error("The app's detail Context was not correct used"); + throw Error(t('app-context-not-correct-used')); } const developers = useMemo(() => getUniqueDeveloperValues(detailContext.packageMeta?.latest[type]), [ @@ -69,7 +61,7 @@ const Developers: React.FC = ({ type, visibleMax = VISIBLE_MAX }) => { return ( <> - {type} + {visibleDevelopers.map(visibleDeveloper => ( diff --git a/src/components/Developers/DevelopersTitle.tsx b/src/components/Developers/DevelopersTitle.tsx new file mode 100644 index 0000000..0eb9c39 --- /dev/null +++ b/src/components/Developers/DevelopersTitle.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import styled from '@emotion/styled'; +import { useTranslation } from 'react-i18next'; + +import Text from '../../muiComponents/Text'; +import { Theme } from '../../design-tokens/theme'; + +import { DeveloperType } from './types'; + +interface Props { + type: DeveloperType; +} + +const DevelopersTitle: React.FC = ({ type }) => { + const { t } = useTranslation(); + switch (type) { + case DeveloperType.CONTRIBUTORS: + return {t('sidebar.contributors.title')}; + case DeveloperType.MAINTAINERS: + return {t('sidebar.maintainers.title')}; + return null; + } +}; + +export default DevelopersTitle; + +const StyledText = styled(Text)<{ theme?: Theme }>(({ theme }) => ({ + fontWeight: theme && theme.fontWeight.bold, + marginBottom: '10px', +})); diff --git a/src/components/Developers/__snapshots__/Developers.test.tsx.snap b/src/components/Developers/__snapshots__/Developers.test.tsx.snap index 94e907d..88f7976 100644 --- a/src/components/Developers/__snapshots__/Developers.test.tsx.snap +++ b/src/components/Developers/__snapshots__/Developers.test.tsx.snap @@ -4,7 +4,6 @@ exports[`test Developers should render the component for contributors with items .emotion-0 { font-weight: 700; margin-bottom: 10px; - text-transform: capitalize; } .emotion-8 > * { @@ -14,64 +13,68 @@ exports[`test Developers should render the component for contributors with items - - - - -
- contributors -
-
-
-
-
+
+ sidebar.contributors.title +
+ + + + +
* { @@ -437,64 +439,68 @@ exports[`test Developers should render the component for maintainers with items - - - - -
- maintainers -
-
-
-
-
+
+ sidebar.maintainers.title +
+ + + + + = ({ name, children }) => const Dist: FC = () => { const { packageMeta } = useContext(DetailContext); + const { t } = useTranslation(); if (!packageMeta) { return null; @@ -30,11 +32,11 @@ const Dist: FC = () => { const { dist, license } = packageMeta && packageMeta.latest; return ( - {'Latest Distribution'}}> + {t('sidebar.distribution.title')}}> - {dist.fileCount} - {dist.unpackedSize && fileSizeSI(dist.unpackedSize)} - {formatLicense(license)} + {dist.fileCount} + {dist.unpackedSize && fileSizeSI(dist.unpackedSize)} + {formatLicense(license)} ); diff --git a/src/components/Dist/__snapshots__/Dist.test.tsx.snap b/src/components/Dist/__snapshots__/Dist.test.tsx.snap index d2b3ab2..8792608 100644 --- a/src/components/Dist/__snapshots__/Dist.test.tsx.snap +++ b/src/components/Dist/__snapshots__/Dist.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` component should render the component in default state 1`] = `"
    Latest Distribution
    file count: 7
    size: 10.00 Bytes
"`; +exports[` component should render the component in default state 1`] = `"
    sidebar.distribution.title
    sidebar.distribution.file-count: 7
    sidebar.distribution.size: 10.00 Bytes
"`; -exports[` component should render the component with license as object 1`] = `"
    Latest Distribution
    file count: 7
    size: 10.00 Bytes
    license: MIT
"`; +exports[` component should render the component with license as object 1`] = `"
    sidebar.distribution.title
    sidebar.distribution.file-count: 7
    sidebar.distribution.size: 10.00 Bytes
    sidebar.distribution.license: MIT
"`; -exports[` component should render the component with license as string 1`] = `"
    Latest Distribution
    file count: 7
    size: 10.00 Bytes
    license: MIT
"`; +exports[` component should render the component with license as string 1`] = `"
    sidebar.distribution.title
    sidebar.distribution.file-count: 7
    sidebar.distribution.size: 10.00 Bytes
    sidebar.distribution.license: MIT
"`; diff --git a/src/components/Engines/Engines.tsx b/src/components/Engines/Engines.tsx index 5e8ea9a..b6d0ab7 100644 --- a/src/components/Engines/Engines.tsx +++ b/src/components/Engines/Engines.tsx @@ -1,4 +1,5 @@ import React, { useContext } from 'react'; +import { useTranslation } from 'react-i18next'; import { DetailContext } from '../../pages/Version'; import Avatar from '../../muiComponents/Avatar'; @@ -12,6 +13,7 @@ import node from './img/node.png'; const Engine: React.FC = () => { const { packageMeta } = useContext(DetailContext); + const { t } = useTranslation(); const engines = packageMeta?.latest?.engines; @@ -23,7 +25,7 @@ const Engine: React.FC = () => { {engines.node && ( - {'node JS'}}> + {t('sidebar.engines.node-js')}}> @@ -34,7 +36,7 @@ const Engine: React.FC = () => { {engines.npm && ( - {'NPM version'}}> + {t('sidebar.engines.npm-version')}}> diff --git a/src/components/Engines/__snapshots__/Engines.test.tsx.snap b/src/components/Engines/__snapshots__/Engines.test.tsx.snap index 28ee25d..a496c67 100644 --- a/src/components/Engines/__snapshots__/Engines.test.tsx.snap +++ b/src/components/Engines/__snapshots__/Engines.test.tsx.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` component should render the component in default state 1`] = `"
    node JS
    >= 0.1.98
    NPM version
    >3
"`; +exports[` component should render the component in default state 1`] = `"
    sidebar.engines.node-js
    >= 0.1.98
    sidebar.engines.npm-version
    >3
"`; diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx index da3b58e..e53ac21 100644 --- a/src/components/Footer/Footer.tsx +++ b/src/components/Footer/Footer.tsx @@ -1,53 +1,38 @@ import React from 'react'; +import { useTranslation, Trans } from 'react-i18next'; import { goToVerdaccioWebsite } from '../../utils/windows'; import { Wrapper, Left, Right, Earth, Flags, Love, Flag, Logo, Inner, ToolTip } from './styles'; -const renderTooltip = (): JSX.Element => ( - - - - - - - - - - - -); -const POWERED_LABEL = 'Powered by'; -const MADEWITH_LABEL = ' Made with'; -const ON_LABEL = 'on'; -const HEARTH_EMOJI = '♥'; - -const renderRight = (version = window.VERDACCIO_VERSION): JSX.Element => { +/* eslint-disable react/jsx-key */ +const Footer: React.FC = () => { + const { t } = useTranslation(); return ( - - {POWERED_LABEL} - - {`/ ${version}`} - + + + + ]} i18nKey="footer.made-with-love-on" /> + + + + + + + + + + + + + + {t('footer.powered-by')} + + {`/ ${window.VERDACCIO_VERSION}`} + + + ); }; -const renderLeft = (): JSX.Element => ( - - {MADEWITH_LABEL} - {HEARTH_EMOJI} - {ON_LABEL} - {renderTooltip()} - -); - -const Footer: React.FC = () => ( - - - {renderLeft()} - {renderRight()} - - -); - export default Footer; diff --git a/src/components/Footer/__snapshots__/Footer.test.tsx.snap b/src/components/Footer/__snapshots__/Footer.test.tsx.snap index 4583d1a..101bbcd 100644 --- a/src/components/Footer/__snapshots__/Footer.test.tsx.snap +++ b/src/components/Footer/__snapshots__/Footer.test.tsx.snap @@ -167,13 +167,13 @@ exports[`