From beafd507b8519d45a0c1b2cadba65530e9a7c656 Mon Sep 17 00:00:00 2001 From: Loren Johnson Date: Fri, 21 Oct 2022 11:55:50 -0700 Subject: [PATCH 01/68] React.lazy loads HyloEditorMobile and moves it above our routing setup allowing it to code-split with minimum dependencies --- src/index.js | 19 ++++++++++++++++--- src/router/index.js | 7 +------ src/routes/HyloAppRouter/HyloAppRouter.js | 16 ---------------- src/routes/HyloAppRouter/index.js | 3 --- 4 files changed, 17 insertions(+), 28 deletions(-) delete mode 100644 src/routes/HyloAppRouter/HyloAppRouter.js delete mode 100644 src/routes/HyloAppRouter/index.js diff --git a/src/index.js b/src/index.js index c2a7231dd..8daa7db79 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,24 @@ -import React from 'react' +import React, { Suspense } from 'react' import ReactDOM from 'react-dom' import { rootDomId } from 'client/util' -import App from './router' +import Loading from 'components/Loading' + import './client/websockets' import './css/global/index.scss' +const App = React.lazy(() => import('./router')) +const HyloEditorMobile = React.lazy(() => import('components/HyloEditor/HyloEditorMobile')) + ReactDOM.render( - , + window.location.pathname === '/hyloApp/editor' + ? ( + null}> + + + ) : ( + }> + + + ), document.getElementById(rootDomId) ) diff --git a/src/router/index.js b/src/router/index.js index 8d6aff4cd..4cead40b3 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,13 +1,11 @@ import React from 'react' import { ConnectedRouter } from 'connected-react-router' -import { Switch, Route } from 'react-router' import { DndProvider } from 'react-dnd' import { HTML5Backend } from 'react-dnd-html5-backend' import { Provider } from 'react-redux' import { LayoutFlagsProvider } from 'contexts/LayoutFlagsContext' import createStore, { history } from '../store' import RootRouter from 'routes/RootRouter' -import HyloAppRouter from 'routes/HyloAppRouter' const store = createStore() @@ -19,10 +17,7 @@ export default function App () { - - - - + diff --git a/src/routes/HyloAppRouter/HyloAppRouter.js b/src/routes/HyloAppRouter/HyloAppRouter.js deleted file mode 100644 index 737a6d379..000000000 --- a/src/routes/HyloAppRouter/HyloAppRouter.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react' -import { Route, Switch } from 'react-router-dom' -import HyloEditorMobile from 'components/HyloEditor/HyloEditorMobile' - -export default function HyloAppRouter () { - return ( - - ( - - )} - /> - - ) -} diff --git a/src/routes/HyloAppRouter/index.js b/src/routes/HyloAppRouter/index.js deleted file mode 100644 index 3e451ab23..000000000 --- a/src/routes/HyloAppRouter/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import component from './HyloAppRouter' - -export default component From 3596b85091dd587a7e67de55fd5c274aadb4341b Mon Sep 17 00:00:00 2001 From: Loren Johnson Date: Mon, 31 Oct 2022 09:02:22 -0700 Subject: [PATCH 02/68] Removes HyloEditor dependency on store for Mobile use --- src/components/HyloEditor/HyloEditor.js | 6 ++-- .../HyloEditor/extensions/PeopleMentions.js | 10 +++--- .../HyloEditor/extensions/TopicMentions.js | 12 ++++--- src/util/graphql.js | 36 +++++++++++++++++++ 4 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/components/HyloEditor/HyloEditor.js b/src/components/HyloEditor/HyloEditor.js index a78554291..723405644 100644 --- a/src/components/HyloEditor/HyloEditor.js +++ b/src/components/HyloEditor/HyloEditor.js @@ -1,5 +1,4 @@ import React, { useRef, useImperativeHandle, useEffect, useState } from 'react' -import { useDispatch } from 'react-redux' import { useEditor, EditorContent, Extension, BubbleMenu } from '@tiptap/react' import Highlight from '@tiptap/extension-highlight' import Placeholder from '@tiptap/extension-placeholder' @@ -31,7 +30,6 @@ export const HyloEditor = React.forwardRef(function HyloEditor ({ showMenu = false, suggestionsThemeName = 'suggestions' }, ref) { - const dispatch = useDispatch() const editorRef = useRef(null) const [selectedLink, setSelectedLink] = useState() const editor = useEditor({ @@ -113,9 +111,9 @@ export const HyloEditor = React.forwardRef(function HyloEditor ({ } }), - PeopleMentions({ onSelection: onAddMention, maxSuggestions, groupIds, suggestionsThemeName, dispatch }), + PeopleMentions({ onSelection: onAddMention, maxSuggestions, groupIds, suggestionsThemeName }), - TopicMentions({ onSelection: onAddTopic, maxSuggestions, groupIds, suggestionsThemeName, dispatch }), + TopicMentions({ onSelection: onAddTopic, maxSuggestions, groupIds, suggestionsThemeName }), Highlight ], diff --git a/src/components/HyloEditor/extensions/PeopleMentions.js b/src/components/HyloEditor/extensions/PeopleMentions.js index be564321e..dd73be641 100644 --- a/src/components/HyloEditor/extensions/PeopleMentions.js +++ b/src/components/HyloEditor/extensions/PeopleMentions.js @@ -1,10 +1,11 @@ import Mention from '@tiptap/extension-mention' import { PluginKey } from 'prosemirror-state' +import { queryHyloAPI } from 'util/graphql' import asyncDebounce from 'util/asyncDebounce' import suggestions from './suggestions' import findMentions from 'store/actions/findMentions' -export const PeopleMentions = ({ dispatch, groupIds, maxSuggestions, onSelection, suggestionsThemeName }) => +export const PeopleMentions = ({ groupIds, maxSuggestions, onSelection, suggestionsThemeName }) => // Mentions (https://github.com/ueberdosis/tiptap/issues/2219#issuecomment-984662243) Mention .extend({ @@ -44,15 +45,16 @@ export const PeopleMentions = ({ dispatch, groupIds, maxSuggestions, onSelection items: asyncDebounce(200, async ({ query, editor }) => { editor.extensionStorage.topic.loading = true - const matchedPeople = await dispatch(findMentions({ + const findMentionsGraphql = findMentions({ autocomplete: query, groupIds: editor.extensionStorage.mention.groupIds, maxItems: maxSuggestions - })) + }).graphql + const matchedPeople = await queryHyloAPI(findMentionsGraphql) editor.extensionStorage.topic.loading = false - return matchedPeople?.payload.getData().items + return matchedPeople?.data.people.items .map(person => ({ id: person.id, label: person.name, diff --git a/src/components/HyloEditor/extensions/TopicMentions.js b/src/components/HyloEditor/extensions/TopicMentions.js index ff29deff5..e618fe9f3 100644 --- a/src/components/HyloEditor/extensions/TopicMentions.js +++ b/src/components/HyloEditor/extensions/TopicMentions.js @@ -1,11 +1,12 @@ import Mention from '@tiptap/extension-mention' import { PluginKey } from 'prosemirror-state' import { uniqBy } from 'lodash/fp' +import { queryHyloAPI } from 'util/graphql' import asyncDebounce from 'util/asyncDebounce' import suggestions from './suggestions' import findTopics from 'store/actions/findTopics' -export const TopicMentions = ({ dispatch, groupIds, maxSuggestions, onSelection, suggestionsThemeName }) => +export const TopicMentions = ({ groupIds, maxSuggestions, onSelection, suggestionsThemeName }) => Mention .extend({ name: 'topic', @@ -44,15 +45,16 @@ export const TopicMentions = ({ dispatch, groupIds, maxSuggestions, onSelection, // Can be fixed if it is a bad UX. editor.extensionStorage.topic.loading = true - // TODO: Integrate `getTopicsBySearchTerm` selector to reduce queries and speed results - const matchedTopics = await dispatch(findTopics({ + const findTopicsGraphql = findTopics({ autocomplete: query, + groupIds: editor.extensionStorage.topic.groupIds, maxItems: maxSuggestions - })) + }).graphql + const matchedTopics = await queryHyloAPI(findTopicsGraphql) editor.extensionStorage.topic.loading = false - const results = matchedTopics?.payload.getData().items + const results = matchedTopics?.data.groupTopics.items .map(t => ({ id: t.topic.name, label: `#${t.topic.name}`, diff --git a/src/util/graphql.js b/src/util/graphql.js index daa04dd91..2d1ed5a78 100644 --- a/src/util/graphql.js +++ b/src/util/graphql.js @@ -1,6 +1,9 @@ +import fetch from 'isomorphic-fetch' import gql from 'graphql-tag' import { print } from 'graphql/language/printer' +export const HYLO_GRAPHQL_ENDPOINT_PATH = '/noo/graphql' + export function stringToGraphql (graphqlString) { return (typeof graphqlString === 'string' || graphqlString instanceof String) ? gql(graphqlString) @@ -13,3 +16,36 @@ export function graphqlToString (unknownGraphql) { ? print(unknownGraphql) : unknownGraphql } + +export function getHyloAPIEndpointURL () { + return typeof window === 'undefined' + ? `${process.env.API_HOST}${HYLO_GRAPHQL_ENDPOINT_PATH}` + : `${window.location.origin}${HYLO_GRAPHQL_ENDPOINT_PATH}` +} + +// For directly querying our API outside of the Redux store. +// Currently only used in the WebView HyloEditor +export async function queryHyloAPI ({ query: unknownGraphql, variables }) { + const params = { query: graphqlToString(unknownGraphql), variables } + const response = await fetch(getHyloAPIEndpointURL(), { + body: JSON.stringify(params), + credentials: 'same-origin', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + method: 'POST' + }) + + if (response.status === 200) { + return response.json() + } else { + const { status, statusText, url } = response + const body = await response.text() + const error = new Error(body) + + error.response = { status, statusText, url, body } + + throw error + } +} From 2b86996de926451a8a3ad638b5a8ccf3e0fcc899 Mon Sep 17 00:00:00 2001 From: Loren Johnson Date: Mon, 31 Oct 2022 09:24:46 -0700 Subject: [PATCH 03/68] Adds an isolated Feature / ReactPlayer route for HyloApp WebView use --- src/index.js | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/index.js b/src/index.js index 8daa7db79..a27695dc0 100644 --- a/src/index.js +++ b/src/index.js @@ -2,23 +2,41 @@ import React, { Suspense } from 'react' import ReactDOM from 'react-dom' import { rootDomId } from 'client/util' import Loading from 'components/Loading' - import './client/websockets' import './css/global/index.scss' const App = React.lazy(() => import('./router')) const HyloEditorMobile = React.lazy(() => import('components/HyloEditor/HyloEditorMobile')) +const Feature = React.lazy(() => import('components/PostCard/Feature')) + +const renderRoot = () => { + switch (window.location.pathname) { + case '/hyloApp/editor': { + return ( + null}> + + + ) + } + + case '/hyloApp/videoPlayer': { + const querystringParams = new URLSearchParams(window.location.search) + + return ( + null}> + + + ) + } + + default: { + return ( + }> + + + ) + } + } +} -ReactDOM.render( - window.location.pathname === '/hyloApp/editor' - ? ( - null}> - - - ) : ( - }> - - - ), - document.getElementById(rootDomId) -) +ReactDOM.render(renderRoot(), document.getElementById(rootDomId)) From 6d790a3d6866b8e86d005a4772efadfcb919acc4 Mon Sep 17 00:00:00 2001 From: Sofia Acosta Date: Mon, 5 Dec 2022 22:32:51 -0600 Subject: [PATCH 04/68] fix code style complaints, make english the default language --- babel.config.js | 1 + package.json | 6 +- public/locales/en/translation.json | 5 + public/locales/es/translation.json | 5 + src/components/Affiliation/Affiliation.js | 6 +- src/components/NoPosts/index.js | 21 ++- src/i18n.js | 27 +++ src/index.js | 9 +- yarn.lock | 218 +++++++++++++++++++++- 9 files changed, 279 insertions(+), 19 deletions(-) create mode 100644 public/locales/en/translation.json create mode 100644 public/locales/es/translation.json create mode 100644 src/i18n.js diff --git a/babel.config.js b/babel.config.js index cb388ae31..04caaf9fb 100644 --- a/babel.config.js +++ b/babel.config.js @@ -39,6 +39,7 @@ module.exports = function (api) { extensions: ['.graphql'] } ], + ['i18next-extract', { outputPath: 'public/locales/{{locale}}/{{ns}}.json' }], 'import-graphql', 'inline-import', 'lodash' diff --git a/package.json b/package.json index c9d9477a2..20b074c9f 100644 --- a/package.json +++ b/package.json @@ -151,6 +151,8 @@ "http-proxy-middleware": "0.17.4", "husky": "^7.0.4", "hylo-shared": "5.1.2", + "i18next": "^22.0.5", + "i18next-http-backend": "^2.0.1", "identity-obj-proxy": "3.0.0", "immutability-helper": "^3.1.1", "immutable": "~3.7.4", @@ -209,6 +211,7 @@ "react-dnd-html5-backend": "^15.1.2", "react-dom": "^16.8.6", "react-flip-move": "^3.0.4", + "react-i18next": "^12.0.0", "react-icons": "^4.4.0", "react-joyride": "^2.3.2", "react-map-gl": "^6", @@ -274,10 +277,11 @@ "xhr": "^2.5.0" }, "devDependencies": { + "babel-plugin-i18next-extract": "^0.9.0", "standard": "12.0.1" }, "resolutions": { "@emotion/utils": "^1.1.0", "@emotion/sheet": "^1.1.0" } -} +} \ No newline at end of file diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json new file mode 100644 index 000000000..0c585c55a --- /dev/null +++ b/public/locales/en/translation.json @@ -0,0 +1,5 @@ +{ + "Are you sure you want to delete your affiliation as {{role}} {{preposition}} {{orgName}}?": "Are you sure you want to delete your affiliation as {{role}} {{preposition}} {{orgName}}?", + "Delete": "Delete", + "Nothing to see here": "Nothing to see here" +} \ No newline at end of file diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json new file mode 100644 index 000000000..646e9f731 --- /dev/null +++ b/public/locales/es/translation.json @@ -0,0 +1,5 @@ +{ + "Are you sure you want to delete your affiliation as {{role}} {{preposition}} {{orgName}}?": "", + "Delete": "", + "Nothing to see here": "" +} \ No newline at end of file diff --git a/src/components/Affiliation/Affiliation.js b/src/components/Affiliation/Affiliation.js index 99c3b9323..816a83fc5 100644 --- a/src/components/Affiliation/Affiliation.js +++ b/src/components/Affiliation/Affiliation.js @@ -1,11 +1,13 @@ import React from 'react' +import { useTranslation } from 'react-i18next' import './Affiliation.scss' export default function Affiliation ({ affiliation, index, archive }) { const { role, preposition, orgName, url } = affiliation + const { t } = useTranslation() const leave = () => { - if (window.confirm(`Are you sure you want to delete your affiliation as ${role} ${preposition} ${orgName}?`)) { + if (window.confirm(t('Are you sure you want to delete your affiliation as {{role}} {{preposition}} {{orgName}}?', { orgName, preposition, role }))) { archive(affiliation.id) } } @@ -16,7 +18,7 @@ export default function Affiliation ({ affiliation, index, archive }) {
{preposition}
{url ? ({orgName}) : orgName }
- { archive && Delete } + { archive && {t('Delete')} } ) } diff --git a/src/components/NoPosts/index.js b/src/components/NoPosts/index.js index 533c6ba76..4359ee552 100644 --- a/src/components/NoPosts/index.js +++ b/src/components/NoPosts/index.js @@ -1,12 +1,19 @@ import React from 'react' +import { useTranslation } from 'react-i18next' import './NoPosts.scss' import { jollyAxolotl } from 'util/assets' -export default ({ message = 'Nothing to see here' }) => ( -
- -
-

{message}

-
-) +const NoPosts = () => { + const { t } = useTranslation() + const message = t('Nothing to see here') + return ( +
+ +
+

{message}

+
+ ) +} + +export default NoPosts diff --git a/src/i18n.js b/src/i18n.js new file mode 100644 index 000000000..f6816ed1b --- /dev/null +++ b/src/i18n.js @@ -0,0 +1,27 @@ +import i18n from 'i18next' +import { initReactI18next } from 'react-i18next' + +import Backend from 'i18next-http-backend' +// don't want to use this? +// have a look at the Quick start guide +// for passing in lng and translations on init + +i18n + // load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales) + // learn more: https://github.com/i18next/i18next-http-backend + // want your translations to be loaded from a professional CDN? => https://github.com/locize/react-tutorial#step-2---use-the-locize-cdn + .use(Backend) + // pass the i18n instance to react-i18next. + .use(initReactI18next) + // init i18next + // for all options read: https://www.i18next.com/overview/configuration-options + .init({ + fallbackLng: 'en', + debug: true, + + interpolation: { + escapeValue: false // not needed for react as it escapes by default + } + }) + +export default i18n diff --git a/src/index.js b/src/index.js index c2a7231dd..65c0ec6e2 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,16 @@ -import React from 'react' +import React, { Suspense } from 'react' import ReactDOM from 'react-dom' import { rootDomId } from 'client/util' import App from './router' +import Loading from 'components/Loading' + import './client/websockets' import './css/global/index.scss' +import './i18n' ReactDOM.render( - , + }> + + , document.getElementById(rootDomId) ) diff --git a/yarn.lock b/yarn.lock index 003e016d9..6c8efd89a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -58,6 +58,11 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.3.tgz#707b939793f867f5a73b2666e6d9a3396eb03151" integrity sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw== +"@babel/compat-data@^7.20.0": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.5.tgz#86f172690b093373a933223b4745deeb6049e733" + integrity sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g== + "@babel/core@7.17.5": version "7.17.5" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.5.tgz#6cd2e836058c28f06a4ca8ee7ed955bbf37c8225" @@ -100,6 +105,27 @@ json5 "^2.2.1" semver "^6.3.0" +"@babel/core@^7.18.9": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.5.tgz#45e2114dc6cd4ab167f81daf7820e8fa1250d113" + integrity sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-module-transforms" "^7.20.2" + "@babel/helpers" "^7.20.5" + "@babel/parser" "^7.20.5" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + "@babel/eslint-parser@^7.17.0": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz#4f68f6b0825489e00a24b41b6a1ae35414ecd2f4" @@ -118,6 +144,15 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/generator@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.5.tgz#cb25abee3178adf58d6814b68517c62bdbfdda95" + integrity sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA== + dependencies: + "@babel/types" "^7.20.5" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -143,6 +178,16 @@ browserslist "^4.21.3" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" + integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== + dependencies: + "@babel/compat-data" "^7.20.0" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.21.3" + semver "^6.3.0" + "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.19.0": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz#bfd6904620df4e46470bae4850d66be1054c404b" @@ -231,6 +276,20 @@ "@babel/traverse" "^7.19.0" "@babel/types" "^7.19.0" +"@babel/helper-module-transforms@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" + integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.2" + "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" @@ -271,6 +330,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" @@ -290,6 +356,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" @@ -319,6 +390,15 @@ "@babel/traverse" "^7.19.0" "@babel/types" "^7.19.0" +"@babel/helpers@^7.20.5": + version "7.20.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.6.tgz#e64778046b70e04779dfbdf924e7ebb45992c763" + integrity sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w== + dependencies: + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + "@babel/highlight@^7.0.0", "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" @@ -345,6 +425,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.3.tgz#8dd36d17c53ff347f9e55c328710321b49479a9a" integrity sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ== +"@babel/parser@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8" + integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" @@ -1182,6 +1267,13 @@ core-js-pure "^3.25.1" regenerator-runtime "^0.13.4" +"@babel/runtime@7.15.4": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" + integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" @@ -1189,6 +1281,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.14.5", "@babel/runtime@^7.17.2", "@babel/runtime@^7.20.6": + version "7.20.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.6.tgz#facf4879bfed9b5326326273a64220f099b0fce3" + integrity sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA== + dependencies: + regenerator-runtime "^0.13.11" + "@babel/template@^7.16.7", "@babel/template@^7.18.10", "@babel/template@^7.3.3": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" @@ -1214,6 +1313,31 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.20.1", "@babel/traverse@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.5.tgz#78eb244bea8270fdda1ef9af22a5d5e5b7e57133" + integrity sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.5" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.20.5" + "@babel/types" "^7.20.5" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" + integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== + dependencies: + "@babel/helper-string-parser" "^7.18.10" + "@babel/helper-validator-identifier" "^7.18.6" + to-fast-properties "^2.0.0" + "@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.19.3", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.19.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.3.tgz#fc420e6bbe54880bce6779ffaf315f5e43ec9624" @@ -1223,6 +1347,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.20.2", "@babel/types@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84" + integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -4404,6 +4537,17 @@ babel-plugin-dynamic-import-node@^2.3.3: dependencies: object.assign "^4.1.0" +babel-plugin-i18next-extract@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/babel-plugin-i18next-extract/-/babel-plugin-i18next-extract-0.9.0.tgz#d003985901c6be3d9ce91a70ccd6503121a98c8e" + integrity sha512-i/kcv6YlYFvUR8rICPBxP4xQXenFOGe+84hzYLXItJYrLlimph4lne6hoZXYgaU3mVJmtRokcBmn4RvcitizXA== + dependencies: + "@babel/core" "^7.18.9" + "@babel/types" "7.18.10" + deepmerge "^4.2.2" + i18next "^21.8.14" + json-stable-stringify "^1.0.1" + babel-plugin-import-graphql@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/babel-plugin-import-graphql/-/babel-plugin-import-graphql-2.8.1.tgz#dec677dc47327181d69e8c451aff290460ca2ed6" @@ -5880,6 +6024,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +cross-fetch@3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" + integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== + dependencies: + node-fetch "2.6.7" + cross-spawn@5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -9336,6 +9487,13 @@ html-minifier-terser@^5.0.1: relateurl "^0.2.7" terser "^4.6.3" +html-parse-stringify@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" + integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== + dependencies: + void-elements "3.1.0" + html-tags@1: version "1.2.0" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-1.2.0.tgz#c78de65b5663aa597989dd2b7ab49200d7e4db98" @@ -9512,6 +9670,27 @@ hylo-shared@5.1.2: trunc-text "^1.0.2" validator "^13.7.0" +i18next-http-backend@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/i18next-http-backend/-/i18next-http-backend-2.0.2.tgz#83fa92c12b0b6e90f1969d904b81855d57b1db1c" + integrity sha512-TFiIqitZEc8+jyca31EW5ef5PjUYtUGGfL8c8FJwiiHguq5OQTqoR3mxpKqaCPiikg+cxSgXtNA2gZPCu0aryQ== + dependencies: + cross-fetch "3.1.5" + +i18next@^21.8.14: + version "21.10.0" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.10.0.tgz#85429af55fdca4858345d0e16b584ec29520197d" + integrity sha512-YeuIBmFsGjUfO3qBmMOc0rQaun4mIpGKET5WDwvu8lU7gvwpcariZLNtL0Fzj+zazcHUrlXHiptcFhBMFaxzfg== + dependencies: + "@babel/runtime" "^7.17.2" + +i18next@^22.0.5: + version "22.0.8" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-22.0.8.tgz#0ab088d60cb83495c85422bb2e035a8a1e3756ef" + integrity sha512-cQUVZ3KUHtua4E9/9naB7oH5ymun0jAYKopXFgmSRXog0nrWFajAErz9eJ/A/dSj69YxmA8xOR4m5dY1OrKG5g== + dependencies: + "@babel/runtime" "^7.20.6" + iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -12287,6 +12466,13 @@ node-environment-flags@^1.0.5: object.getownpropertydescriptors "^2.0.3" semver "^5.7.0" +node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + node-fetch@^1.5.3: version "1.7.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" @@ -12295,13 +12481,6 @@ node-fetch@^1.5.3: encoding "^0.1.11" is-stream "^1.0.1" -node-fetch@^2.6.1, node-fetch@^2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - node-forge@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" @@ -14594,6 +14773,14 @@ react-floater@^0.7.6: react-proptype-conditional-require "^1.0.4" tree-changes "^0.9.1" +react-i18next@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-12.0.0.tgz#634015a2c035779c5736ae4c2e5c34c1659753b1" + integrity sha512-/O7N6aIEAl1FaWZBNvhdIo9itvF/MO/nRKr9pYqRc9LhuC1u21SlfwpiYQqvaeNSEW3g3qUXLREOWMt+gxrWbg== + dependencies: + "@babel/runtime" "^7.14.5" + html-parse-stringify "^3.0.1" + react-icons@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.4.0.tgz#a13a8a20c254854e1ec9aecef28a95cdf24ef703" @@ -14997,6 +15184,11 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== +regenerator-runtime@^0.13.11: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + regenerator-runtime@^0.13.4: version "0.13.9" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" @@ -16969,6 +17161,13 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +translation-check@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/translation-check/-/translation-check-1.0.2.tgz#2299cf0a196005bf90ded1d4b6edd792be5c6737" + integrity sha512-Bt5BDrIfa7R/+1x1OMskS3JZ2b1AEkaUdcHc40fj1IltM/EA1uX+1yroQU6IdtSFhIH7HFip7vXaLmuiUclSgg== + dependencies: + "@babel/runtime" "7.15.4" + traverse-chain@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" @@ -17500,6 +17699,11 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +void-elements@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + vt-pbf@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.3.tgz#68fd150756465e2edae1cc5c048e063916dcfaac" From 54ebf6b45d0c6a7768f468ff67dc6f96d8dfe1f6 Mon Sep 17 00:00:00 2001 From: Sofia Acosta Date: Tue, 6 Dec 2022 23:30:51 -0600 Subject: [PATCH 05/68] additional translations, configure translation extractor --- babel.config.js | 2 +- public/locales/en/translation.json | 5 -- public/locales/es/translation.json | 53 +++++++++++++++++-- src/components/Affiliation/Affiliation.js | 2 +- .../Affiliation/Affiliation.test.js | 12 +++++ src/components/CreateGroup/CreateGroup.js | 28 +++++----- .../CreateGroup/CreateGroup.test.js | 8 +++ src/components/CreateModal/CreateModal.js | 8 ++- .../CreateModal/CreateModal.test.js | 12 +++++ .../CreateModal/CreateModalChooser.js | 8 +-- .../EventInviteDialog/EventInviteDialog.js | 19 ++++--- .../EventInviteDialog.test.js | 12 +++++ .../StreamViewControls/StreamViewControls.js | 33 ++++++------ .../FarmOpenToPublic/FarmOpenToPublic.js | 6 ++- .../Widget/JoinWidget/JoinWidget.js | 12 +++-- src/components/Widget/MapWidget/MapWidget.js | 7 ++- .../ModeratorsWidget/ModeratorsWidget.js | 5 +- .../Widget/PrivacyWidget/PrivacyWidget.js | 5 +- .../Widget/ProjectsWidget/ProjectsWidget.js | 10 ++-- src/i18n.js | 5 +- .../AuthLayoutRouter/AuthLayoutRouter.test.js | 12 +++++ 21 files changed, 197 insertions(+), 67 deletions(-) delete mode 100644 public/locales/en/translation.json diff --git a/babel.config.js b/babel.config.js index 04caaf9fb..615b85962 100644 --- a/babel.config.js +++ b/babel.config.js @@ -39,7 +39,7 @@ module.exports = function (api) { extensions: ['.graphql'] } ], - ['i18next-extract', { outputPath: 'public/locales/{{locale}}/{{ns}}.json' }], + ['i18next-extract', { keySeparator: null, locales: ['es'], nsSeparator: null, outputPath: 'public/locales/{{locale}}/{{ns}}.json' }], 'import-graphql', 'inline-import', 'lodash' diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json deleted file mode 100644 index 0c585c55a..000000000 --- a/public/locales/en/translation.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Are you sure you want to delete your affiliation as {{role}} {{preposition}} {{orgName}}?": "Are you sure you want to delete your affiliation as {{role}} {{preposition}} {{orgName}}?", - "Delete": "Delete", - "Nothing to see here": "Nothing to see here" -} \ No newline at end of file diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json index 646e9f731..30fe588d3 100644 --- a/public/locales/es/translation.json +++ b/public/locales/es/translation.json @@ -1,5 +1,52 @@ { - "Are you sure you want to delete your affiliation as {{role}} {{preposition}} {{orgName}}?": "", + "+ New project": "", + "All Posts": "", + "Already Invited": "", + "Are you sure you want to delete your affiliation as {{affiliation.role}} {{affiliation.preposition}} {{affiliation.orgName}}?": "", + "Card view": "", + "Changes won't be saved. Are you sure you want to cancel?": "", + "Community map": "", + "Create Group": "", + "Create a new movement, network, community or group!": "", "Delete": "", - "Nothing to see here": "" -} \ No newline at end of file + "Discussions": "", + "Events": "", + "Group": "", + "IS THIS GROUP A MEMBER OF OTHER GROUPS?": "", + "Invite 1 person": "", + "Invite {{invitedIds.length}} people": "", + "Join to see": "", + "Large Grid": "", + "List view": "", + "Member": "", + "Members": "", + "New Post at this location": "", + "Nothing to see here": "", + "Offers": "", + "Only members of this group can see posts": "", + "Open": "", + "Please enter a URL slug": "", + "Please enter a group name": "", + "Projects": "", + "Projects help you and your group accomplish shared goals.": "", + "Public Offerings": "", + "Recent Posts": "", + "Requests": "", + "Resources": "", + "Search members": "", + "Search posts": "", + "Select people to invite": "", + "Signup or Login to connect with": "", + "Small Grid": "", + "The moderators go here": "", + "There was an error, please try again.": "", + "This URL already exists. Try another.": "", + "URLs must have between 2 and 40 characters, and can only have lower case letters, numbers, and dashes.": "", + "WHO CAN JOIN THIS GROUP?": "", + "WHO CAN SEE THIS GROUP?": "", + "What are you doing together?": "", + "What would you like to create?": "", + "You may add parent groups if you are a moderator of the group you wish to add, or if the group you wish to add has the Open access setting which allows any group to join it": "", + "Your group's name": "", + "group privacy settings": "" +} diff --git a/src/components/Affiliation/Affiliation.js b/src/components/Affiliation/Affiliation.js index 816a83fc5..1eb9521e9 100644 --- a/src/components/Affiliation/Affiliation.js +++ b/src/components/Affiliation/Affiliation.js @@ -7,7 +7,7 @@ export default function Affiliation ({ affiliation, index, archive }) { const { t } = useTranslation() const leave = () => { - if (window.confirm(t('Are you sure you want to delete your affiliation as {{role}} {{preposition}} {{orgName}}?', { orgName, preposition, role }))) { + if (window.confirm(t('Are you sure you want to delete your affiliation as {{affiliation.role}} {{affiliation.preposition}} {{affiliation.orgName}}?', { affiliation }))) { archive(affiliation.id) } } diff --git a/src/components/Affiliation/Affiliation.test.js b/src/components/Affiliation/Affiliation.test.js index 500b1adc8..270af3e53 100644 --- a/src/components/Affiliation/Affiliation.test.js +++ b/src/components/Affiliation/Affiliation.test.js @@ -2,6 +2,18 @@ import React from 'react' import { shallow } from 'enzyme' import Affiliation from './Affiliation' +jest.mock('react-i18next', () => ({ + ...jest.requireActual('react-i18next'), + useTranslation: (domain) => { + return { + t: (str) => str, + i18n: { + changeLanguage: () => new Promise(() => {}) + } + } + } +})) + describe('Affiliation', () => { it('matches last snapshot', () => { const props = { diff --git a/src/components/CreateGroup/CreateGroup.js b/src/components/CreateGroup/CreateGroup.js index 01e44a617..384a3a9c1 100644 --- a/src/components/CreateGroup/CreateGroup.js +++ b/src/components/CreateGroup/CreateGroup.js @@ -1,5 +1,6 @@ import { trim } from 'lodash/fp' import React, { Component } from 'react' +import { withTranslation } from 'react-i18next' import Button from 'components/Button' import Dropdown from 'components/Dropdown' import GroupsSelector from 'components/GroupsSelector' @@ -19,7 +20,7 @@ import styles from './CreateGroup.scss' const slugValidatorRegex = /^[0-9a-z-]{2,40}$/ -export default class CreateGroup extends Component { +class CreateGroup extends Component { constructor (props) { super(props) @@ -47,7 +48,7 @@ export default class CreateGroup extends Component { componentDidUpdate (oldProps) { if (oldProps.groupSlugExists !== this.props.groupSlugExists) { - this.setState({ errors: { ...this.state.errors, slug: this.props.groupSlugExists ? 'This URL already exists. Try another.' : false } }) + this.setState({ errors: { ...this.state.errors, slug: this.props.groupSlugExists ? this.props.t('This URL already exists. Try another.') : false } }) } } @@ -62,9 +63,9 @@ export default class CreateGroup extends Component { validateSlug (val) { if (val === '') { - return 'Please enter a URL slug' + return this.props.t('Please enter a URL slug') } else if (!slugValidatorRegex.test(val)) { - return 'URLs must have between 2 and 40 characters, and can only have lower case letters, numbers, and dashes.' + return this.props.t('URLs must have between 2 and 40 characters, and can only have lower case letters, numbers, and dashes.') } else { this.props.fetchGroupExists(val) return false @@ -81,7 +82,7 @@ export default class CreateGroup extends Component { } if (field === 'name') { - updates.errors.name = newValue === '' ? 'Please enter a group name' : false + updates.errors.name = newValue === '' ? this.props.t('Please enter a group name') : false updates.characterCount = newValue.length } @@ -111,7 +112,7 @@ export default class CreateGroup extends Component { if (error) { // `state.error` doesn't appear to be displayed anywhere this.setState({ - error: 'There was an error, please try again.' + error: this.props.t('There was an error, please try again.') }) } else { this.props.goToGroup(slug) @@ -130,7 +131,7 @@ export default class CreateGroup extends Component {
- Create Group + {this.props.t('Create Group')}
-
WHO CAN SEE THIS GROUP?
+
{this.props.t('WHO CAN SEE THIS GROUP?')}
{visibilityString(visibility)} {visibilityDescription(visibility)} @@ -211,7 +212,7 @@ export default class CreateGroup extends Component {
-
WHO CAN JOIN THIS GROUP?
+
{this.props.t('WHO CAN JOIN THIS GROUP?')}
{accessibilityString(accessibility)} {accessibilityDescription(accessibility)} @@ -252,10 +253,10 @@ export default class CreateGroup extends Component { {parentGroupOptions && parentGroupOptions.length > 0 && (
- IS THIS GROUP A MEMBER OF OTHER GROUPS? + {this.props.t('IS THIS GROUP A MEMBER OF OTHER GROUPS?')}
? -
You may add parent groups if you are a moderator of the group you wish to add, or if the group you wish to add has the Open access setting which allows any group to join it
+
{this.props.t('You may add parent groups if you are a moderator of the group you wish to add, or if the group you wish to add has the Open access setting which allows any group to join it')}
{/* TODO: somehow show groups that are restricted and will be a join request differently */} - Create Group + {this.props.t('Create Group')}
) } } +export default withTranslation()(CreateGroup) diff --git a/src/components/CreateGroup/CreateGroup.test.js b/src/components/CreateGroup/CreateGroup.test.js index 82360ff30..b359f2a96 100644 --- a/src/components/CreateGroup/CreateGroup.test.js +++ b/src/components/CreateGroup/CreateGroup.test.js @@ -2,6 +2,14 @@ import CreateGroup from './CreateGroup' import { shallow } from 'enzyme' import React from 'react' +jest.mock('react-i18next', () => ({ + // this mock makes sure any components using the translate HoC receive the t function as a prop + withTranslation: () => Component => { + Component.defaultProps = { ...Component.defaultProps, t: () => '' } + return Component + } +})) + describe('CreateGroup', () => { it('matches snapshot', () => { const wrapper = shallow() diff --git a/src/components/CreateModal/CreateModal.js b/src/components/CreateModal/CreateModal.js index 048a0bba7..54c699043 100644 --- a/src/components/CreateModal/CreateModal.js +++ b/src/components/CreateModal/CreateModal.js @@ -1,6 +1,7 @@ import React, { useState } from 'react' import { useSelector } from 'react-redux' import { Route, Switch, useHistory, useLocation } from 'react-router-dom' +import { useTranslation } from 'react-i18next' import { CSSTransition } from 'react-transition-group' import getPreviousLocation from 'store/selectors/getPreviousLocation' import CreateModalChooser from './CreateModalChooser' @@ -9,12 +10,13 @@ import Icon from 'components/Icon' import PostEditor from 'components/PostEditor' import './CreateModal.scss' -export default function CreateModal (props) { +const CreateModal = (props) => { const location = useLocation() const history = useHistory() const previousLocation = useSelector(getPreviousLocation) const [returnToLocation] = useState(previousLocation) const [isDirty, setIsDirty] = useState() + const { t } = useTranslation() const querystringParams = new URLSearchParams(location.search) const mapLocation = (querystringParams.has('lat') && querystringParams.has('lng')) @@ -29,7 +31,7 @@ export default function CreateModal (props) { } const confirmClose = () => { - const confirmed = !isDirty || window.confirm('Changes won\'t be saved. Are you sure you want to cancel?') + const confirmed = !isDirty || window.confirmt(t('Changes won\'t be saved. Are you sure you want to cancel?')) if (confirmed) { closeModal() @@ -71,3 +73,5 @@ export default function CreateModal (props) { ) } + +export default CreateModal diff --git a/src/components/CreateModal/CreateModal.test.js b/src/components/CreateModal/CreateModal.test.js index 009663508..9f9a98e60 100644 --- a/src/components/CreateModal/CreateModal.test.js +++ b/src/components/CreateModal/CreateModal.test.js @@ -3,6 +3,18 @@ import orm from 'store/models' import { AllTheProviders, render, screen } from 'util/testing/reactTestingLibraryExtended' import CreateModal from './CreateModal' +jest.mock('react-i18next', () => ({ + ...jest.requireActual('react-i18next'), + useTranslation: (domain) => { + return { + t: (str) => str, + i18n: { + changeLanguage: () => new Promise(() => {}) + } + } + } +})) + function testProviders () { const ormSession = orm.mutableSession(orm.getEmptyState()) ormSession.Me.create({ id: '1' }) diff --git a/src/components/CreateModal/CreateModalChooser.js b/src/components/CreateModal/CreateModalChooser.js index f61cb2b66..451daf049 100644 --- a/src/components/CreateModal/CreateModalChooser.js +++ b/src/components/CreateModal/CreateModalChooser.js @@ -4,16 +4,18 @@ import isWebView from 'util/webView' import { POST_TYPES } from 'store/models/Post' import Icon from 'components/Icon' import './CreateModal.scss' +import { useTranslation } from 'react-i18next' const postTypes = Object.keys(POST_TYPES) export default function CreateModalChooser ({ location }) { const querystringParams = new URLSearchParams(location.search) const hasLocation = querystringParams.has('lat') && querystringParams.has('lng') + const { t } = useTranslation() return (
-

{hasLocation && 'New Post at this location: '}What would you like to create?

+

{hasLocation && `${t('New Post at this location')}:`}{t('What would you like to create?')}

{postTypes.map(postType => { querystringParams.set('newPostType', postType) @@ -40,8 +42,8 @@ export default function CreateModalChooser ({ location }) {
- Group - Create a new movement, network, community or group! + {t('Group')} + {t('Create a new movement, network, community or group!')}
diff --git a/src/components/EventInviteDialog/EventInviteDialog.js b/src/components/EventInviteDialog/EventInviteDialog.js index 230641895..159d082e8 100644 --- a/src/components/EventInviteDialog/EventInviteDialog.js +++ b/src/components/EventInviteDialog/EventInviteDialog.js @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react' +import { useTranslation } from 'react-i18next' import cx from 'classnames' import { bgImageStyle } from 'util/index' import ModalDialog from 'components/ModalDialog' @@ -11,7 +12,7 @@ import useInView from 'react-cool-inview' import Loading from 'components/Loading' const pageSize = 30 -export default function EventInviteDialog ({ +const EventInviteDialog = ({ fetchPeople, forGroups, eventInvitations, @@ -20,7 +21,7 @@ export default function EventInviteDialog ({ onClose, invitePeopleToEvent, pending -}) { +}) => { const [invitedIds, setInvitedIds] = useState([]) const [searchTerm, setSearchTerm] = useState('') const [pageFetched, setPageFetched] = useState(0) @@ -30,6 +31,7 @@ export default function EventInviteDialog ({ : setInvitedIds(invitedIds.concat([id])) const onSearchChange = ({ target: { value } }) => setSearchTerm(value) + const { t } = useTranslation() useEffect(() => { const fetch = () => { @@ -66,10 +68,10 @@ export default function EventInviteDialog ({ const filteredInviteSuggestions = getFilteredInviteSuggestions() const inviteButtonLabel = invitedIds.length === 0 - ? 'Select people to invite' + ? t('Select people to invite') : invitedIds.length === 1 - ? 'Invite 1 person' - : `Invite ${invitedIds.length} people` + ? t('Invite 1 person') + : t('Invite {{invitedIds.length}} people', { invitedIds }) return
-
Already Invited
+
{t('Already Invited')}
{eventInvitations.map(eventInvitation => { }) export function Search ({ onChange }) { + const { t } = useTranslation() return
x && x.focus()} - placeholder='Search members' + placeholder={t('Search members')} onChange={onChange} />
} + +export default EventInviteDialog diff --git a/src/components/EventInviteDialog/EventInviteDialog.test.js b/src/components/EventInviteDialog/EventInviteDialog.test.js index 91b54058e..574d9897c 100644 --- a/src/components/EventInviteDialog/EventInviteDialog.test.js +++ b/src/components/EventInviteDialog/EventInviteDialog.test.js @@ -2,6 +2,18 @@ import EventInviteDialog, { InviteeRow, Search } from './EventInviteDialog' import { shallow } from 'enzyme' import React from 'react' +jest.mock('react-i18next', () => ({ + ...jest.requireActual('react-i18next'), + useTranslation: (domain) => { + return { + t: (str) => str, + i18n: { + changeLanguage: () => new Promise(() => {}) + } + } + } +})) + describe('EventInviteDialog', () => { it('renders correctly', () => { const props = { diff --git a/src/components/StreamViewControls/StreamViewControls.js b/src/components/StreamViewControls/StreamViewControls.js index 3517318c6..95649c3c8 100644 --- a/src/components/StreamViewControls/StreamViewControls.js +++ b/src/components/StreamViewControls/StreamViewControls.js @@ -1,22 +1,12 @@ import React, { useState } from 'react' +import { useTranslation } from 'react-i18next' import cx from 'classnames' import Dropdown from 'components/Dropdown' import Icon from 'components/Icon' import Tooltip from 'components/Tooltip' import { COLLECTION_SORT_OPTIONS, STREAM_SORT_OPTIONS } from 'util/constants' - import './StreamViewControls.scss' -const POST_TYPE_OPTIONS = [ - { id: undefined, label: 'All Posts' }, - { id: 'discussion', label: 'Discussions' }, - { id: 'event', label: 'Events' }, - { id: 'offer', label: 'Offers' }, - { id: 'project', label: 'Projects' }, - { id: 'request', label: 'Requests' }, - { id: 'resource', label: 'Resources' } -] - const makeDropdown = (selected, options, onChange) => ( ( ) const StreamViewControls = (props) => { + const { t } = useTranslation() + const POST_TYPE_OPTIONS = [ + { id: undefined, label: t('All Posts') }, + { id: 'discussion', label: t('Discussions') }, + { id: 'event', label: t('Events') }, + { id: 'offer', label: t('Offers') }, + { id: 'project', label: t('Projects') }, + { id: 'request', label: t('Requests') }, + { id: 'resource', label: t('Resources') } + ] + const { customViewType, sortBy, postTypeFilter, viewMode, changeSearch, changeSort, changeTab, changeView, searchValue, view, customPostTypes } = props const [searchActive, setSearchActive] = useState(!!searchValue) const [searchState, setSearchState] = useState('') @@ -52,7 +53,7 @@ const StreamViewControls = (props) => {
changeView('cards')} - data-tip='Card view' data-for='stream-viewmode-tip' + data-tip={t('Card view')} data-for='stream-viewmode-tip' >
@@ -60,7 +61,7 @@ const StreamViewControls = (props) => {
changeView('list')} - data-tip='List view' data-for='stream-viewmode-tip' + data-tip={t('List view')} data-for='stream-viewmode-tip' >
@@ -68,7 +69,7 @@ const StreamViewControls = (props) => {
changeView('bigGrid')} - data-tip='Large Grid' data-for='stream-viewmode-tip' + data-tip={t('Large Grid')} data-for='stream-viewmode-tip' >
@@ -76,7 +77,7 @@ const StreamViewControls = (props) => {
changeView('grid')} - data-tip='Small Grid' data-for='stream-viewmode-tip' + data-tip={t('Small Grid')} data-for='stream-viewmode-tip' >
@@ -100,7 +101,7 @@ const StreamViewControls = (props) => { e.target.blur() } }} - placeholder='Search posts' + placeholder={t('Search posts')} value={searchState} />
} diff --git a/src/components/Widget/FarmOpenToPublic/FarmOpenToPublic.js b/src/components/Widget/FarmOpenToPublic/FarmOpenToPublic.js index 1f0c829f0..5c54fe33c 100644 --- a/src/components/Widget/FarmOpenToPublic/FarmOpenToPublic.js +++ b/src/components/Widget/FarmOpenToPublic/FarmOpenToPublic.js @@ -1,4 +1,5 @@ import React from 'react' +import { useTranslation } from 'react-i18next' import { keyBy } from 'lodash' import { getOpeningHours, getPublicOfferings, getOpenToPublic, getFarmAddressLine1, getFarmLocality, getFarmAdministrativeArea, getFarmPostalCode, getFarmCountryCode } from 'store/selectors/farmExtensionSelectors' import { PUBLIC_OFFERINGS } from 'util/constants' @@ -16,18 +17,19 @@ export default function FarmOpenToPublic ({ group }) { const openingHours = getOpeningHours(group) const publicOfferings = getPublicOfferings(group) const openToPublic = getOpenToPublic(group) + const { t } = useTranslation() return ( openToPublic ?
-
Open {openingHours}
+
{t('Open', { openingHours })}
{getFarmAddressLine1(group) &&
{getFarmAddressLine1(group)}
{`${getFarmLocality(group)}, ${getFarmAdministrativeArea(group)}`}
{`${getFarmPostalCode(group)}, ${getFarmCountryCode(group)}`}
} - {publicOfferings.length > 0 && publicOfferingsLookup[offering].label)} title='Public Offerings' />} + {publicOfferings.length > 0 && publicOfferingsLookup[offering].label)} title={t('Public Offerings')} />}
: null ) diff --git a/src/components/Widget/JoinWidget/JoinWidget.js b/src/components/Widget/JoinWidget/JoinWidget.js index 28f7fc2c4..ae0544f4d 100644 --- a/src/components/Widget/JoinWidget/JoinWidget.js +++ b/src/components/Widget/JoinWidget/JoinWidget.js @@ -1,4 +1,5 @@ import React from 'react' +import { useTranslation } from 'react-i18next' import { JoinSection } from 'routes/GroupDetail/GroupDetail' import { Link } from 'react-router-dom' import Avatar from 'components/Avatar' @@ -23,28 +24,29 @@ export default function JoinWidget ({ group, fullPage = true, routeParams }) { const handleRemoveSkill = (skillId) => dispatch(removeSkill(skillId)) const handleJoinGroup = (groupId) => dispatch(joinGroup(groupId)) const handleRequestToJoinGroup = (groupId, questionAnswers) => dispatch(createJoinRequest(groupId, questionAnswers)) + const { t } = useTranslation() return
{!currentUser - ?
Signup or Login to connect with {group.name}
+ ?
{t('Signup or Login to connect with')} {group.name}
:
-
Recent Posts
+
{t('Recent Posts')}
- Only members of this group can see posts + {t('Only members of this group can see posts')}
-
{group.memberCount} {group.memberCount > 1 ? `Members` : `Member`}
+
{group.memberCount} {group.memberCount > 1 ? t(`Members`) : t(`Member`)}
{get(group, 'settings.publicMemberDirectory') ?
{group.members.map(member => { return
{member.name}
})}
:
- Join to see + {t('Join to see')}
}
diff --git a/src/components/Widget/MapWidget/MapWidget.js b/src/components/Widget/MapWidget/MapWidget.js index 5750dee87..c370ad10e 100644 --- a/src/components/Widget/MapWidget/MapWidget.js +++ b/src/components/Widget/MapWidget/MapWidget.js @@ -1,11 +1,12 @@ import PropTypes from 'prop-types' import React, { Component } from 'react' +import { withTranslation } from 'react-i18next' import './MapWidget.scss' const { array } = PropTypes -export default class MapWidget extends Component { +class MapWidget extends Component { static propTypes = { map: array } @@ -13,8 +14,10 @@ export default class MapWidget extends Component { render () { return (
- Community map + {this.props.t('Community map')}
) } } + +export default withTranslation()(MapWidget) diff --git a/src/components/Widget/ModeratorsWidget/ModeratorsWidget.js b/src/components/Widget/ModeratorsWidget/ModeratorsWidget.js index f8fd31da0..998b82878 100644 --- a/src/components/Widget/ModeratorsWidget/ModeratorsWidget.js +++ b/src/components/Widget/ModeratorsWidget/ModeratorsWidget.js @@ -1,11 +1,14 @@ import React from 'react' +import { useTranslation } from 'react-i18next' import './Moderators.scss' export default function ModeratorsWidget (props) { + const { t } = useTranslation() + return (
- The moderators go here + {t('The moderators go here')}
) } diff --git a/src/components/Widget/PrivacyWidget/PrivacyWidget.js b/src/components/Widget/PrivacyWidget/PrivacyWidget.js index 1109c3c00..c582e7516 100644 --- a/src/components/Widget/PrivacyWidget/PrivacyWidget.js +++ b/src/components/Widget/PrivacyWidget/PrivacyWidget.js @@ -1,11 +1,14 @@ import React from 'react' +import { useTranslation } from 'react-i18next' import './Privacy.scss' export default function PrivacyWidget (props) { + const { t } = useTranslation() + return (
- group privacy settings + {t('group privacy settings')}
) } diff --git a/src/components/Widget/ProjectsWidget/ProjectsWidget.js b/src/components/Widget/ProjectsWidget/ProjectsWidget.js index 7c462e435..8c19332f0 100644 --- a/src/components/Widget/ProjectsWidget/ProjectsWidget.js +++ b/src/components/Widget/ProjectsWidget/ProjectsWidget.js @@ -1,6 +1,7 @@ import moment from 'moment-timezone' import PropTypes from 'prop-types' import React, { Component } from 'react' +import { withTranslation } from 'react-i18next' import { Link } from 'react-router-dom' import { postUrl, createPostUrl } from 'util/navigation' import RoundImage from '../../RoundImage' @@ -9,7 +10,7 @@ import './ProjectsWidget.scss' const { array, bool, object } = PropTypes -export default class ProjectsWidget extends Component { +class ProjectsWidget extends Component { static propTypes = { isMember: bool, items: array, @@ -36,10 +37,10 @@ export default class ProjectsWidget extends Component {
-
What are you doing together?
-
Projects help you and your group accomplish shared goals.
+
{this.props.t('What are you doing together?')}
+
{this.props.t('Projects help you and your group accomplish shared goals.')}
-
+ New project
+
{this.props.t('+ New project')}
: '' } @@ -47,3 +48,4 @@ export default class ProjectsWidget extends Component { ) } } +export default withTranslation()(ProjectsWidget) diff --git a/src/i18n.js b/src/i18n.js index f6816ed1b..675f0ec93 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -16,7 +16,10 @@ i18n // init i18next // for all options read: https://www.i18next.com/overview/configuration-options .init({ - fallbackLng: 'en', + lng: 'en', + nsSeparator: false, + keySeparator: false, + fallbackLng: false, debug: true, interpolation: { diff --git a/src/routes/AuthLayoutRouter/AuthLayoutRouter.test.js b/src/routes/AuthLayoutRouter/AuthLayoutRouter.test.js index 3f1267125..ef57492c3 100644 --- a/src/routes/AuthLayoutRouter/AuthLayoutRouter.test.js +++ b/src/routes/AuthLayoutRouter/AuthLayoutRouter.test.js @@ -7,6 +7,18 @@ import AuthLayoutRouter from './AuthLayoutRouter' const { ResizeObserver } = window +jest.mock('react-i18next', () => ({ + ...jest.requireActual('react-i18next'), + useTranslation: (domain) => { + return { + t: (str) => str, + i18n: { + changeLanguage: () => new Promise(() => {}) + } + } + } +})) + jest.mock('mixpanel-browser', () => ({ track: jest.fn(), identify: jest.fn(), From 8350af9a85023bd9d716d32d7d94811dca9f3aa7 Mon Sep 17 00:00:00 2001 From: Sofia Acosta Date: Tue, 6 Dec 2022 23:34:19 -0600 Subject: [PATCH 06/68] suppress test warnings --- src/routes/GroupExplorer/GroupExplorer.test.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/routes/GroupExplorer/GroupExplorer.test.js b/src/routes/GroupExplorer/GroupExplorer.test.js index 9e4f48734..13cb8fc94 100644 --- a/src/routes/GroupExplorer/GroupExplorer.test.js +++ b/src/routes/GroupExplorer/GroupExplorer.test.js @@ -7,6 +7,18 @@ import userEvent from '@testing-library/user-event' import orm from 'store/models' import { FARM_VIEW } from 'util/constants' +jest.mock('react-i18next', () => ({ + ...jest.requireActual('react-i18next'), + useTranslation: (domain) => { + return { + t: (str) => str, + i18n: { + changeLanguage: () => new Promise(() => {}) + } + } + } +})) + jest.mock('components/ScrollListener', () => () =>
) // was throwing errors with this.element().removeEventListener('blabadlbakdbfl') function testProviders () { From 6f26f97df3512edca4d3a4b0a86e829e50a3940e Mon Sep 17 00:00:00 2001 From: Sofia Acosta Date: Wed, 7 Dec 2022 16:02:48 -0600 Subject: [PATCH 07/68] additional translations --- public/locales/es/translation.json | 34 +++ .../RecentPostsWidget/RecentPostsWidget.js | 11 +- .../Widget/RichTextWidget/RichTextWidget.js | 7 +- .../Widget/TopicsWidget/TopicsWidget.js | 5 +- .../Widget/WelcomeWidget/WelcomeWidget.js | 8 +- src/components/Widget/Widget.js | 223 +++++++++--------- 6 files changed, 168 insertions(+), 120 deletions(-) diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json index 30fe588d3..4aef6e3c7 100644 --- a/public/locales/es/translation.json +++ b/public/locales/es/translation.json @@ -1,52 +1,86 @@ { + "+ New post": "", "+ New project": "", "All Posts": "", "Already Invited": "", + "Announcements": "", "Are you sure you want to delete your affiliation as {{affiliation.role}} {{affiliation.preposition}} {{affiliation.orgName}}?": "", + "At A Glance": "", + "Cancel": "", "Card view": "", "Changes won't be saved. Are you sure you want to cancel?": "", + "Community Topics": "", "Community map": "", "Create Group": "", "Create a new movement, network, community or group!": "", "Delete": "", "Discussions": "", + "Edit welcome message": "", + "Enter a title": "", + "Enter your message here": "", "Events": "", + "Farm Details": "", + "Farm Surrounds & Posts": "", "Group": "", + "Hidden": "", "IS THIS GROUP A MEMBER OF OTHER GROUPS?": "", "Invite 1 person": "", "Invite {{invitedIds.length}} people": "", "Join to see": "", "Large Grid": "", "List view": "", + "Location & Hours": "", "Member": "", "Members": "", + "Moderators": "", + "NO MORE RECENT ACTIVITY": "", + "Nearby Relevant Events": "", + "Nearby Relevant Groups": "", + "Nearby Relevant Offers and Requests": "", "New Post at this location": "", "Nothing to see here": "", "Offers": "", "Only members of this group can see posts": "", "Open": "", + "Open Requests & Offers": "", + "Opportunities to Collaborate": "", "Please enter a URL slug": "", "Please enter a group name": "", + "Privacy": "", "Projects": "", "Projects help you and your group accomplish shared goals.": "", "Public Offerings": "", "Recent Posts": "", + "Recently Active Members": "", + "Recently Active Projects": "", "Requests": "", "Resources": "", + "Save": "", "Search members": "", "Search posts": "", "Select people to invite": "", "Signup or Login to connect with": "", "Small Grid": "", + "Subgroups": "", "The moderators go here": "", + "The {{title}} section is not visible to members of this group. Click the three dots": "", "There was an error, please try again.": "", "This URL already exists. Try another.": "", + "Topics": "", "URLs must have between 2 and 40 characters, and can only have lower case letters, numbers, and dashes.": "", + "Upcoming Events": "", + "View all": "", + "Visibility": "", + "Visible": "", "WHO CAN JOIN THIS GROUP?": "", "WHO CAN SEE THIS GROUP?": "", + "We're happy you're here! Please take a moment to explore this page to see what's alive in our group. Introduce yourself by clicking Create to post a Discussion sharing who you are and what brings you to our group. And don't forget to fill out your profile - so likeminded new friends can connect with you!": "", + "Welcome Message": "", + "Welcome to {{group.name}}!": "", "What are you doing together?": "", "What would you like to create?": "", "You may add parent groups if you are a moderator of the group you wish to add, or if the group you wish to add has the Open access setting which allows any group to join it": "", "Your group's name": "", + "above this box to change the visibility settings. Only moderators can see this message": "", "group privacy settings": "" } diff --git a/src/components/Widget/RecentPostsWidget/RecentPostsWidget.js b/src/components/Widget/RecentPostsWidget/RecentPostsWidget.js index 47327ba11..091133a9c 100644 --- a/src/components/Widget/RecentPostsWidget/RecentPostsWidget.js +++ b/src/components/Widget/RecentPostsWidget/RecentPostsWidget.js @@ -1,4 +1,5 @@ import React, { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' import Slider from 'react-slick' import { Link } from 'react-router-dom' import { filter, isEmpty } from 'lodash/fp' @@ -34,8 +35,9 @@ const settings = { ] } -export default ({ group, items, routeParams }) => { +const RecentPostsWidget = ({ group, items, routeParams }) => { const [swiped, setSwiped] = useState(false) + const { t } = useTranslation() const handleSwiped = useCallback(() => { setSwiped(true) @@ -59,13 +61,13 @@ export default ({ group, items, routeParams }) => {
-
NO MORE RECENT ACTIVITY
-
+ New post
+
{t('NO MORE RECENT ACTIVITY')}
+
{t('+ New post')}
- View all + {t('View all')}
@@ -97,3 +99,4 @@ const RecentPostCard = ({ group, onClickCapture, post, routeParams }) => {
) } +export default RecentPostsWidget diff --git a/src/components/Widget/RichTextWidget/RichTextWidget.js b/src/components/Widget/RichTextWidget/RichTextWidget.js index f7a734455..9019de11c 100644 --- a/src/components/Widget/RichTextWidget/RichTextWidget.js +++ b/src/components/Widget/RichTextWidget/RichTextWidget.js @@ -1,14 +1,17 @@ import React from 'react' +import { useTranslation } from 'react-i18next' import GroupAboutVideoEmbed from 'components/GroupAboutVideoEmbed' import ClickCatcher from 'components/ClickCatcher' import HyloHTML from 'components/HyloHTML' import './RichText.scss' export default function RichTextWidget ({ group, settings }) { + const { t } = useTranslation() + return (
-

{settings.title || `Welcome to ${group.name}!`}

+

{settings.title || t('Welcome to {{group.name}}!', { group })}

{settings.richText && ( @@ -19,7 +22,7 @@ export default function RichTextWidget ({ group, settings }) { )} {!settings.text && !settings.richText && (

- We're happy you're here! Please take a moment to explore this page to see what's alive in our group. Introduce yourself by clicking Create to post a Discussion sharing who you are and what brings you to our group. And don't forget to fill out your profile - so likeminded new friends can connect with you! + {t("We're happy you're here! Please take a moment to explore this page to see what's alive in our group. Introduce yourself by clicking Create to post a Discussion sharing who you are and what brings you to our group. And don't forget to fill out your profile - so likeminded new friends can connect with you!")}

)}
diff --git a/src/components/Widget/TopicsWidget/TopicsWidget.js b/src/components/Widget/TopicsWidget/TopicsWidget.js index c38168ff8..43ebfe023 100644 --- a/src/components/Widget/TopicsWidget/TopicsWidget.js +++ b/src/components/Widget/TopicsWidget/TopicsWidget.js @@ -1,11 +1,14 @@ import React from 'react' +import { useTranslation } from 'react-i18next' import './Topics.scss' export default function TopicsWidget ({ group = {} }) { + const { t } = useTranslation() + return ( group.topics ?
-
Topics
+
{t('Topics')}
{group.topics.slice(0, 10).map(topic => { return ( -

{settings.title || `Welcome to ${group.name}!`}

+

{settings.title || this.props.t('Welcome to {{group.name}}!', { group })}

@@ -25,3 +26,4 @@ export default class WelcomeWidget extends Component { ) } } +export default withTranslation()(WelcomeWidget) diff --git a/src/components/Widget/Widget.js b/src/components/Widget/Widget.js index 5d26a3610..4b84bcc5d 100644 --- a/src/components/Widget/Widget.js +++ b/src/components/Widget/Widget.js @@ -1,4 +1,5 @@ import React, { useState } from 'react' +import { useTranslation } from 'react-i18next' import Icon from 'components/Icon' import AtAGlanceWidget from 'components/Widget/AtAGlanceWidget' import AnnouncementWidget from 'components/Widget/AnnouncementWidget' @@ -28,103 +29,113 @@ import getMe from 'store/selectors/getMe' import { updateWidget } from './Widget.store' import useRouter from 'hooks/useRouter' -const WIDGETS = { - text_block: { - title: '', - moderatorTitle: 'Welcome Message', - component: WelcomeWidget - }, - announcements: { - title: 'Announcements', - component: AnnouncementWidget - }, - active_members: { - title: 'Recently Active Members', - component: MembersWidget - }, - requests_offers: { - title: 'Open Requests & Offers', - component: OffersAndRequestsWidget - }, - posts: { - title: 'Recent Posts', - component: RecentPostsWidget - }, - community_topics: { - title: 'Community Topics', - component: GroupTopicsWidget - }, - events: { - title: 'Upcoming Events', - component: EventsWidget - }, - project_activity: { - title: 'Recently Active Projects', - component: ProjectsWidget - }, - group_affiliations: { - title: 'Subgroups', - component: GroupsWidget - }, - relevant_project_activity: { - title: 'Recently Active Projects', - component: ProjectsWidget - }, - relevant_groups: { - title: 'Nearby Relevant Groups', // TODO: ensure there is a way to customize/overwrite this - component: GroupsWidget - }, - relevant_events: { - title: 'Nearby Relevant Events', // TODO: ensure there is a way to customize/overwrite this - component: EventsWidget - }, - relevant_requests_offers: { - title: 'Nearby Relevant Offers and Requests', // TODO: ensure there is a way to customize/overwrite this - component: OffersAndRequestsWidget - }, - farm_at_a_glance: { - title: 'At A Glance', - component: AtAGlanceWidget - }, - farm_details: { - title: 'Farm Details', - component: FarmDetailsWidget - }, - farm_open_to_public: { - title: 'Location & Hours', - component: FarmOpenToPublic - }, - farm_map: { - title: 'Farm Surrounds & Posts', - component: FarmMapWidget - }, - moderators: { - title: 'Moderators', // TODO: ensure there is a way to customize/overwrite this - component: ModeratorsWidget - }, - opportunities_to_collaborate: { - title: 'Opportunities to Collaborate', - component: OpportunitiesToCollaborateWidget - }, - privacy_settings: { - title: 'Privacy', - component: PrivacyWidget - }, - mission: { - title: null, - component: RichTextWidget - }, - join: { - title: null, - component: JoinWidget - }, - topics: { - title: null, - component: TopicsWidget +export default function Widget (props) { + const { t } = useTranslation() + const WIDGETS = { + text_block: { + title: '', + moderatorTitle: t('Welcome Message'), + component: WelcomeWidget + }, + announcements: { + title: t('Announcements'), + component: AnnouncementWidget + }, + active_members: { + title: t('Recently Active Members'), + component: MembersWidget + }, + requests_offers: { + title: t('Open Requests & Offers'), + component: OffersAndRequestsWidget + }, + posts: { + title: t('Recent Posts'), + component: RecentPostsWidget + }, + community_topics: { + title: t('Community Topics'), + component: GroupTopicsWidget + }, + events: { + title: t('Upcoming Events'), + component: EventsWidget + }, + project_activity: { + title: t('Recently Active Projects'), + component: ProjectsWidget + }, + group_affiliations: { + title: t('Subgroups'), + component: GroupsWidget + }, + relevant_project_activity: { + title: t('Recently Active Projects'), + component: ProjectsWidget + }, + relevant_groups: { + title: t('Nearby Relevant Groups'), // TODO: ensure there is a way to customize/overwrite this + component: GroupsWidget + }, + relevant_events: { + title: t('Nearby Relevant Events'), // TODO: ensure there is a way to customize/overwrite this + component: EventsWidget + }, + relevant_requests_offers: { + title: t('Nearby Relevant Offers and Requests'), // TODO: ensure there is a way to customize/overwrite this + component: OffersAndRequestsWidget + }, + farm_at_a_glance: { + title: t('At A Glance'), + component: AtAGlanceWidget + }, + farm_details: { + title: t('Farm Details'), + component: FarmDetailsWidget + }, + farm_open_to_public: { + title: t('Location & Hours'), + component: FarmOpenToPublic + }, + farm_map: { + title: t('Farm Surrounds & Posts'), + component: FarmMapWidget + }, + moderators: { + title: t('Moderators'), // TODO: ensure there is a way to customize/overwrite this + component: ModeratorsWidget + }, + opportunities_to_collaborate: { + title: t('Opportunities to Collaborate'), + component: OpportunitiesToCollaborateWidget + }, + privacy_settings: { + title: t('Privacy'), + component: PrivacyWidget + }, + mission: { + title: null, + component: RichTextWidget + }, + join: { + title: null, + component: JoinWidget + }, + topics: { + title: null, + component: TopicsWidget + } + } + const HiddenWidget = ({ name }) => { + const { t } = useTranslation() + return ( +
+

{t('Hidden')}

+

{t('The {{title}} section is not visible to members of this group. Click the three dots', { title: WIDGETS[name].moderatorTitle || WIDGETS[name].title })} () {t('above this box to change the visibility settings. Only moderators can see this message')}.

+
+ ) } -} -export default function Widget (props) { const dispatch = useDispatch() const { childGroups, id, isModerator, isVisible, name, posts, isMember, settings = {} } = props const router = useRouter() @@ -154,14 +165,14 @@ export default function Widget (props) {
{!isEditingSettings &&
  - {name === 'text_block' &&
setIsEditingSettings(!isEditingSettings)}> Edit welcome message
} + {name === 'text_block' &&
setIsEditingSettings(!isEditingSettings)}> {t('Edit welcome message')}
}
handleUpdateWidget(id, { isVisible: !isVisible })} styleName='widget-visibility' - backgroundColor={isVisible ? 'gray' : 'black'} /> Visibility: {isVisible ? 'Visible' : 'Hidden'} + backgroundColor={isVisible ? 'gray' : 'black'} /> {t('Visibility')}: {isVisible ? t('Visible') : t('Hidden')}
}
@@ -185,16 +196,8 @@ export default function Widget (props) { ) } -const HiddenWidget = ({ isVisible, name }) => { - return ( -
-

Hidden

-

The {WIDGETS[name].moderatorTitle || WIDGETS[name].title} section is not visible to members of this group. Click the three dots () above this box to change the visibility settings. Only moderators can see this message.

-
- ) -} - const EditForm = ({ id, setIsEditingSettings, setIsMenuOpen, newSettings, updateSettings, save }) => { + const { t } = useTranslation() return (
@@ -202,7 +205,7 @@ const EditForm = ({ id, setIsEditingSettings, setIsMenuOpen, newSettings, update type='text' autoFocus onChange={e => updateSettings({ ...newSettings, title: e.target.value.substring(0, 50) })} - placeholder='Enter a title' + placeholder={t('Enter a title')} value={newSettings.title} />
{(newSettings.title && newSettings.title.length) || 0}/{50}
@@ -212,7 +215,7 @@ const EditForm = ({ id, setIsEditingSettings, setIsMenuOpen, newSettings, update