🔥 meilleure prise en charge de la traduction

- Les traductions sont désormais récupérées uniquement pour le site anglais
- Engine ne dépend plus des traductions
pull/949/head
Johan Girod 2020-03-30 18:24:18 +02:00
parent 3e8c7b4b26
commit 4020e49871
14 changed files with 128 additions and 118 deletions

View File

@ -17,7 +17,7 @@ export type Action =
| HideControlAction
| LoadPreviousSimulationAction
| SetSituationBranchAction
| UpdateDefaultUnit
| UpdateDefaultUnitAction
| SetActiveTargetAction
export type ThunkResult<R> = ThunkAction<
@ -64,7 +64,7 @@ type SetSituationBranchAction = ReturnType<typeof setSituationBranch>
type SetActiveTargetAction = ReturnType<typeof setActiveTarget>
type HideControlAction = ReturnType<typeof hideControl>
type ExplainVariableAction = ReturnType<typeof explainVariable>
type UpdateDefaultUnit = ReturnType<typeof updateUnit>
type UpdateDefaultUnitAction = ReturnType<typeof updateUnit>
export const resetSimulation = () =>
({

View File

@ -27,6 +27,7 @@ type Cache = {
}
}
export { default as translateRules } from './translateRules'
export { parseRules }
export default class Engine {
parsedRules: Record<DottedName, Rule>

View File

@ -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)

View File

@ -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
}

View File

@ -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 </0>: <3>Calculez votre indemnité</3>': '<0>Covid-19 and Short-Time </0>Work: <3>Calculate Your Benefit</3>'
<0>Oui</0>: <0>Yes</0>
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?</0><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.</1><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.</2>
short-time working compensation by the State.</1>
page:
description: Estimate net income with short-time working benefits
titre: 'Coronavirus and short-time working: what impact on your income?'

View File

@ -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

View File

@ -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

View File

@ -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
}}
>
<RouterSwitch />

View File

@ -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(<App language="en" basename="infrance" />, anchor)
console.log(translateRules('en', translations, rules))
render(
<App
language="en"
basename="infrance"
rules={translateRules('en', translations, rules)}
/>,
anchor
)

View File

@ -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(<App language="fr" basename="mon-entreprise" />, anchor)
render(<App language="fr" basename="mon-entreprise" rules={rules} />, anchor)

View File

@ -139,17 +139,20 @@ function ExplanationSection() {
netHabituel,
{
...net,
additionalText: (
additionalText: language === 'fr' && (
<>
Soit{' '}
<strong>
{formatValue({
value: (net.nodeValue / netHabituel.nodeValue) * 100,
unit: '%',
maximumFractionDigits: 0
})}
</strong>{' '}
du revenu net
<Trans>
Soit{' '}
<strong>
{formatValue({
value:
(net.nodeValue / netHabituel.nodeValue) * 100,
unit: '%',
maximumFractionDigits: 0
})}
</strong>{' '}
du revenu net
</Trans>
</>
)
}
@ -159,7 +162,7 @@ function ExplanationSection() {
totalEntrepriseHabituel,
{
...totalEntreprise,
additionalText: (
additionalText: language === 'fr' && (
<>
Soit{' '}
<strong>

View File

@ -162,8 +162,10 @@ export let SalarySimulation = () => {
/>
<br />
<Banner icon={'😷'}>
<strong>Covid-19 et chômage partiel </strong>:{' '}
<Link to={sitePaths.coronavirus}>Calculez votre indemnité</Link>
<Trans>
<strong>Covid-19 et chômage partiel </strong>:{' '}
<Link to={sitePaths.coronavirus}>Calculez votre indemnité</Link>
</Trans>
</Banner>
<PreviousSimulationBanner />
</>

View File

@ -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 (
<Provider
basename="publicodes"
language={language}
reduxMiddlewares={[]}
initialStore={{
rules,
simulation: {
config: {
objectifs: []
}
}
}}
>
<Provider basename="publicodes" language={language} rules={rules}>
<RouterSwitch />
</Provider>
)

View File

@ -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