👽🔨 ajoute un script de traduction automatique des règles

Ainsi qu'une tâche circle ci pour vérifier qu'il ne manque pas des traductions de règles
pull/855/head
Johan Girod 2020-01-21 18:11:28 +01:00
parent a393dbe948
commit efbaaa742e
9 changed files with 929 additions and 488 deletions

View File

@ -37,7 +37,13 @@ jobs:
- install
- run: |
yarn run type-check
i18n-check:
docker:
- image: node
steps:
- install
- run: |
yarn run i18n:check-rules
unit-test:
docker:
- image: node
@ -91,6 +97,7 @@ workflows:
test:
jobs:
- type-check
- i18n-check
- unit-test
- end-to-end-test
- production-end-to-end-test:

View File

@ -89,6 +89,8 @@
"type-check": "tsc --noEmit",
"compile-lib": "yarn webpack --config source/webpack.lib.js",
"compile-dev": "FR_SITE='http://localhost:5000${path}' EN_SITE='http://localhost:5001${path}' yarn run compile",
"i18n:check-rules": "node source/scripts/check-missing-translation.js",
"i18n:translate-rules": "node source/scripts/automatic-translate.js",
"mon-entreprise:serve": "PORT=5000 serve --config serve.mon-entreprise.json --no-clipboard",
"mon-entreprise:test": "cypress open --browser chromium",
"mycompanyinfrance:serve": "PORT=5001 serve --config serve.infrance.json --no-clipboard",

View File

@ -635,7 +635,15 @@ simulateurs:
titre: Before starting...
plus: Read explanations
urssaf: The figures are indicative and do not replace the actual accounts of the Urssaf, impots.gouv.fr, etc
auto-entrepreneur: Self-employed entrepreneurs cannot deduct their expenses from their turnover. Therefore, <3>all costs related to the business must be deducted on a net basis to obtain the actual income received</3>.
auto-entrepreneur: |
Self-employed entrepreneurs cannot deduct their expenses
from their turnover. Therefore, <3>all costs related to the business must be
deducted on a net basis to obtain the actual income received</3>.
cfe: >
The simulator does not include the corporate property tax (CFE), which is
due from the second year of the fiscal year. Its amount varies greatly
depending on the company's turnover and domiciliation.
<2> More info. </2>
précision:
défaut: 'Refine the simulation by answering the following questions:'
faible: Low accuracy

View File

@ -4426,7 +4426,7 @@ dirigeant . auto-entrepreneur . cotisations et contributions . TFC . métiers:
- sinon: 0.48%
dirigeant . auto-entrepreneur . cotisations et contributions . contribution formation professionnelle:
titre: Contribution à la formation professionnelle
titre: Contribution à la formation professionnelleyay
unité par défaut: €/mois
références:
shine.fr: https://www.shine.fr/blog/formation-professionnelle-auto-entrepreneur/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
require('dotenv').config()
var { safeDump } = require('js-yaml')
var fs = require('fs')
var querystring = require('querystring')
require('isomorphic-fetch')
const {
getMissingTranslations,
externalizationPath
} = require('./get-missing-translations')
const [missingTranslations, resolved] = getMissingTranslations()
fs.writeFileSync(externalizationPath, safeDump(resolved))
const translateWithDeepl = async text => {
const response = await fetch(
`https://api.deepl.com/v2/translate?${querystring.stringify({
text,
auth_key: process.env.DEEPL_API_SECRET,
source_lang: 'FR',
target_lang: 'EN'
})}`
)
const { translations } = await response.json()
return translations[0].text
}
missingTranslations.length &&
console.log(
`Fetch translation for: \n${missingTranslations
.map(([dottedName, attr]) => `\t- ${dottedName} [${attr}]\n`)
.join('')}`
)
missingTranslations.forEach(async ([dottedName, attr, value]) => {
try {
const translation = await translateWithDeepl(value)
resolved[dottedName][attr] = translation
// C'est très bourrin, mais on ne veut pas perdre une traduction qu'on a payé
fs.writeFileSync(externalizationPath, safeDump(resolved))
} catch (e) {
console.log(e)
}
})

