diff --git a/examples/full/gatsby-config.js b/examples/full/gatsby-config.js index 89230f3e..31a2fce4 100644 --- a/examples/full/gatsby-config.js +++ b/examples/full/gatsby-config.js @@ -1,13 +1,32 @@ const { resolve } = require('path') +const merge = require('deepmerge') + +const tailwindConfigStub = require('gatsby-theme-mdx-suite-core/src/tailwind.default.config') +const minimumConfig = require('gatsby-theme-mdx-suite-core/minimum-config') + const themeConfig = require('./tailwind.config.js') +const localesEn = require('./src/locales/en-US/messages') +const localesDe = require('./src/locales/de/messages') const isProduction = process.env.NODE_ENV === 'production' // Duplicate and rename .env.example to .env and fill in your credentials require('dotenv').config({ path: `.env` }) -const translationsEn = require('./translations/en') -const translationsDe = require('./translations/de') +function prepareMdxSuiteOptions(config) { + const mergedConfig = merge(minimumConfig, config, { + arrayMerge: (destinationArray, sourceArray, options) => sourceArray, + }) + + const theme = merge(tailwindConfigStub, mergedConfig.themeConfig.theme.extend) + + const cleanConfig = Object.entries(mergedConfig) + .filter(([key]) => !['themeConfig', 'mediaCollections'].includes(key)) + .reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {}) + + // @todo clean up this mess! + return { ...cleanConfig, cleanConfig, theme } +} module.exports = { pathPrefix: ``, @@ -20,7 +39,7 @@ module.exports = { plugins: [ { resolve: `gatsby-theme-mdx-suite-core`, - options: { + options: prepareMdxSuiteOptions({ // Configure the MDX Suite themeConfig, /** @@ -32,14 +51,14 @@ module.exports = { 'en-US': '', de: 'de', }, - // @todo - // Will be passes as resources to i18next. - // See https://www.i18next.com/overview/configuration-options + // Will be passed to LinguiJS. + // @todo can't we automatically import that in onPreInit? translations: { - 'en-US': translationsEn, - de: translationsDe, + 'en-US': localesEn.messages, + de: localesDe.messages, }, /** + * @todo this is!!! exposed via page state json. Switch to env vars!! * Contentful credentials from environment variables will be used by default. * Never hardcode API credentials in your projects. * This is a exception for demonstration purposes. @@ -49,7 +68,7 @@ module.exports = { accessToken: `yfNcvsaJfU6nmL6xzKwbP-WHw27vvDzjTCeFkg93pKk`, environment: `full`, }, - }, + }), }, // Basic layout, SEO, Analytics and more { diff --git a/examples/full/gatsby-node.js b/examples/full/gatsby-node.js index c8bbfdc2..9356bea1 100644 --- a/examples/full/gatsby-node.js +++ b/examples/full/gatsby-node.js @@ -1,6 +1,17 @@ const { resolve } = require('path') const { createPath } = require('@gatsby-mdx-suite/helpers/routing') +// exports.onPreInit = async ({ store }, config) => { +// const program = store.getState().program +// const rootDir = program.directory +// console.log({ +// rootDir, +// store, +// key: Object.keys(store), +// state: store.getState(), +// }) +// } + exports.createPages = async ({ graphql, actions, getCache }) => { const { createPage, createRedirect } = actions const { config } = await getCache().get('mdx-suite') diff --git a/examples/full/src/gatsby-theme-mdx-suite-base/components/navigation/mobile.js b/examples/full/src/gatsby-theme-mdx-suite-base/components/navigation/mobile.js index 34cbab11..b6c7bccc 100644 --- a/examples/full/src/gatsby-theme-mdx-suite-base/components/navigation/mobile.js +++ b/examples/full/src/gatsby-theme-mdx-suite-base/components/navigation/mobile.js @@ -180,7 +180,7 @@ const NavigationMobile = ({ rootMenuItemId }) => { - + diff --git a/examples/full/src/locales/de/messages.js b/examples/full/src/locales/de/messages.js new file mode 100644 index 00000000..b5e64258 --- /dev/null +++ b/examples/full/src/locales/de/messages.js @@ -0,0 +1,78 @@ +/*eslint-disable*/ module.exports = { + messages: { + 'consent-manager.close': 'consent-manager.close', + 'consent-manager.fallback.algolia.title': + 'consent-manager.fallback.algolia.title', + 'consent-manager.fallback.default.description': + 'consent-manager.fallback.default.description', + 'consent-manager.fallback.default.enable': + 'consent-manager.fallback.default.enable', + 'consent-manager.fallback.default.learn-more': + 'consent-manager.fallback.default.learn-more', + 'consent-manager.fallback.default.title': + 'consent-manager.fallback.default.title', + 'consent-manager.fallback.mapbox.title': + 'consent-manager.fallback.mapbox.title', + 'consent-manager.fallback.vimeo.title': + 'consent-manager.fallback.vimeo.title', + 'consent-manager.fallback.youtube.title': + 'consent-manager.fallback.youtube.title', + 'consent-manager.form.description': 'consent-manager.form.description', + 'consent-manager.form.disable-all': 'consent-manager.form.disable-all', + 'consent-manager.form.enable-all': 'consent-manager.form.enable-all', + 'consent-manager.form.headline': 'consent-manager.form.headline', + 'consent-manager.form.reset': 'consent-manager.form.reset', + 'consent-manager.form.save': 'consent-manager.form.save', + 'consent-manager.integration.algolia.category': + 'consent-manager.integration.algolia.category', + 'consent-manager.integration.algolia.description': + 'consent-manager.integration.algolia.description', + 'consent-manager.integration.default.category': + 'consent-manager.integration.default.category', + 'consent-manager.integration.default.company': + 'consent-manager.integration.default.company', + 'consent-manager.integration.default.description': + 'consent-manager.integration.default.description', + 'consent-manager.integration.default.privacy-policy': + 'consent-manager.integration.default.privacy-policy', + 'consent-manager.integration.default.title': + 'consent-manager.integration.default.title', + 'consent-manager.integration.mapbox.category': + 'consent-manager.integration.mapbox.category', + 'consent-manager.integration.mapbox.description': + 'consent-manager.integration.mapbox.description', + 'consent-manager.integration.matomo.category': + 'consent-manager.integration.matomo.category', + 'consent-manager.integration.matomo.description': + 'consent-manager.integration.matomo.description', + 'consent-manager.integration.vimeo.category': + 'consent-manager.integration.vimeo.category', + 'consent-manager.integration.vimeo.description': + 'consent-manager.integration.vimeo.description', + 'consent-manager.integration.youtube.category': + 'consent-manager.integration.youtube.category', + 'consent-manager.integration.youtube.description': + 'consent-manager.integration.youtube.description', + 'consent-manager.introduction.description': + 'consent-manager.introduction.description', + 'consent-manager.introduction.enable-all': + 'consent-manager.introduction.enable-all', + 'consent-manager.introduction.learn-more': + 'consent-manager.introduction.learn-more', + 'consent-manager.introduction.title': 'consent-manager.introduction.title', + contactFormCompany: 'contactFormCompany', + contactFormCompanyEmail: 'contactFormCompanyEmail', + contactFormFirstName: 'contactFormFirstName', + contactFormLastName: 'contactFormLastName', + contactFormNumberOfEmployees: 'contactFormNumberOfEmployees', + contactFormPosition: 'contactFormPosition', + contactFormSubmit: 'contactFormSubmit', + contactFormTosAccepted: 'contactFormTosAccepted', + copyright: 'copyright', + menu: 'menu', + newsReadMore: 'Weiterlesen', + next: 'next', + previous: 'previous', + '{0} min. to read': [['0'], ' min. to read'], + }, +} diff --git a/examples/full/src/locales/de/messages.po b/examples/full/src/locales/de/messages.po new file mode 100644 index 00000000..fe9fc6c1 --- /dev/null +++ b/examples/full/src/locales/de/messages.po @@ -0,0 +1,207 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2021-11-13 12:07+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: de\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/consent-manager-i18n.js:8 +msgid "consent-manager.close" +msgstr "" + +#: src/consent-manager-i18n.js:275 +msgid "consent-manager.fallback.algolia.title" +msgstr "" + +#: src/consent-manager-i18n.js:99 +msgid "consent-manager.fallback.default.description" +msgstr "" + +#: src/consent-manager-i18n.js:117 +msgid "consent-manager.fallback.default.enable" +msgstr "" + +#: src/consent-manager-i18n.js:124 +msgid "consent-manager.fallback.default.learn-more" +msgstr "" + +#: src/consent-manager-i18n.js:90 +msgid "consent-manager.fallback.default.title" +msgstr "" + +#: src/consent-manager-i18n.js:272 +msgid "consent-manager.fallback.mapbox.title" +msgstr "" + +#: src/consent-manager-i18n.js:281 +msgid "consent-manager.fallback.vimeo.title" +msgstr "" + +#: src/consent-manager-i18n.js:278 +msgid "consent-manager.fallback.youtube.title" +msgstr "" + +#: src/consent-manager-i18n.js:48 +msgid "consent-manager.form.description" +msgstr "" + +#: src/consent-manager-i18n.js:79 +msgid "consent-manager.form.disable-all" +msgstr "" + +#: src/consent-manager-i18n.js:73 +msgid "consent-manager.form.enable-all" +msgstr "" + +#: src/consent-manager-i18n.js:42 +msgid "consent-manager.form.headline" +msgstr "" + +#: src/consent-manager-i18n.js:70 +msgid "consent-manager.form.reset" +msgstr "" + +#: src/consent-manager-i18n.js:85 +msgid "consent-manager.form.save" +msgstr "" + +#: src/consent-manager-i18n.js:186 +msgid "consent-manager.integration.algolia.category" +msgstr "" + +#: src/consent-manager-i18n.js:176 +msgid "consent-manager.integration.algolia.description" +msgstr "" + +#: src/consent-manager-i18n.js:139 +msgid "consent-manager.integration.default.category" +msgstr "" + +#: src/consent-manager-i18n.js:132 +msgid "consent-manager.integration.default.company" +msgstr "" + +#: src/consent-manager-i18n.js:151 +msgid "consent-manager.integration.default.description" +msgstr "" + +#: src/consent-manager-i18n.js:162 +msgid "consent-manager.integration.default.privacy-policy" +msgstr "" + +#: src/consent-manager-i18n.js:145 +msgid "consent-manager.integration.default.title" +msgstr "" + +#: src/consent-manager-i18n.js:206 +msgid "consent-manager.integration.mapbox.category" +msgstr "" + +#: src/consent-manager-i18n.js:196 +msgid "consent-manager.integration.mapbox.description" +msgstr "" + +#: src/consent-manager-i18n.js:226 +msgid "consent-manager.integration.matomo.category" +msgstr "" + +#: src/consent-manager-i18n.js:216 +msgid "consent-manager.integration.matomo.description" +msgstr "" + +#: src/consent-manager-i18n.js:246 +msgid "consent-manager.integration.vimeo.category" +msgstr "" + +#: src/consent-manager-i18n.js:236 +msgid "consent-manager.integration.vimeo.description" +msgstr "" + +#: src/consent-manager-i18n.js:266 +msgid "consent-manager.integration.youtube.category" +msgstr "" + +#: src/consent-manager-i18n.js:256 +msgid "consent-manager.integration.youtube.description" +msgstr "" + +#: src/consent-manager-i18n.js:22 +msgid "consent-manager.introduction.description" +msgstr "" + +#: src/consent-manager-i18n.js:34 +msgid "consent-manager.introduction.enable-all" +msgstr "" + +#: src/consent-manager-i18n.js:28 +msgid "consent-manager.introduction.learn-more" +msgstr "" + +#: src/consent-manager-i18n.js:12 +msgid "consent-manager.introduction.title" +msgstr "" + +#: :121 +msgid "contactFormCompany" +msgstr "" + +#: :112 +msgid "contactFormCompanyEmail" +msgstr "" + +#: :95 +msgid "contactFormFirstName" +msgstr "" + +#: :103 +msgid "contactFormLastName" +msgstr "" + +#: :126 +msgid "contactFormNumberOfEmployees" +msgstr "" + +#: :108 +msgid "contactFormPosition" +msgstr "" + +#: :148 +msgid "contactFormSubmit" +msgstr "" + +#: :144 +msgid "contactFormTosAccepted" +msgstr "" + +#: :33 +msgid "copyright" +msgstr "" + +#: :185 +#: src/gatsby-theme-mdx-suite-base/components/navigation/mobile.js:183 +msgid "menu" +msgstr "" + +#: :42 +msgid "newsReadMore" +msgstr "Weiterlesen" + +#: :64 +msgid "next" +msgstr "" + +#: :57 +msgid "previous" +msgstr "" + +#: :33 +msgid "{0} min. to read" +msgstr "" diff --git a/examples/full/src/locales/en-US/messages.js b/examples/full/src/locales/en-US/messages.js new file mode 100644 index 00000000..96f4ed82 --- /dev/null +++ b/examples/full/src/locales/en-US/messages.js @@ -0,0 +1,78 @@ +/*eslint-disable*/ module.exports = { + messages: { + 'consent-manager.close': 'consent-manager.close', + 'consent-manager.fallback.algolia.title': + 'consent-manager.fallback.algolia.title', + 'consent-manager.fallback.default.description': + 'consent-manager.fallback.default.description', + 'consent-manager.fallback.default.enable': + 'consent-manager.fallback.default.enable', + 'consent-manager.fallback.default.learn-more': + 'consent-manager.fallback.default.learn-more', + 'consent-manager.fallback.default.title': + 'consent-manager.fallback.default.title', + 'consent-manager.fallback.mapbox.title': + 'consent-manager.fallback.mapbox.title', + 'consent-manager.fallback.vimeo.title': + 'consent-manager.fallback.vimeo.title', + 'consent-manager.fallback.youtube.title': + 'consent-manager.fallback.youtube.title', + 'consent-manager.form.description': 'consent-manager.form.description', + 'consent-manager.form.disable-all': 'consent-manager.form.disable-all', + 'consent-manager.form.enable-all': 'consent-manager.form.enable-all', + 'consent-manager.form.headline': 'consent-manager.form.headline', + 'consent-manager.form.reset': 'consent-manager.form.reset', + 'consent-manager.form.save': 'consent-manager.form.save', + 'consent-manager.integration.algolia.category': + 'consent-manager.integration.algolia.category', + 'consent-manager.integration.algolia.description': + 'consent-manager.integration.algolia.description', + 'consent-manager.integration.default.category': + 'consent-manager.integration.default.category', + 'consent-manager.integration.default.company': + 'consent-manager.integration.default.company', + 'consent-manager.integration.default.description': + 'consent-manager.integration.default.description', + 'consent-manager.integration.default.privacy-policy': + 'consent-manager.integration.default.privacy-policy', + 'consent-manager.integration.default.title': + 'consent-manager.integration.default.title', + 'consent-manager.integration.mapbox.category': + 'consent-manager.integration.mapbox.category', + 'consent-manager.integration.mapbox.description': + 'consent-manager.integration.mapbox.description', + 'consent-manager.integration.matomo.category': + 'consent-manager.integration.matomo.category', + 'consent-manager.integration.matomo.description': + 'consent-manager.integration.matomo.description', + 'consent-manager.integration.vimeo.category': + 'consent-manager.integration.vimeo.category', + 'consent-manager.integration.vimeo.description': + 'consent-manager.integration.vimeo.description', + 'consent-manager.integration.youtube.category': + 'consent-manager.integration.youtube.category', + 'consent-manager.integration.youtube.description': + 'consent-manager.integration.youtube.description', + 'consent-manager.introduction.description': + 'consent-manager.introduction.description', + 'consent-manager.introduction.enable-all': + 'consent-manager.introduction.enable-all', + 'consent-manager.introduction.learn-more': + 'consent-manager.introduction.learn-more', + 'consent-manager.introduction.title': 'consent-manager.introduction.title', + contactFormCompany: 'contactFormCompany', + contactFormCompanyEmail: 'contactFormCompanyEmail', + contactFormFirstName: 'contactFormFirstName', + contactFormLastName: 'contactFormLastName', + contactFormNumberOfEmployees: 'contactFormNumberOfEmployees', + contactFormPosition: 'contactFormPosition', + contactFormSubmit: 'contactFormSubmit', + contactFormTosAccepted: 'contactFormTosAccepted', + copyright: 'copyright', + menu: 'menu', + newsReadMore: 'Read more', + next: 'next', + previous: 'previous', + '{0} min. to read': [['0'], ' min. to read'], + }, +} diff --git a/examples/full/src/locales/en-US/messages.po b/examples/full/src/locales/en-US/messages.po index 94c77f65..8ac6909f 100644 --- a/examples/full/src/locales/en-US/messages.po +++ b/examples/full/src/locales/en-US/messages.po @@ -184,5 +184,22 @@ msgid "copyright" msgstr "" #: :185 +#: src/gatsby-theme-mdx-suite-base/components/navigation/mobile.js:183 msgid "menu" msgstr "" + +#: :42 +msgid "newsReadMore" +msgstr "Read more" + +#: :64 +msgid "next" +msgstr "" + +#: :57 +msgid "previous" +msgstr "" + +#: :33 +msgid "{0} min. to read" +msgstr "" diff --git a/examples/full/src/templates/page.js b/examples/full/src/templates/page.js index e3abb7b7..c9f196bc 100644 --- a/examples/full/src/templates/page.js +++ b/examples/full/src/templates/page.js @@ -1,4 +1,4 @@ -import React, { useContext, useEffect } from 'react' +import React, { useContext } from 'react' import { graphql } from 'gatsby' import * as propTypes from 'prop-types' diff --git a/themes/blog/src/components/blog-post-teaser.js b/themes/blog/src/components/blog-post-teaser.js index b3372aec..a92c76a7 100644 --- a/themes/blog/src/components/blog-post-teaser.js +++ b/themes/blog/src/components/blog-post-teaser.js @@ -30,10 +30,7 @@ const BlogPostTeaser = ({ blogPost, ...props }) => { {Intl.DateTimeFormat(blogPost.locale).format( new Date(blogPost.publicationDate) )}{' '} - -{' '} - {t('newsTimeToRead', { - minutes: blogPost.content.childMdx.timeToRead, - })} + - {t`${blogPost.content.childMdx.timeToRead} min. to read`} {blogPost.teaser && ( @@ -42,7 +39,7 @@ const BlogPostTeaser = ({ blogPost, ...props }) => { )} - {t('newsReadMore')} + {t`newsReadMore`} ) diff --git a/themes/blog/src/templates/blog-post-list.js b/themes/blog/src/templates/blog-post-list.js index 9a698781..5136bc4b 100644 --- a/themes/blog/src/templates/blog-post-list.js +++ b/themes/blog/src/templates/blog-post-list.js @@ -5,6 +5,7 @@ import tw from 'twin.macro' import styled from '@emotion/styled' import { t } from '@lingui/macro' import { MDXRenderer } from 'gatsby-plugin-mdx' +import { useLingui } from '@lingui/react' import MdxSuiteContext from '@gatsby-mdx-suite/contexts/mdx-suite' import mergeContextData from '@gatsby-mdx-suite/helpers/data/merge-context-data' @@ -22,7 +23,9 @@ const Pagination = styled.div` ` const BlogPostList = ({ data, pageContext }) => { + const { i18n } = useLingui() const MdxSuiteData = useContext(MdxSuiteContext) + i18n.activate(pageContext.locale) const blogPosts = data.allContentfulBlogPost.nodes.map((blogPost, i) => ( @@ -54,14 +57,14 @@ const BlogPostList = ({ data, pageContext }) => { {pageContext.previousPagePath && ( - {t('previous')} + {t`previous`} )}
{pageContext.nextPagePath && ( - {t('next')} + {t`next`} )} diff --git a/themes/core/gatsby-browser.js b/themes/core/gatsby-browser.js index 5ec4caf9..8351bfdf 100644 --- a/themes/core/gatsby-browser.js +++ b/themes/core/gatsby-browser.js @@ -1,54 +1,10 @@ import React from 'react' import propTypes from 'prop-types' -import { i18n } from '@lingui/core' -import { I18nProvider } from '@lingui/react' -import { remoteLoader } from '@lingui/remote-loader' -import merge from 'deepmerge' -import { MDXProvider } from '@mdx-js/react' -import { ThemeProvider } from '@emotion/react' -import { css, Global } from '@emotion/react' -import { globalStyles } from 'twin.macro' -import MdxSuiteContextProvider from '@gatsby-mdx-suite/contexts/provider' -import { BreakpointProvider } from '@gatsby-mdx-suite/helpers/hooks/use-breakpoint' -import tailwindConfigStub from './src/tailwind.default.config' -import minimumConfig from './minimum-config' -import components from './src/components' +import { ContextProvider } from './src/context-provider' export const wrapRootElement = ({ element }, config) => { - const mergedConfig = merge(minimumConfig, config, { - arrayMerge: (destinationArray, sourceArray, options) => sourceArray, - }) - - const { defaultLocale, themeConfig } = mergedConfig - - const { translations } = config - - const theme = merge(tailwindConfigStub, themeConfig.theme.extend) - - if (translations) { - Object.keys(translations).forEach((locale) => { - i18n.load(locale, remoteLoader(translations[locale])) - }) - } - i18n.activate(defaultLocale) - - delete mergedConfig.translations - delete mergedConfig.themeConfig - delete mergedConfig.mediaCollections - - return ( - - - - - - {element} - - - - - ) + return } wrapRootElement.propTypes = { element: propTypes.element.isRequired, diff --git a/themes/core/src/context-provider.js b/themes/core/src/context-provider.js new file mode 100644 index 00000000..70d78851 --- /dev/null +++ b/themes/core/src/context-provider.js @@ -0,0 +1,55 @@ +import React, { useEffect, useMemo } from 'react' +import { css, Global, ThemeProvider } from '@emotion/react' +import MdxSuiteContextProvider from '@gatsby-mdx-suite/contexts/provider' +import { BreakpointProvider } from '@gatsby-mdx-suite/helpers/hooks/use-breakpoint' +import { i18n } from '@lingui/core' +import { I18nProvider } from '@lingui/react' +import { MDXProvider } from '@mdx-js/react' +import { globalStyles } from 'twin.macro' +import { en, de } from 'make-plural/plurals' +import components from './components' + +export const ContextProvider = ({ element, config }) => { + const { cleanConfig, theme } = config + + const i18nReady = useMemo(() => { + const { defaultLocale, translations } = cleanConfig + + const messages = Object.keys(translations).reduce((locales, locale) => { + console.log('loading', locale, translations[locale]) + return { ...locales, [locale]: translations[locale] } + }, {}) + + console.log({ messages }) + + i18n.load(messages) + // @todo make dynamic! + i18n.loadLocaleData({ + 'en-US': { plurals: en }, + de: { plurals: de }, + }) + + i18n.activate(defaultLocale) + + return true + }, [cleanConfig]) + + console.log({ config, i18n }) + + if (!i18nReady) { + return false + } + + return ( + + + + + + {element} + + + + + ) +}