Skip to content

Commit 014b3b6

Browse files
committed
Add translation sorting script.
1 parent e38abe5 commit 014b3b6

3 files changed

Lines changed: 133 additions & 5 deletions

File tree

package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,16 @@
3131
"test:update-all-desktop-snapshots": "CONFIG=hsl yarn test:update-snapshots && CONFIG=tampere yarn test:update-snapshots && CONFIG=matka yarn test:update-snapshots",
3232
"test:update-all-mobile-snapshots": "MOBILE=true CONFIG=hsl yarn test:update-snapshots && MOBILE=true CONFIG=tampere yarn test:update-snapshots && MOBILE=true CONFIG=matka yarn test:update-snapshots",
3333
"test:update-all-snapshots": "yarn test:update-all-desktop-snapshots && yarn test:update-all-mobile-snapshots",
34+
"lint": "yarn run sort-translations-check && yarn run es-lint && yarn run prettier-styles && yarn run stylelint",
35+
"format": "yarn run sort-translations-fix && yarn run es-lint-fix && yarn run prettier-styles-fix && yarn run stylelint-fix",
36+
"sort-translations-check": "node scripts/sort-translations.js --check",
37+
"sort-translations-fix": "node scripts/sort-translations.js --fix",
3438
"es-lint": "eslint .",
35-
"lint": "yarn run es-lint && yarn run prettier-styles && yarn run stylelint",
36-
"prettier-styles": "prettier sass/**/*.scss app/**/*.scss digitransit-component/**/*.scss --fix --write",
39+
"es-lint-fix": "eslint . --fix",
40+
"prettier-styles": "prettier sass/**/*.scss app/**/*.scss digitransit-component/**/*.scss --check",
41+
"prettier-styles-fix": "prettier sass/**/*.scss app/**/*.scss digitransit-component/**/*.scss --write",
3742
"stylelint": "stylelint sass/**/*.scss app/**/*.scss digitransit-component/**/*.scss",
43+
"stylelint-fix": "stylelint sass/**/*.scss app/**/*.scss digitransit-component/**/*.scss --fix",
3844
"build": "NODE_OPTIONS=--openssl-legacy-provider && NODE_ENV=production && yarn prebuild && node --max_old_space_size=4096 node_modules/.bin/webpack --progress --color",
3945
"build-workspaces": "yarn build-components && yarn build-search-utils && yarn build-store",
4046
"start": "NODE_ENV=production node $NODE_OPTS server/server",

scripts/README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1-
# Using scripts
1+
# Scripts
2+
3+
## Using `sort-translations.js`
4+
5+
This script sorts [`app/translations.js`](/app/translations.js).
6+
See the `sort-translations` and `format` scripts in [`package.json`](/package.json).
7+
8+
## Using `ui.sh`
29

310
See the `themeMap` in `app/configurations/config.default.js` for configuration options.
411

5-
## Before using
12+
### Before using
613
```
714
source ui.sh
815
```
9-
## Usage examples
16+
### Usage examples
1017

1118
Using remote instance of OTP with subscription key:
1219
```

scripts/sort-translations.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#!/usr/bin/env node
2+
/* eslint-disable no-console */
3+
4+
const fs = require('fs');
5+
const path = require('path');
6+
7+
const TRANSLATIONS_FILE = path.resolve(__dirname, '../app/translations.js');
8+
const PRINT_WIDTH = 80;
9+
const VALID_FLAGS = new Set(['--check', '--fix']);
10+
11+
function needsQuoting(key) {
12+
return !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key);
13+
}
14+
15+
function formatKey(key) {
16+
return needsQuoting(key) ? `'${key}'` : key;
17+
}
18+
19+
function formatValue(value) {
20+
const escaped = value
21+
.replace(/\\/g, '\\\\')
22+
.replace(/\n/g, '\\n')
23+
.replace(/\r/g, '\\r')
24+
.replace(/\t/g, '\\t');
25+
// Use double quotes when value contains a single quote to avoid escaping
26+
if (escaped.includes("'")) {
27+
return `"${escaped.replace(/"/g, '\\"')}"`;
28+
}
29+
return `'${escaped}'`;
30+
}
31+
32+
function sortedTranslationsFileContent(translations) {
33+
const lines = [];
34+
lines.push('/* eslint sort-keys: "error" */');
35+
lines.push('const translations = {');
36+
37+
const sortedLangs = Object.keys(translations).sort();
38+
for (let i = 0; i < sortedLangs.length; i++) {
39+
const lang = sortedLangs[i];
40+
lines.push(` ${formatKey(lang)}: {`);
41+
42+
const keys = Object.keys(translations[lang]).sort();
43+
for (let j = 0; j < keys.length; j++) {
44+
const key = keys[j];
45+
const formattedKey = formatKey(key);
46+
const formattedValue = formatValue(translations[lang][key]);
47+
const singleLine = ` ${formattedKey}: ${formattedValue},`;
48+
if (singleLine.length <= PRINT_WIDTH) {
49+
lines.push(singleLine);
50+
} else {
51+
lines.push(` ${formattedKey}:`);
52+
lines.push(` ${formattedValue},`);
53+
}
54+
}
55+
lines.push(' },');
56+
}
57+
58+
lines.push('};');
59+
lines.push('');
60+
lines.push('export default translations;');
61+
lines.push('');
62+
63+
return lines.join('\n');
64+
}
65+
66+
async function main() {
67+
try {
68+
console.log('---------- Running sort-translations.js script ----------');
69+
const flag = process.argv[2];
70+
if (!VALID_FLAGS.has(flag)) {
71+
console.error(
72+
'Missing or invalid mode. Use --check to verify sorting or --fix to write sorted translations.',
73+
);
74+
process.exitCode = 1;
75+
return;
76+
}
77+
78+
const translations = (await import(TRANSLATIONS_FILE)).default;
79+
const current = fs.readFileSync(TRANSLATIONS_FILE, 'utf-8');
80+
const sorted = sortedTranslationsFileContent(translations);
81+
82+
if (flag === '--check') {
83+
if (current === sorted) {
84+
console.log('CHECK mode - Translations are sorted:', TRANSLATIONS_FILE);
85+
} else {
86+
console.error(
87+
'CHECK mode - Translations are not sorted:',
88+
TRANSLATIONS_FILE,
89+
);
90+
process.exitCode = 1;
91+
}
92+
return;
93+
}
94+
95+
try {
96+
fs.writeFileSync(TRANSLATIONS_FILE, sorted, 'utf-8');
97+
console.log(
98+
'FIX mode - Sorted translations written to',
99+
TRANSLATIONS_FILE,
100+
);
101+
} catch (error) {
102+
const message = error instanceof Error ? error.message : String(error);
103+
console.error(
104+
`FIX - Failed to write sorted translations to ${TRANSLATIONS_FILE}: ${message}`,
105+
);
106+
process.exitCode = 1;
107+
}
108+
} catch (error) {
109+
const message = error instanceof Error ? error.message : String(error);
110+
console.error(`Failed to process translations: ${message}`);
111+
process.exitCode = 1;
112+
}
113+
}
114+
115+
main();

0 commit comments

Comments
 (0)