Skip to content

Commit

Permalink
Update i18n tooling (#1502)
Browse files Browse the repository at this point in the history
* update en translation files

* Upgrade i18next parser

* Revert "update en translation files"

This reverts commit 88ccdc9.

* Update i18n tooling scripts

* Update the i18n automation scripts

* Extract locale files for added packages
  • Loading branch information
peterMuriuki authored Dec 5, 2024
1 parent 0cb79b2 commit 03fbfc0
Show file tree
Hide file tree
Showing 15 changed files with 4,356 additions and 334 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"flush-promises": "^1.0.2",
"glob": "^8.0.1",
"husky": "^8.0.0",
"i18next-parser": "^5.4.0",
"i18next-parser": "^9.0.2",
"jest": "27.5.1",
"jest-canvas-mock": "^2.5.2",
"jest-environment-jsdom-sixteen": "^2.0.0",
Expand Down
20 changes: 20 additions & 0 deletions packages/i18n/locales/eusm/fhir-flag/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"(Auto generated)": "",
"Active": "",
"An error occurred while fetching the inventory": "",
"Close App": "",
"Error Occurred When Closing Flag": "",
"Error occurred while trying to fetch flag data": "",
"Error occurred while trying to fetch practitioners data": "",
"Flag Closed successfully": "",
"How was the flag resolved?": "",
"Inactive": "",
"Invalid Flag": "",
"Missing location field. This information is required to close the flag form.": "",
"Product": "",
"Save": "",
"Saving": "",
"Select flag status": "",
"Service Point": "",
"Status": ""
}
33 changes: 33 additions & 0 deletions packages/i18n/locales/eusm/fhir-import/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"Actions": "",
"An error occurred while fetching the csv templates": "",
"Author": "",
"Bulk upload": "",
"Check for any data inconsistencies or errors (e.g. missing values, incorrect data types) before uploading": "",
"Click the \"Attach\" button to select your prepared data file.": "",
"Click the button below to download the bulk upload template file(s).": "",
"Data import started successfully": "",
"Data imports": "",
"Date created": "",
"Date Created": "",
"Date Ended": "",
"Date Started": "",
"Download Template": "",
"Enter your data into the template file. Ensure all required fields are filled and follow the specified format(e.g. date format)": "",
"Failed to refresh data, please refresh the page": "",
"File name": "",
"Follow these simple instructions to help you prepare, upload, and verify your data smoothly and efficiently": "",
"Log Output": "",
"Once the file or files are selected, click \"Start Import\" to begin the upload": "",
"Prepare your data file": "",
"Resource upload": "",
"Start Import": "",
"status": "",
"Step by step guide for bulk upload": "",
"Upload your data file": "",
"Uploading": "",
"View": "",
"View details | {{workflowId}}": "",
"Workflow Id": "",
"Workflow type": ""
}
1 change: 1 addition & 0 deletions scripts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.yarn
7 changes: 7 additions & 0 deletions scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# scripts

Home of tiny bespoke and lightweight utility code that automates common code maintenance and shipping tasks.

These include:

1. i18n - cli utility that one can use to manage internationalization tasks on the repository
72 changes: 72 additions & 0 deletions scripts/i18n/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# I!8n Script

Automates i18n tasks across the repository i.e extracting, and updating translation string.

## Features

Read more on how fhir-web structures internationalization in this [guide]()

**Extract Translations**

```shell
# pwd is repo root.
cd scripts

# enable corepack to better manage node js package managers
corepack enable

# install packages
yarn install

# Extract all translatable strings to i18n package, eusm project to both english and french resource files
./i18n/cli.js extract --project eusm -l en,fr
# Extract translatable strings from the fhir-clients package to i18n package > eusm project > fhir-clients namespace> english resource file
./i18n/cli.js extract fhir-clients --project eusm -l en
```

Consider a scenario where you have a view say `fhir-location-management.ListView` that is re-used in more than two places with different semantics. Now how would one support translations if the 2 views should show different set of texts within the same language.

We use the concept of namespaces. Pass a namespace config to the ListView i18n configuration that determines which texts to pick even within the same language. Furthermore we regenerate and store a copy of the translation strings under the new namespace. The end results is 2 namespaces that are usable from the same module but that define different string translations for the same lookup text.

To generate a new namespace for a module

```shell
# Extract translatable strings from the fhir-locations package to i18n package > eusm project> fhir-service-points namespace > english resource file
./i18n/cli.js extract fhir-locations --project eusm -l en --output-namespace fhir-service-points
```

**Download Translations**

Merges previously extracted translations into a single duplicate-free translation file. This file can then be uploaded to translation services like Transifex for translation.

