🌐(i18n) create package i18n
We create a package i18n to manage the translations of the project. It help us to extract the translations from the frontend to be deployed to crowdin. It also help us to format the translations from crowdin to be used by the frontend apps.
This commit is contained in:
34
.github/workflows/people.yml
vendored
34
.github/workflows/people.yml
vendored
@@ -101,7 +101,7 @@ jobs:
|
||||
|
||||
test-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
@@ -254,29 +254,59 @@ jobs:
|
||||
- name: Run tests
|
||||
run: ~/.local/bin/pytest -n 2
|
||||
|
||||
i18n-back:
|
||||
i18n-crowdin:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install gettext (required to make messages)
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gettext
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Install development dependencies
|
||||
working-directory: src/backend
|
||||
run: pip install --user .[dev]
|
||||
|
||||
- name: Generate the translation base file
|
||||
run: ~/.local/bin/django-admin makemessages --keep-pot --all
|
||||
|
||||
- name: Load sops secrets
|
||||
uses: rouja/actions-sops@main
|
||||
with:
|
||||
secret-file: .github/workflows/secrets.enc.env
|
||||
age-key: ${{ secrets.SOPS_PRIVATE }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18.x'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: src/frontend/yarn.lock
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd src/frontend/ && yarn install --frozen-lockfile
|
||||
|
||||
- name: Download sources from Crowdin to stay synchronized
|
||||
run: |
|
||||
docker run \
|
||||
--rm \
|
||||
-e CROWDIN_API_TOKEN=$CROWDIN_API_TOKEN \
|
||||
-e CROWDIN_PROJECT_ID=$CROWDIN_PROJECT_ID \
|
||||
-e CROWDIN_BASE_PATH=$CROWDIN_BASE_PATH \
|
||||
-v "${{ github.workspace }}:/app" \
|
||||
crowdin/cli:3.16.0 \
|
||||
crowdin download sources -c /app/crowdin/config.yml
|
||||
|
||||
- name: Extract the frontend translation
|
||||
run: make frontend-i18n-extract
|
||||
|
||||
- name: Upload files to Crowdin
|
||||
run: |
|
||||
docker run \
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -78,3 +78,4 @@ db.sqlite3
|
||||
.idea/
|
||||
.vscode/
|
||||
*.iml
|
||||
.devcontainer/
|
||||
|
||||
29
Makefile
29
Makefile
@@ -53,7 +53,8 @@ MAIL_YARN = $(COMPOSE_RUN) -w /app/src/mail node yarn
|
||||
TSCLIENT_YARN = $(COMPOSE_RUN) -w /app/src/tsclient node yarn
|
||||
|
||||
# -- Frontend
|
||||
PATH_FRONT_DESK = ./src/frontend/apps/desk
|
||||
PATH_FRONT = ./src/frontend
|
||||
PATH_FRONT_DESK = $(PATH_FRONT)/apps/desk
|
||||
|
||||
# ==============================================================================
|
||||
# RULES
|
||||
@@ -84,7 +85,7 @@ bootstrap: \
|
||||
build \
|
||||
run \
|
||||
migrate \
|
||||
i18n-compile \
|
||||
back-i18n-compile \
|
||||
mails-install \
|
||||
mails-build \
|
||||
install-front-desk
|
||||
@@ -227,18 +228,24 @@ crowdin-download: ## Download translated message from crowdin
|
||||
@$(COMPOSE_RUN_CROWDIN) download -c crowdin/config.yml
|
||||
.PHONY: crowdin-download
|
||||
|
||||
crowdin-download-sources: ## Download sources from crowdin
|
||||
@$(COMPOSE_RUN_CROWDIN) download sources -c crowdin/config.yml
|
||||
.PHONY: crowdin-download-sources
|
||||
|
||||
crowdin-upload: ## Upload source translations to crowdin
|
||||
@$(COMPOSE_RUN_CROWDIN) upload sources -c crowdin/config.yml
|
||||
.PHONY: crowdin-upload
|
||||
|
||||
i18n-compile: ## compile all translations
|
||||
i18n-compile: \
|
||||
back-i18n-compile
|
||||
back-i18n-compile \
|
||||
frontend-i18n-compile
|
||||
.PHONY: i18n-compile
|
||||
|
||||
i18n-generate: ## create the .pot files and extract frontend messages
|
||||
i18n-generate: \
|
||||
back-i18n-generate
|
||||
back-i18n-generate \
|
||||
frontend-i18n-generate
|
||||
.PHONY: i18n-generate
|
||||
|
||||
i18n-download-and-compile: ## download all translated messages and compile them to be used by all applications
|
||||
@@ -301,3 +308,17 @@ install-front-desk: ## Install the frontend dependencies of app Desk
|
||||
run-front-desk: ## Start app Desk
|
||||
cd $(PATH_FRONT_DESK) && yarn dev
|
||||
.PHONY: run-front-desk
|
||||
|
||||
frontend-i18n-extract: ## Extract the frontend translation inside a json to be used for crowdin
|
||||
cd $(PATH_FRONT) && yarn i18n:extract
|
||||
.PHONY: frontend-i18n-extract
|
||||
|
||||
frontend-i18n-generate: ## Generate the frontend json files used for crowdin
|
||||
frontend-i18n-generate: \
|
||||
crowdin-download-sources \
|
||||
frontend-i18n-extract
|
||||
.PHONY: frontend-i18n-generate
|
||||
|
||||
frontend-i18n-compile: ## Format the crowin json files used deploy to the apps
|
||||
cd $(PATH_FRONT) && yarn i18n:deploy
|
||||
.PHONY: frontend-i18n-compile
|
||||
|
||||
@@ -14,10 +14,17 @@ preserve_hierarchy: true
|
||||
#
|
||||
# Files configuration
|
||||
#
|
||||
files: [
|
||||
{
|
||||
source : "/backend/locale/django.pot",
|
||||
dest: "/backend.pot",
|
||||
translation : "/backend/locale/%locale_with_underscore%/LC_MESSAGES/django.po"
|
||||
},
|
||||
]
|
||||
files:
|
||||
[
|
||||
{
|
||||
source: "/backend/locale/django.pot",
|
||||
dest: "/backend.pot",
|
||||
translation: "/backend/locale/%locale_with_underscore%/LC_MESSAGES/django.po",
|
||||
},
|
||||
{
|
||||
source: "/frontend/packages/i18n/locales/desk/translations-crowdin.json",
|
||||
dest: "/desk.json",
|
||||
translation: "/frontend/packages/i18n/locales/desk/%two_letters_code%/translations.json",
|
||||
skip_untranslated_strings: true,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,27 +1,18 @@
|
||||
{
|
||||
"extends": [
|
||||
"github>numerique-gouv/renovate-configuration"
|
||||
],
|
||||
"extends": ["github>numerique-gouv/renovate-configuration"],
|
||||
"dependencyDashboard": true,
|
||||
"packageRules": [
|
||||
{
|
||||
"enabled": false,
|
||||
"groupName": "ignored python dependencies",
|
||||
"matchManagers": [
|
||||
"pep621"
|
||||
],
|
||||
"matchManagers": ["pep621"],
|
||||
"matchPackageNames": []
|
||||
},
|
||||
{
|
||||
"enabled": false,
|
||||
"groupName": "ignored js dependencies",
|
||||
"matchManagers": [
|
||||
"npm"
|
||||
],
|
||||
"matchPackageNames": [
|
||||
"node",
|
||||
"node-fetch"
|
||||
]
|
||||
"matchManagers": ["npm"],
|
||||
"matchPackageNames": ["node", "node-fetch", "i18next-parser"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -11,13 +11,16 @@
|
||||
"scripts": {
|
||||
"APP_DESK": "yarn workspace app-desk",
|
||||
"APP_E2E": "yarn workspace app-e2e",
|
||||
"I18N": "yarn workspace packages-i18n",
|
||||
"app:dev": "yarn APP_DESK run dev",
|
||||
"app:start": "yarn APP_DESK run start",
|
||||
"app:build": "yarn APP_DESK run build",
|
||||
"app:test": "yarn APP_DESK run test",
|
||||
"ci:build": "yarn APP_DESK run build:ci",
|
||||
"e2e:test": "yarn APP_E2E run test",
|
||||
"lint": "yarn APP_DESK run lint && yarn APP_E2E run lint"
|
||||
"lint": "yarn APP_DESK run lint && yarn APP_E2E run lint",
|
||||
"i18n:extract": "yarn I18N run extract-translation",
|
||||
"i18n:deploy": "yarn I18N run format-deploy"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/node": "20.11.16",
|
||||
|
||||
1
src/frontend/packages/i18n/.gitignore
vendored
Normal file
1
src/frontend/packages/i18n/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
locales
|
||||
49
src/frontend/packages/i18n/format-deploy.mjs
Normal file
49
src/frontend/packages/i18n/format-deploy.mjs
Normal file
@@ -0,0 +1,49 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
import { hideBin } from "yargs/helpers";
|
||||
import yargs from "yargs/yargs";
|
||||
|
||||
// Get our args
|
||||
const argv = yargs(hideBin(process.argv)).argv;
|
||||
const { app, output } = argv;
|
||||
|
||||
const folderPath = "./locales/" + app;
|
||||
const namefile = "translations.json";
|
||||
const jsonI18n = {};
|
||||
|
||||
// Fetch the files in the locales folder
|
||||
fs.readdirSync(folderPath).map((language) => {
|
||||
const languagePath = path.join(folderPath, path.sep, language);
|
||||
// Crowdin output file in folder, we want to treat only these ones
|
||||
if (!fs.lstatSync(languagePath).isDirectory()) {
|
||||
return;
|
||||
}
|
||||
|
||||
jsonI18n[language] = {
|
||||
translation: {},
|
||||
};
|
||||
|
||||
// Get the json file generated by crowdin
|
||||
const pathTranslateFile = path.join(languagePath, path.sep, namefile);
|
||||
|
||||
if (!fs.existsSync(pathTranslateFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const json = JSON.parse(fs.readFileSync(pathTranslateFile, "utf8"));
|
||||
|
||||
// Transform the json file to the format expected by i18next
|
||||
const jsonKeyMessage = {};
|
||||
Object.keys(json).forEach((key) => {
|
||||
jsonKeyMessage[key] = json[key].message;
|
||||
});
|
||||
jsonI18n[language] = {
|
||||
translation: jsonKeyMessage,
|
||||
};
|
||||
});
|
||||
|
||||
// Write the file to the output
|
||||
fs.writeFileSync(output, JSON.stringify(jsonI18n), "utf8");
|
||||
|
||||
console.log(`${app} translations deployed!`);
|
||||
@@ -0,0 +1,9 @@
|
||||
const config = {
|
||||
customValueTemplate: {
|
||||
message: '${key}',
|
||||
description: '${description}',
|
||||
},
|
||||
keepRemoved: false,
|
||||
};
|
||||
|
||||
export default config;
|
||||
9
src/frontend/packages/i18n/i18next-parser.config.mjs
Normal file
9
src/frontend/packages/i18n/i18next-parser.config.mjs
Normal file
@@ -0,0 +1,9 @@
|
||||
const config = {
|
||||
customValueTemplate: {
|
||||
message: "${key}",
|
||||
description: "${description}",
|
||||
},
|
||||
keepRemoved: true,
|
||||
};
|
||||
|
||||
export default config;
|
||||
15
src/frontend/packages/i18n/package.json
Normal file
15
src/frontend/packages/i18n/package.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "packages-i18n",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"extract-translation": "yarn extract-translation:desk",
|
||||
"extract-translation:desk": "yarn i18next ../../apps/desk/**/*.{ts,tsx} -c ./i18next-parser.config.mjs -o ./locales/desk/translations-crowdin.json",
|
||||
"format-deploy": "yarn format-deploy:desk",
|
||||
"format-deploy:desk": "node ./format-deploy.mjs --app=desk --output=../../apps/desk/src/i18n/translations.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"i18next-parser": "8.8.0",
|
||||
"yargs": "17.7.2"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user