Skip to content
This repository was archived by the owner on Jul 9, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .storybook/LangSwitcher.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { useState, useMemo } from 'react'
import { TransProvider } from '../src/Atoms/Trans'

const LangSwitcher = ({ langs = {}, children }) => {
const availableLangs = useMemo(() => Object.keys(langs), [])
const [lang, setLang] = useState(availableLangs[0])

return (
<TransProvider value={langs[lang]}>
<select onChange={ev => setLang(ev.target.value)}>
{availableLangs.map(value =>
<option value={value} key={value}>{value}</option>
)}
</select>
<div>
{children}
</div>
</TransProvider>
)
}

export default LangSwitcher
6 changes: 3 additions & 3 deletions .storybook/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ import { withThemesProvider } from 'storybook-addon-styled-component-theme'
import { ThemeProvider } from '@material-ui/core/styles'

import { MuiPickersUtilsProvider } from '../src/Atoms/DateTimePicker'
import { TransProvider } from '../src/Atoms/Trans'
import results from '../.jest-test-results.json'
import { muiRg6Theme } from './themes'
import { rg6, dark } from '../src/themes'
import { en } from './locales'
import LangSwitcher from './LangSwitcher'
import { locales } from '../src'

addDecorator(withKnobs)
addDecorator(withA11y)
addDecorator(withThemesProvider([rg6, dark]))
addDecorator(withTests({ results }))
addDecorator(storyFn => <TransProvider value={en}>{storyFn()}</TransProvider>)
addDecorator(storyFn => <ThemeProvider theme={muiRg6Theme}>{storyFn()}</ThemeProvider>)
addDecorator(storyFn => <MuiPickersUtilsProvider utils={util} locale={fr}>{storyFn()}</MuiPickersUtilsProvider>)
addDecorator(storyFn => <LangSwitcher langs={locales}>{storyFn()}</LangSwitcher>)
// automatically import all files ending in *.stories.jsx
configure(require.context('../src', true, /\.stories\.jsx$/), module)
34 changes: 0 additions & 34 deletions .storybook/locales/en.js

This file was deleted.

3 changes: 0 additions & 3 deletions .storybook/locales/index.js

This file was deleted.

67 changes: 56 additions & 11 deletions src/Atoms/Trans/Trans.stories.jsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,72 @@
import React from 'react'

import Trans, { TransProvider } from './index'
import Trans, { TransProvider, useTranslation } from './index'
import FlexBox from '../../Templates/FlexBox'

import markdown from './README.md'

export default {
title: 'Atoms/Trans',
}

