diff --git a/source/actions/actions.ts b/source/actions/actions.ts index 8661b1458..35ace4be8 100644 --- a/source/actions/actions.ts +++ b/source/actions/actions.ts @@ -17,7 +17,7 @@ export type Action = | HideControlAction | LoadPreviousSimulationAction | SetSituationBranchAction - | UpdateDefaultUnit + | UpdateDefaultUnitAction | SetActiveTargetAction export type ThunkResult = ThunkAction< @@ -64,7 +64,7 @@ type SetSituationBranchAction = ReturnType type SetActiveTargetAction = ReturnType type HideControlAction = ReturnType type ExplainVariableAction = ReturnType -type UpdateDefaultUnit = ReturnType +type UpdateDefaultUnitAction = ReturnType export const resetSimulation = () => ({ diff --git a/source/engine/index.ts b/source/engine/index.ts index c368e7c46..e1e350159 100644 --- a/source/engine/index.ts +++ b/source/engine/index.ts @@ -27,6 +27,7 @@ type Cache = { } } +export { default as translateRules } from './translateRules' export { parseRules } export default class Engine { parsedRules: Record diff --git a/source/engine/ruleUtils.js b/source/engine/ruleUtils.js index 6b3c1fc47..eda9b54ce 100644 --- a/source/engine/ruleUtils.js +++ b/source/engine/ruleUtils.js @@ -1,5 +1,4 @@ import { - assoc, dropLast, filter, isNil, @@ -9,7 +8,6 @@ import { pipe, propEq, range, - reduce, reject, split, take @@ -72,70 +70,6 @@ export function collectDefaults(parsedRules) { Autres */ -/* Traduction */ -const translateContrôle = (prop, rule, translation, lang) => - assoc( - 'contrôles', - rule.contrôles.map((control, i) => ({ - ...control, - message: translation[`${prop}.${i}.${lang}`]?.replace( - /^\[automatic\] /, - '' - ) - })), - rule - ) -const translateSuggestion = (prop, rule, translation, lang) => - assoc( - 'suggestions', - Object.entries(rule.suggestions).reduce( - (acc, [name, value]) => ({ - ...acc, - [translation[`${prop}.${name}.${lang}`]?.replace( - /^\[automatic\] /, - '' - )]: value - }), - {} - ), - rule - ) - -export const attributesToTranslate = [ - 'titre', - 'description', - 'question', - 'résumé', - 'suggestions', - 'contrôles', - 'note' -] - -export let translateAll = (translations, flatRules) => { - let translationsOf = rule => translations[rule.dottedName], - translateProp = (lang, translation) => (rule, prop) => { - if (prop === 'contrôles' && rule?.contrôles) { - return translateContrôle(prop, rule, translation, lang) - } - if (prop === 'suggestions' && rule?.suggestions) { - return translateSuggestion(prop, rule, translation, lang) - } - let propTrans = translation[prop + '.' + lang] - propTrans = propTrans?.replace(/^\[automatic\] /, '') - return propTrans ? assoc(prop, propTrans, rule) : rule - }, - translateRule = (lang, translations, props) => rule => { - let ruleTrans = translationsOf(rule) - return ruleTrans - ? reduce(translateProp(lang, ruleTrans), rule, props) - : rule - } - return map( - translateRule('en', translations, attributesToTranslate), - flatRules - ) -} - export let findParentDependencies = (rules, rule) => { // A parent dependency means that one of a rule's parents is not just a namespace holder, it is a boolean question. E.g. is it a fixed-term contract, yes / no // When it is resolved to false, then the whole branch under it is disactivated (non applicable) diff --git a/source/engine/translateRules.ts b/source/engine/translateRules.ts new file mode 100644 index 000000000..26716c2c1 --- /dev/null +++ b/source/engine/translateRules.ts @@ -0,0 +1,74 @@ +import { assoc } from 'ramda' + +/* Traduction */ +const translateContrôle = (prop, rule, translation, lang) => + assoc( + 'contrôles', + rule.contrôles.map((control, i) => ({ + ...control, + message: translation[`${prop}.${i}.${lang}`]?.replace( + /^\[automatic\] /, + '' + ) + })), + rule + ) + +const translateSuggestion = (prop, rule, translation, lang) => + assoc( + 'suggestions', + Object.entries(rule.suggestions).reduce( + (acc, [name, value]) => ({ + ...acc, + [translation[`${prop}.${name}.${lang}`]?.replace( + /^\[automatic\] /, + '' + )]: value + }), + {} + ), + rule + ) + +export const attributesToTranslate = [ + 'titre', + 'description', + 'question', + 'résumé', + 'suggestions', + 'contrôles', + 'note' +] + +const translateProp = (lang, translation) => (rule, prop) => { + if (prop === 'contrôles' && rule?.contrôles) { + return translateContrôle(prop, rule, translation, lang) + } + if (prop === 'suggestions' && rule?.suggestions) { + return translateSuggestion(prop, rule, translation, lang) + } + let propTrans = translation[prop + '.' + lang] + propTrans = propTrans?.replace(/^\[automatic\] /, '') + return propTrans ? assoc(prop, propTrans, rule) : rule +} + +const translateRule = (lang, translations, name, rule) => { + let ruleTrans = translations[name] + if (!ruleTrans) { + return rule + } + return attributesToTranslate.reduce( + translateProp(lang, ruleTrans), + rule ?? {} + ) +} + +export default function translateRules(lang, translations, rules) { + const translatedRules = Object.fromEntries( + Object.entries(rules).map(([name, rule]) => [ + name, + translateRule(lang, translations, name, rule) + ]) + ) + return translatedRules +} diff --git a/source/locales/en.yaml b/source/locales/en.yaml index 92fdc1115..d26cb8326 100644 --- a/source/locales/en.yaml +++ b/source/locales/en.yaml @@ -1,6 +1,7 @@ '404': action: Return to safe place message: This page does not exist or no longer exists +'<0>Covid-19 et chômage partiel : <3>Calculez votre indemnité': '<0>Covid-19 and Short-Time Work: <3>Calculate Your Benefit' <0>Oui: <0>Yes A quoi servent mes cotisations ?: What's included in my contributions? Accueil: Home @@ -60,7 +61,7 @@ Gérant minoritaire: Managing director Habituellement: Usually Imprimer: Print Impôts: Taxes -'Indemnité chômage partiel prise en charge par l''état :': 'State-paid short-time working allowance :' +"Indemnité chômage partiel prise en charge par l'état :": 'State-paid short-time working allowance :' Indépendant: Independent International: International Intégrer l'interface de simulation: Integrate the simulation interface @@ -77,7 +78,7 @@ Mon entreprise: My company Mon revenu: My income Montant: Amount Montant des cotisations: Amount of contributions -'Nom de l''entreprise ou SIREN ': Company name or SIREN code +"Nom de l'entreprise ou SIREN ": Company name or SIREN code Non: 'No' Nous n'avons rien trouvé: We didn't find any matching registered company. Oui: 'Yes' @@ -142,7 +143,7 @@ Taux: Rate Taux calculé: Calculated rate Taux moyen: Average rate Total des retenues: Total withheld -'Total payé par l''entreprise :': 'Total paid by the company :' +"Total payé par l'entreprise :": 'Total paid by the company :' Tout effacer: Delete all Tranche de l'assiette: Scale bracket Un seul associé: Only one partner @@ -401,9 +402,7 @@ coronavirus: <0>Coronavirus and short-time working: what impact on my income?<1>The government is putting in place measures to support employees affected by the Coronavirus crisis. One of the key measures is the assumption of the entire - short-time working compensation by the State.<2>This simulator allows - you to find out your net income if you have been placed on short-time work, - as well as the total cost to the company. + short-time working compensation by the State. page: description: Estimate net income with short-time working benefits titre: 'Coronavirus and short-time working: what impact on your income?' diff --git a/source/reducers/rootReducer.ts b/source/reducers/rootReducer.ts index e322f124d..3e1f31910 100644 --- a/source/reducers/rootReducer.ts +++ b/source/reducers/rootReducer.ts @@ -1,24 +1,21 @@ import { Action } from 'Actions/actions' import { Unit } from 'Engine/units' +import originRules from 'Publicode/rules' import { defaultTo, identity, omit, without } from 'ramda' import reduceReducers from 'reduce-reducers' import { combineReducers, Reducer } from 'redux' import { analysisWithDefaultsSelector } from 'Selectors/analyseSelectors' import { SavedSimulation } from 'Selectors/storageSelectors' -import { DottedName } from 'Types/rule' +import { DottedName, Rules } from 'Types/rule' import i18n, { AvailableLangs } from '../i18n' import { areUnitConvertible, convertUnit, parseUnit } from './../engine/units' import inFranceAppReducer, { Company } from './inFranceAppReducer' import storageRootReducer from './storageReducer' -function rules(state = null, action) { - switch (action.type) { - case 'SET_RULES': - return action.rules - default: - return state - } +function rules(state: Rules = originRules) { + return state } + function explainedVariable( state: DottedName | null = null, action: Action diff --git a/source/selectors/analyseSelectors.ts b/source/selectors/analyseSelectors.ts index 67542f619..ab6e62927 100644 --- a/source/selectors/analyseSelectors.ts +++ b/source/selectors/analyseSelectors.ts @@ -5,7 +5,6 @@ import { disambiguateRuleReference, splitName } from 'Engine/ruleUtils' -import rules from 'Publicode/rules' import { add, difference, @@ -54,9 +53,7 @@ let configSelector = (state: RootState) => state.simulation?.config || {} // hot-reloading on developement). See // https://github.com/betagouv/mon-entreprise/issues/912 // TOTO -const rulesEn = rules -let flatRulesSelector = (state: RootState) => - state.rules ?? (state.lang === 'en' ? rulesEn : rules) +let flatRulesSelector = (state: RootState) => state.rules // We must here compute parsedRules, flatRules, analyse which contains both // targets and cache objects diff --git a/source/sites/mon-entreprise.fr/App.tsx b/source/sites/mon-entreprise.fr/App.tsx index 217803277..21dcc3cd7 100644 --- a/source/sites/mon-entreprise.fr/App.tsx +++ b/source/sites/mon-entreprise.fr/App.tsx @@ -55,7 +55,7 @@ const middlewares = [ trackSimulatorActions(tracker) ] -function InFranceRoute({ basename, language }) { +function InFranceRoute({ basename, language, rules }) { useEffect(() => { getSessionStorage()?.setItem('lang', language) }, [language]) @@ -73,7 +73,8 @@ function InFranceRoute({ basename, language }) { }} initialStore={{ ...retrievePersistedState(), - previousSimulation: retrievePersistedSimulation() + previousSimulation: retrievePersistedSimulation(), + rules }} > diff --git a/source/sites/mon-entreprise.fr/entry.en.tsx b/source/sites/mon-entreprise.fr/entry.en.tsx index c4ef853de..7f081817e 100644 --- a/source/sites/mon-entreprise.fr/entry.en.tsx +++ b/source/sites/mon-entreprise.fr/entry.en.tsx @@ -1,8 +1,19 @@ import 'core-js/stable' +import { translateRules } from 'Engine' +import rules from 'Publicode/rules' import React from 'react' import { render } from 'react-dom' import 'regenerator-runtime/runtime' +import translations from '../../locales/rules-en.yaml' import App from './App' let anchor = document.querySelector('#js') -render(, anchor) +console.log(translateRules('en', translations, rules)) +render( + , + anchor +) diff --git a/source/sites/mon-entreprise.fr/entry.fr.tsx b/source/sites/mon-entreprise.fr/entry.fr.tsx index d33bc59e6..2be9019d4 100644 --- a/source/sites/mon-entreprise.fr/entry.fr.tsx +++ b/source/sites/mon-entreprise.fr/entry.fr.tsx @@ -1,8 +1,9 @@ import 'core-js/stable' +import rules from 'Publicode/rules' import React from 'react' import { render } from 'react-dom' import 'regenerator-runtime/runtime' import App from './App' let anchor = document.querySelector('#js') -render(, anchor) +render(, anchor) diff --git a/source/sites/mon-entreprise.fr/pages/Coronavirus.tsx b/source/sites/mon-entreprise.fr/pages/Coronavirus.tsx index 94937f1ae..25b78ed3e 100644 --- a/source/sites/mon-entreprise.fr/pages/Coronavirus.tsx +++ b/source/sites/mon-entreprise.fr/pages/Coronavirus.tsx @@ -139,17 +139,20 @@ function ExplanationSection() { netHabituel, { ...net, - additionalText: ( + additionalText: language === 'fr' && ( <> - Soit{' '} - - {formatValue({ - value: (net.nodeValue / netHabituel.nodeValue) * 100, - unit: '%', - maximumFractionDigits: 0 - })} - {' '} - du revenu net + + Soit{' '} + + {formatValue({ + value: + (net.nodeValue / netHabituel.nodeValue) * 100, + unit: '%', + maximumFractionDigits: 0 + })} + {' '} + du revenu net + ) } @@ -159,7 +162,7 @@ function ExplanationSection() { totalEntrepriseHabituel, { ...totalEntreprise, - additionalText: ( + additionalText: language === 'fr' && ( <> Soit{' '} diff --git a/source/sites/mon-entreprise.fr/pages/Simulateurs/Salarié.tsx b/source/sites/mon-entreprise.fr/pages/Simulateurs/Salarié.tsx index 7232bc392..74fb2de30 100644 --- a/source/sites/mon-entreprise.fr/pages/Simulateurs/Salarié.tsx +++ b/source/sites/mon-entreprise.fr/pages/Simulateurs/Salarié.tsx @@ -162,8 +162,10 @@ export let SalarySimulation = () => { />
- Covid-19 et chômage partiel :{' '} - Calculez votre indemnité + + Covid-19 et chômage partiel :{' '} + Calculez votre indemnité + diff --git a/source/sites/publi.codes/App.js b/source/sites/publi.codes/App.js index 1e561c81e..3841095ad 100644 --- a/source/sites/publi.codes/App.js +++ b/source/sites/publi.codes/App.js @@ -10,25 +10,12 @@ import Landing from './Landing' import Studio from './LazyStudio' import Mécanismes from './Mécanismes' -function Router({ language }) { +function Router({ language, rules }) { useEffect(() => { getSessionStorage()?.setItem('lang', language) }, [language]) - const rules = language === 'en' ? rules : rules return ( - + ) diff --git a/source/types/rule.ts b/source/types/rule.ts index 99f2ac00c..45ca571b0 100644 --- a/source/types/rule.ts +++ b/source/types/rule.ts @@ -2,6 +2,9 @@ import { Unit } from 'Engine/units' import jsonRules from './dottednames.json' export type DottedName = keyof typeof jsonRules +// TODO : différencier Rule et ParsedRule +export type Rules = { [key in DottedName]: Rule } + export type Rule = { dottedName: DottedName question?: string