View File

@ -0,0 +1,13 @@
const { getMissingTranslations } = require('./get-missing-translations')
const missingTranslations = getMissingTranslations()[0]
if (missingTranslations.length) {
throw new Error(
`Il manque les traductions suivantes dans 'externalized.yaml' : \n${missingTranslations
.map(([dottedName, attr]) => `\t- ${dottedName} [${attr}]\n`)
.join(
''
)}\nUtilisez la commande suivante pour traduire automatiquement les clés manquantes :\n\n\tyarn run i18n:translate-rules\n`
)
}

View File

@ -1,57 +0,0 @@
var { safeLoad, safeDump } = require('js-yaml')
var fs = require('fs')
var path = require('path')
let R = require('ramda')
let externalizationPath = 'source/règles/externalized.yaml'
let rules = safeLoad(
fs.readFileSync(path.resolve('source/règles/base.yaml'), 'utf-8')
)
let currentExternalization = safeLoad(
fs.readFileSync(path.resolve(externalizationPath), 'utf-8')
)
let attributesToExternalize = [
'titre',
'description',
'question',
'résumé',
'suggestions',
'contrôles'
]
let resolved = Object.entries(rules)
.map(([dottedName, rule]) => [
dottedName,
!rule || !rule.titre
? { ...rule, titre: dottedName.split(' . ').slice(-1)[0] }
: rule
])
.map(([dottedName, rule]) => ({
[dottedName]: R.mergeAll(
R.toPairs(rule)
.filter(([, v]) => !!v)
.map(([k, v]) => {
let attrToTranslate = attributesToExternalize.find(R.equals(k))
if (!attrToTranslate) return {}
let enTrad = attrToTranslate + '.en',
frTrad = attrToTranslate + '.fr'
let currentTranslation = currentExternalization[dottedName]
//Check if a human traduction exists already for this attribute
if (currentTranslation && currentTranslation[enTrad])
return {
[enTrad]: currentTranslation[enTrad],
[frTrad]: v
}
return {
[enTrad]: '!!' + v,
[frTrad]: v
}
})
)
}))
fs.writeFileSync(externalizationPath, safeDump(R.mergeAll(resolved)))

View File

@ -0,0 +1,75 @@
var fs = require('fs')
var path = require('path')
let R = require('ramda')
let { safeLoad } = require('js-yaml')
let externalizationPath = 'source/règles/externalized.yaml'
let rules = safeLoad(
fs.readFileSync(path.resolve('source/règles/base.yaml'), 'utf-8')
)
let currentExternalization = safeLoad(
fs.readFileSync(path.resolve(externalizationPath), 'utf-8')
)
let attributesToExternalize = [
'titre',
'description',
'question',
'résumé',
'suggestions',
'contrôles'
]
function getMissingTranslations() {
let missingTranslations = []
let resolved = Object.entries(rules)
.map(([dottedName, rule]) => [
dottedName,
!rule || !rule.titre
? { ...rule, titre: dottedName.split(' . ').slice(-1)[0] }
: rule
])
.map(([dottedName, rule]) => ({
[dottedName]: R.mergeAll(
R.toPairs(rule)
.filter(([, v]) => !!v)
.map(([k, v]) => {
let attrToTranslate = attributesToExternalize.find(R.equals(k))
if (!attrToTranslate) return {}
let enTrad = attrToTranslate + '.en',
frTrad = attrToTranslate + '.fr'
let currentTranslation = currentExternalization[dottedName]
// Check if a human traduction exists already for this attribute and if
// it does need to be updated
if (
currentTranslation &&
currentTranslation[enTrad] &&
currentTranslation[frTrad] === v
)
return {
[enTrad]: currentTranslation[enTrad],
[frTrad]: v
}
if (['contrôles', 'suggestions'].includes(attrToTranslate)) {
return {
[frTrad]: v
}
}
missingTranslations.push([dottedName, enTrad, v])
return {
[frTrad]: v
}
})
)
}))
resolved = R.mergeAll(resolved)
return [missingTranslations, resolved]
}
module.exports = {
getMissingTranslations,
externalizationPath
}