```shell
# pwd is repo root.
cd scripts

# enable corepack to better manage node js package managers
corepack enable

# install packages
yarn install

# Downloads all extracted strings in all the generated english resource files in the eusm project.
./i18n/cli.js download --project eusm -l en
```

**Upload Translations**

Undoes a download operation. Takes a translated resource file, unravels the translations into the individual resource files in the i18n package.

```shell
# pwd is repo root.
cd scripts

# enable corepack to better manage node js package managers
corepack enable

# install packages
yarn install

# Downloads all extracted strings in all the generated english resource files in the eusm project.
./i18n/cli.js upload --project eusm -l en -tfile <file-path-to-resource-file>
```
110 changes: 110 additions & 0 deletions scripts/i18n/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#!/usr/bin/env node
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { downloadStrings } from './lib/downloadMerged.js';
import { uploadTranslations } from './lib/uploadMerged.js';
import { runExtractions, supportedProjectCodes, supportedLocaleCodes } from './lib/extract.js';

// Define global options and configure commands
yargs(hideBin(process.argv))
.options({
project: {
alias: 'p',
describe: 'Code for project that own/consume translations',
choices: supportedProjectCodes,
default: 'core',
},
})
.command(
'extract [packages...]',
'Gets all translatable strings and writes them to the i18n package',
(yargs) => {
yargs
.positional('packages', {
describe: 'A list of folders from which to parse for translatable strings',
type: 'string',
demandOption: true,
})
.options({
locales: {
alias: 'l',
describe: 'Extracted strings will be generated for this locale(s)',
choices: supportedLocaleCodes,
type: 'array',
default: ['en'],
},
'key-as-default': {
default: false,
describe: 'Duplicate key to also be value',
type: 'boolean',
alias: 'k',
},
preserve: {
default: false,
describe: 'Retain removed translations in separate json files',
type: 'boolean',
alias: 'pr',
},
'output-namespace': {
default: false,
describe:
'Manually override the namespace/package under which the translations will be written to',
type: 'string',
alias: 'on',
},
});
},
async (argv) => {
console.log({ argv });
await runExtractions(argv);
}
)
.command(
'download',
'Merges all generated translatable strings into a single file',
(yargs) => {
yargs.options({
out: {
describe: 'Where to write the merged strings into',
type: 'string',
},
locale: {
choices: supportedLocaleCodes,
describe: 'Locale for which to download translation files for',
type: 'string',
default: ['en'],
},
});
},
async (argv) => {
const { project, out, locale } = argv;
await downloadStrings(project, locale, out);
}
)
.command(
'upload',
'Takes a merged translation file and expands the strings into the i18n package',
(yargs) => {
yargs.options({
tfile: {
describe: 'File with merged translations',
type: 'string',
demandOption: true,
},
locale: {
alias: 'l',
describe: 'Upload strings for this locale',
choices: supportedLocaleCodes,
default: ['en'],
},
});
},
async (argv) => {
const { project, tfile, locale } = argv;
await uploadTranslations(tfile, project, locale);
}
)
.demandCommand(1, 'You need to specify at least one command')
.help()
.strict()
.parse();
38 changes: 38 additions & 0 deletions scripts/i18n/lib/downloadMerged.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import path from 'path';
import glob from 'glob';
import { promisify } from 'util';
import * as fs from 'fs';
import { getLocaleFilePaths, REPO_ROOT_PATH } from './utils.js';

const promisedGlob = promisify(glob);

/** parses through an individual translation file gets the translation string and
* appends them to the hashmap store.
* @param {string} filePath - file path for the translation file
* @param {Record<string, string>} unifiedJson - The hashmap store
*/
function processFile(filePath, unifiedJson) {
const qualifiedPath = path.resolve(REPO_ROOT_PATH, filePath);
const contents = JSON.parse(fs.readFileSync(qualifiedPath, 'utf-8'));
Object.entries(contents).forEach(([key, value]) => {
if (!value) {
unifiedJson[key] = key;
} else {
if (key !== value) {
unifiedJson[value] = value;
}
unifiedJson[key] = value;
}
});
}

export async function downloadStrings(projectCode = 'core', locale = 'en', outFile = undefined) {
const unifiedJson = {};
const resourceFiles = await getLocaleFilePaths(projectCode, locale);
for (const resource of resourceFiles) {
processFile(resource, unifiedJson);
}
const mergedTranslationFilePath =
outFile ?? path.resolve(REPO_ROOT_PATH, `fhir-web-${projectCode}-${locale}.json`);
fs.writeFileSync(mergedTranslationFilePath, JSON.stringify(unifiedJson, undefined, 2));
}
Loading

0 comments on commit 03fbfc0

Please sign in to comment.