const translations = {
global: {
welcome: 'Hello there !',
},
const LocalWrapper = ({ translations = {}, ...props }) => {
const t = useTranslation()
const lang = t('lang')

return <TransProvider value={translations[lang]} {...props} />
}

export const trans = () => (
<TransProvider value={translations}>
<Trans transKey="global.welcome"/>
<br />
<Trans transKey="global.goodbye"/>
</TransProvider>
const TransWrapper = ({ transKey, ...props }) => <div><b>{transKey}</b>: <Trans {...props}>{transKey}</Trans></div>

const FirstChild = () => (
<FlexBox flexDirection="column" gap={0.5} bgcolor="rgba(0, 0, 255, 0.1)" p={2}>
<LocalWrapper translations={{ en: { scope: 'first child' }, fr: { value: 'premier enfant' } }}>
<TransWrapper transKey="scope" />
</LocalWrapper>
</FlexBox>
)

const SecondChild = () => (
<FlexBox flexDirection="column" gap={0.5} bgcolor="rgba(0, 255, 0, 0.1)" p={2}>
<LocalWrapper translations={{
en: { scope: 'second child', local: { value: 'some specific local value can be declared' } },
fr: { scope: 'deuxième enfant', local: { value: 'une valeur locale peut être déclarée' } },
}}>
<TransWrapper transKey="scope" />
<TransWrapper transKey="local.value" />
<TransWrapper transKey="keep" />
</LocalWrapper>
</FlexBox>
)

export const trans = () => <>
<FlexBox flexDirection="column" gap={2} bgcolor="rgba(0, 0, 0, 0.1)" p={2}>
<LocalWrapper translations={{
en: {
scope: 'parent',
keep: 'not overridden value still reachable',
outscope: 'Not accessible from outside it\'s scope so fallback is used',
JSXReplace: 'JSX can be used in %variables%',
},
fr: {
scope: 'parent',
keep: 'valeur non surchargée toujours accessibles',
outscope: 'Non accessible en dehors de son scope le fallback est donc utilisé',
JSXReplace: 'Du JSX peut être utilisé dans les %variables%',
},
}}>
<TransWrapper transKey="JSXReplace" variables={<span style={{ color: 'red', fontWeight: 'bold' }}>variables</span>} />
<TransWrapper transKey="scope" />
<FirstChild />
<SecondChild />
<TransWrapper transKey="scope" />
<TransWrapper transKey="keep" />
<Trans>outscope</Trans>
<TransWrapper transKey="local.value" />
</LocalWrapper>
</FlexBox>
</>

trans.story = {
parameters: {
notes: { markdown },
Expand Down
76 changes: 62 additions & 14 deletions src/Atoms/Trans/useTranslation.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,70 @@
import { createContext, useContext } from 'react'
import React, { createContext, useContext, useMemo, Fragment } from 'react'
import { deepMerge } from '../../utils'
const Context = createContext()
export const { Provider } = Context
const { Provider: BaseProvider } = Context

export const Provider = ({ value, ...props }) => {
const translations = useContext(Context) || {}
const merged = useMemo(
() => deepMerge(translations, value),
[translations, value],
)

return <BaseProvider value={merged} {...props} />
}

const preProcessor = ({ translations }) => (key = '') => (
key.split('.').reduce(
(acc, k) => (acc[k] || key),
translations,
)
)

const processor = ({ translations = {}, ...context }) => {
const params = Object.entries(context)
if (!params.length) {
return translation => [translation]
}
const mapReplace = params.reduce(
(acc, [k, v]) => Object.assign(acc, { [`%${k}%`]: v }),
{},
)
const re = new RegExp(Object.keys(mapReplace).join('|'), 'g')

return function * (translation = '') {
let lastIndex = 0
for (const match of translation.matchAll(re)) {
yield translation.slice(lastIndex, match.index)
yield mapReplace[match[0]]
lastIndex = match.index + match[0].length
}
yield translation.slice(lastIndex)
}
}

const postProcessor = () => (processedTranslation = []) => {
let result = ''
for (const chunk of processedTranslation) {
console.info('chunk', chunk)
if (!['number', 'string'].includes(typeof chunk)) {
return React.createElement(Fragment, null, result, chunk, ...processedTranslation)
}
result = result.concat(chunk)
}
return result
}

export default () => {
const translations = useContext(Context) || {}

return (transKey = '', parameters = {}) => {
let translated = transKey
.split('.')
.reduce(
(acc, k) => acc[k] || transKey,
translations,
)

Object.entries(parameters).forEach(([key, value]) => {
translated = translated.replace(new RegExp(`%${key}%`, 'g'), value)
})

return translated
const context = { translations, ...parameters }

const preProcessed = preProcessor(context)(transKey)
const processed = processor(context)(preProcessed)
const postProcessed = postProcessor(context)(processed)

console.info(transKey, postProcessed)
return postProcessed
}
}
2 changes: 2 additions & 0 deletions src/Molecules/DateRange/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import DateRange from './DateRange'
import * as locales from './locales'

export { locales }
export default DateRange
6 changes: 6 additions & 0 deletions src/Molecules/DateRange/locales/en.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const locale = {
startDate: 'Start date',
endDate: 'End date',
}

export default locale
6 changes: 6 additions & 0 deletions src/Molecules/DateRange/locales/fr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const locale = {
startDate: 'Date de début',
endDate: 'Date de fin',
}

export default locale
4 changes: 4 additions & 0 deletions src/Molecules/DateRange/locales/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import en from './en'
import fr from './fr'

export { en, fr }
10 changes: 5 additions & 5 deletions src/Molecules/Export/Export.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ const Actions = ({ onClose, onExport, disabled }) => <>
<Trans transKey="global.action.cancel"/>
</Button>
<Button color="success" onClick={onExport} size="small" disabled={disabled}>
<Trans transKey="global.export.actionExport"/>
<Trans transKey="molecules.export.actionExport"/>
</Button>
</>

const Export = ({
descriptionText = <Trans transKey="global.export.description"/>,
descriptionText = <Trans transKey="molecules.export.description"/>,
onExport = () => {},
value = {},
onChange = () => {},
Expand All @@ -36,7 +36,7 @@ const Export = ({
...props
}) => {
const t = useTranslation()
const { defaultName = t('global.export.defaultFilename') } = props
const { defaultName = t('molecules.export.defaultFilename') } = props
const { format = '', filename = defaultName } = value
const onFileNameChange = event => onChange({ filename: event.target.value, format })

Expand All @@ -45,13 +45,13 @@ const Export = ({
<FlexBox alignItems='center' mb={2} >
<Box component={Download} size={20} color="primary.main" />
<Box component='h2' ml={0.5} my={0} fontSize="fontSizes.title" fontFamily="fontFamily" color="primary.main">
<Trans transKey="global.export.title" />
<Trans transKey="molecules.export.title" />
</Box>
</FlexBox>
<Typo>{descriptionText}</Typo>
<FormControl>
<InputLabel>
<Trans transKey="global.export.filename" />
<Trans transKey="molecules.export.filename" />
<Input value={filename} onChange={onFileNameChange} />
</InputLabel>
</FormControl>
Expand Down
Loading