Updating translations and adding a translation tracker
This commit is contained in:
@@ -32,6 +32,19 @@ services:
|
||||
- "traefik.http.routers.gui.rule=PathPrefix(`/`)"
|
||||
- "traefik.http.services.gui.loadbalancer.server.port=5173"
|
||||
|
||||
i18n:
|
||||
build:
|
||||
context: ./i18n
|
||||
restart: always
|
||||
volumes:
|
||||
- ./i18n/app/src:/app/src
|
||||
- ./gui/rpk-gui/public:/app/public
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.i18n.entrypoints=web"
|
||||
- "traefik.http.routers.i18n.rule=PathPrefix(`/locales/add`)"
|
||||
- "traefik.http.services.i18n.loadbalancer.server.port=8100"
|
||||
|
||||
proxy:
|
||||
image: traefik:latest
|
||||
restart: always
|
||||
|
||||
@@ -73,8 +73,8 @@
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"info": "You may have forgotten to add the {{action}} component to {{resource}} resource.",
|
||||
"404": "Sorry, the page you visited does not exist.",
|
||||
"info": "You may have forgotten to add the {{action}} component to {{resource}} resource.",
|
||||
"resource404": "Are you sure you have created the {{resource}} resource.",
|
||||
"backHome": "Back Home"
|
||||
}
|
||||
@@ -163,5 +163,90 @@
|
||||
"error": "auto save failure",
|
||||
"loading": "saving...",
|
||||
"idle": "waiting for changes"
|
||||
},
|
||||
"undefined": {
|
||||
"undefined": "No translation",
|
||||
"titles": {
|
||||
"list": "No translation"
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"individual": {
|
||||
"type": "Individual",
|
||||
"lastname": "Lastname",
|
||||
"surnames": "Surname",
|
||||
"day_of_birth": "Date of birth",
|
||||
"firstname": "Firstname",
|
||||
"place_of_birth": "Place of birth",
|
||||
"middlename": "Middlename"
|
||||
},
|
||||
"corporation": {
|
||||
"type": "Corporation",
|
||||
"activity": "Activity",
|
||||
"title": "Title",
|
||||
"employees": "Employees"
|
||||
},
|
||||
"employee": {
|
||||
"position": "Position",
|
||||
"entity_id": "Identity"
|
||||
},
|
||||
"institution": {
|
||||
"type": "Institution",
|
||||
"title": "Title",
|
||||
"activity": "Activity",
|
||||
"employees": "Employees"
|
||||
},
|
||||
"entity": {
|
||||
"entity_data": "Informations",
|
||||
"address": "Address"
|
||||
},
|
||||
"provision_template": {
|
||||
"name": "Name",
|
||||
"title": "Title",
|
||||
"body": "Body"
|
||||
},
|
||||
"contract_template": {
|
||||
"name": "Name",
|
||||
"title": "Title",
|
||||
"provisions": "Provisions",
|
||||
"parties": "Parties",
|
||||
"variables": "Variables"
|
||||
},
|
||||
"party_template": {
|
||||
"entity_id": "Party Template",
|
||||
"representative_id": "Representative",
|
||||
"part": "Part"
|
||||
},
|
||||
"provision_template_reference": {
|
||||
"provision_template_id": "Provision Template"
|
||||
},
|
||||
"dictionary_entry": {
|
||||
"key": "Variable",
|
||||
"value": "Value"
|
||||
},
|
||||
"contract_draft": {
|
||||
"name": "Name",
|
||||
"title": "Title",
|
||||
"parties": "Parties",
|
||||
"provisions": "Provisions",
|
||||
"variables": "Variables"
|
||||
},
|
||||
"draft_party": {
|
||||
"entity_id": "Client",
|
||||
"part": "Part",
|
||||
"representative_id": "Representative"
|
||||
},
|
||||
"contract_provision_template_reference": {
|
||||
"provision_template_id": "Provision Template",
|
||||
"type": "Type"
|
||||
},
|
||||
"provision_genuine": {
|
||||
"title": "Title",
|
||||
"body": "Body",
|
||||
"type": "Type"
|
||||
},
|
||||
"draft_provision": {
|
||||
"provision": "Provision"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +73,8 @@
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"info": "Il manque l'action {{action}} component à la ressource {{resource}} .",
|
||||
"404": "Cette page n'existe pas.",
|
||||
"info": "Il manque l'action {{action}} component à la ressource {{resource}} .",
|
||||
"resource404": "Cette page n'existe pas.",
|
||||
"backHome": "Retour à l'accueil"
|
||||
}
|
||||
@@ -163,5 +163,90 @@
|
||||
"error": "Sauvegarde automatique ratée",
|
||||
"loading": "Sauvegarde...",
|
||||
"idle": "En attente de modification"
|
||||
},
|
||||
"undefined": {
|
||||
"undefined": "No translation",
|
||||
"titles": {
|
||||
"list": "No translation"
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"individual": {
|
||||
"type": "Particulier",
|
||||
"middlename": "Autres prénoms",
|
||||
"lastname": "Nom",
|
||||
"firstname": "Prénom",
|
||||
"day_of_birth": "Date de naissance",
|
||||
"surnames": "Surnoms",
|
||||
"place_of_birth": "Lieu de naissance"
|
||||
},
|
||||
"corporation": {
|
||||
"type": "Entreprise",
|
||||
"title": "Titre",
|
||||
"activity": "Activité",
|
||||
"employees": "Employés"
|
||||
},
|
||||
"employee": {
|
||||
"entity_id": "Identité",
|
||||
"position": "Poste"
|
||||
},
|
||||
"institution": {
|
||||
"type": "Institution",
|
||||
"title": "Titre",
|
||||
"employees": "Employés",
|
||||
"activity": "Activité"
|
||||
},
|
||||
"entity": {
|
||||
"entity_data": "Informations",
|
||||
"address": "Adresse"
|
||||
},
|
||||
"provision_template": {
|
||||
"name": "Nom",
|
||||
"body": "Corps",
|
||||
"title": "Titre"
|
||||
},
|
||||
"contract_template": {
|
||||
"name": "Nom",
|
||||
"title": "Titre",
|
||||
"parties": "Parties",
|
||||
"provisions": "Clauses",
|
||||
"variables": "Variables"
|
||||
},
|
||||
"party_template": {
|
||||
"entity_id": "Entité",
|
||||
"part": "Rôle",
|
||||
"representative_id": "Représentant"
|
||||
},
|
||||
"provision_template_reference": {
|
||||
"provision_template_id": "Template de clause"
|
||||
},
|
||||
"dictionary_entry": {
|
||||
"key": "Variable",
|
||||
"value": "Valeur"
|
||||
},
|
||||
"contract_draft": {
|
||||
"name": "Nom",
|
||||
"parties": "Parties",
|
||||
"title": "Titre",
|
||||
"provisions": "Clauses",
|
||||
"variables": "Variables"
|
||||
},
|
||||
"draft_party": {
|
||||
"part": "Rôle",
|
||||
"representative_id": "Représentant",
|
||||
"entity_id": "Entité"
|
||||
},
|
||||
"contract_provision_template_reference": {
|
||||
"type": "Template",
|
||||
"provision_template_id": "Template de clause"
|
||||
},
|
||||
"provision_genuine": {
|
||||
"type": "Personalisée",
|
||||
"title": "Titre",
|
||||
"body": "Corps"
|
||||
},
|
||||
"draft_provision": {
|
||||
"provision": "Clause"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,9 @@ i18n
|
||||
.init({
|
||||
supportedLngs: ["EN", "FR"],
|
||||
backend: {
|
||||
loadPath: "/locales/{{lng}}/{{ns}}.json", // locale files path
|
||||
loadPath: "/locales/{{lng}}/{{ns}}.json", // "http/locales/{{lng}}/{{ns}}.json"
|
||||
},
|
||||
//saveMissing: true,
|
||||
ns: ["common"],
|
||||
defaultNS: "common",
|
||||
fallbackLng: ["EN", "FR"],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { JSONSchema7Definition } from "json-schema";
|
||||
import { RJSFSchema } from '@rjsf/utils';
|
||||
import i18n from '../../../i18n'
|
||||
|
||||
const API_URL = "/api/v1";
|
||||
|
||||
@@ -19,6 +19,10 @@ const getJsonschema = async (): Promise<RJSFSchema> => {
|
||||
return rawSchema;
|
||||
}
|
||||
|
||||
function convertCamelToSnake(str: string): string {
|
||||
return str.replace(/([a-zA-Z])(?=[A-Z])/g,'$1_').toLowerCase()
|
||||
}
|
||||
|
||||
function buildResource(rawSchemas: RJSFSchema, resourceName: string) {
|
||||
let resource;
|
||||
|
||||
@@ -45,6 +49,12 @@ function buildResource(rawSchemas: RJSFSchema, resourceName: string) {
|
||||
} else if (is_array(prop) && is_reference(prop.items)) {
|
||||
resolveReference(rawSchemas, resource, prop.items);
|
||||
}
|
||||
|
||||
if (prop.hasOwnProperty("title")) {
|
||||
const shortResourceName = convertCamelToSnake(resourceName.replace(/(-Input|Create|Update)$/g, ""));
|
||||
prop.title = i18n.t(`schemas.${shortResourceName}.${convertCamelToSnake(prop_name)}`, prop.title);
|
||||
console.log(`schemas.${shortResourceName}.${convertCamelToSnake(prop_name)}`);
|
||||
}
|
||||
}
|
||||
|
||||
return resource;
|
||||
@@ -161,7 +171,6 @@ function path_exists(rawSchemas: RJSFSchema, resource: RJSFSchema, path: string)
|
||||
return has_descendant(rawSchemas, resource, path);
|
||||
}
|
||||
|
||||
|
||||
return has_descendant(rawSchemas, resource, path.substring(0, pointFirstPosition))
|
||||
&& path_exists(
|
||||
rawSchemas,
|
||||
|
||||
11
i18n/Dockerfile
Normal file
11
i18n/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM node:lts-alpine
|
||||
|
||||
WORKDIR /app
|
||||
COPY app/package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY app/tsconfig*.json ./
|
||||
COPY app/src ./src
|
||||
EXPOSE 8100
|
||||
|
||||
CMD [ "npm", "--watch", "start" ]
|
||||
26
i18n/app/.gitignore
vendored
Normal file
26
i18n/app/.gitignore
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# Logs
|
||||
|
||||
logs
|
||||
_.log
|
||||
npm-debug.log_
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
\*.local
|
||||
|
||||
# Editor directories and files
|
||||
|
||||
.vscode/_
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
_.suo
|
||||
_.ntvs_
|
||||
_.njsproj
|
||||
_.sln
|
||||
\*.sw?
|
||||
1164
i18n/app/package-lock.json
generated
Normal file
1164
i18n/app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
i18n/app/package.json
Normal file
25
i18n/app/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "i18n Helper",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "ts-node src/index.ts",
|
||||
"build": "tsc",
|
||||
"serve": "node dist/index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"devDependencies": {
|
||||
"@types/express": "^5.0.1",
|
||||
"@types/node": "^22.15.3",
|
||||
"express": "^5.1.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"body-parser": "^2.2.0"
|
||||
}
|
||||
}
|
||||
49
i18n/app/src/index.ts
Normal file
49
i18n/app/src/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
import bodyParser from "body-parser";
|
||||
|
||||
import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'fs';
|
||||
|
||||
|
||||
|
||||
const app = express();
|
||||
app.use(bodyParser.json());
|
||||
const port = process.env.PORT || 8100;
|
||||
|
||||
app.post('/locales/add/:lng/:ns', (req: Request, res: Response) => {
|
||||
const keyPath = Object.keys(req.body)[0];
|
||||
|
||||
const basePath = "public/locales";
|
||||
const lgPath = `${basePath}/${req.params.lng}`;
|
||||
if (!existsSync(lgPath)) {
|
||||
mkdirSync(lgPath);
|
||||
}
|
||||
const filePath = `${lgPath}/common.json`;
|
||||
|
||||
let missingTrans;
|
||||
try {
|
||||
missingTrans = JSON.parse(readFileSync(filePath, 'utf8'));
|
||||
} catch(err) {
|
||||
missingTrans = JSON.parse("{}");
|
||||
}
|
||||
|
||||
let current = missingTrans
|
||||
const splitPath = keyPath.split(".");
|
||||
for (let i=0; i < splitPath.length; i++) {
|
||||
const key = splitPath[i];
|
||||
if (! current.hasOwnProperty(key)) {
|
||||
if (i + 1 == splitPath.length) {
|
||||
current[key] = "No translation";
|
||||
} else {
|
||||
current[key] = JSON.parse("{}");
|
||||
}
|
||||
}
|
||||
current = current[key];
|
||||
}
|
||||
writeFileSync(filePath, JSON.stringify(missingTrans, null, 2))
|
||||
|
||||
res.send("OK");
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server running at http://localhost:${port}`);
|
||||
});
|
||||
13
i18n/app/tsconfig.json
Normal file
13
i18n/app/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"outDir": "./dist",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user