Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"file-loader": "^6.2.0",
"form-data": "^3.0.0",
"fs-extra": "^8.1.0",
"gettext-parser": "^3.0.0",
"handlebars": "^4.3.3",
"html-webpack-plugin": "^5.5.0",
"http-proxy": "^1.18.1",
Expand Down
52 changes: 51 additions & 1 deletion cli/src/lib/i18n/generate.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const path = require('path')
const { reporter, chalk } = require('@dhis2/cli-helpers-engine')
const fs = require('fs-extra')
const GettextParser = require('gettext-parser')
const handlebars = require('handlebars')
const { gettextToI18next } = require('i18next-conv')
const { checkDirectoryExists } = require('./helpers')
Expand Down Expand Up @@ -40,6 +41,50 @@
}
}

/**
* For .pot files, msgstr values for plural entries may be empty (standard
* template format, or after round-tripping through a translation platform).
* gettextToI18next converts empty msgstr to empty strings in the JSON, which
* causes i18next to miss these translations at runtime.
*
* This function fills in empty msgstr values with the source text (msgid for
* singular, msgid_plural for plural forms) so that the English locale always
* has usable translation values.
*/
const fillEmptyPotMsgstr = (potContent) => {

Check failure on line 54 in cli/src/lib/i18n/generate.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 23 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=dhis2_app-platform&issues=AZ02q91nbMJlKzbocK26&open=AZ02q91nbMJlKzbocK26&pullRequest=957
const parsed = GettextParser.po.parse(potContent)
let modified = false

for (const context of Object.values(parsed.translations)) {
for (const entry of Object.values(context)) {
if (!entry.msgid) {
continue
}

if (entry.msgid_plural) {
if (!entry.msgstr[0]) {
entry.msgstr[0] = entry.msgid
modified = true
}
if (!entry.msgstr[1]) {
entry.msgstr[1] = entry.msgid_plural
modified = true
}
} else {
if (!entry.msgstr[0]) {

Check warning on line 74 in cli/src/lib/i18n/generate.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'If' statement should not be the only statement in 'else' block

See more on https://sonarcloud.io/project/issues?id=dhis2_app-platform&issues=AZ02q91nbMJlKzbocK27&open=AZ02q91nbMJlKzbocK27&pullRequest=957
entry.msgstr[0] = entry.msgid
modified = true
}
}
}
}

if (modified) {
return GettextParser.po.compile(parsed).toString()
}
return potContent
}

const generate = async ({ input, output, namespace, paths }) => {
if (!checkDirectoryExists(input)) {
const relativeInput = './' + path.relative(paths.base, input)
Expand Down Expand Up @@ -88,7 +133,12 @@

if (ext === '.po' || ext === '.pot') {
const filePath = path.join(input, f)
const contents = fs.readFileSync(filePath, 'utf8')
let contents = fs.readFileSync(filePath, 'utf8')

if (ext === '.pot') {
contents = fillEmptyPotMsgstr(contents)
}

const json = await gettextToI18next(lang, contents)

const target = path.join(dst, lang)
Expand Down
Loading