|
| 1 | +#!/usr/bin/env node |
| 2 | +/* eslint-disable no-console */ |
| 3 | + |
| 4 | +import fs from 'fs'; |
| 5 | +import path from 'path'; |
| 6 | + |
| 7 | +const TRANSLATIONS_DIR = path.resolve( |
| 8 | + import.meta.dirname, |
| 9 | + '../app/translations', |
| 10 | +); |
| 11 | +const PRINT_WIDTH = 80; |
| 12 | + |
| 13 | +function needsQuoting(key) { |
| 14 | + return !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key); |
| 15 | +} |
| 16 | + |
| 17 | +function formatKey(key) { |
| 18 | + return needsQuoting(key) ? `'${key}'` : key; |
| 19 | +} |
| 20 | + |
| 21 | +function formatValue(value) { |
| 22 | + const escaped = value |
| 23 | + .replace(/\\/g, '\\\\') |
| 24 | + .replace(/\n/g, '\\n') |
| 25 | + .replace(/\r/g, '\\r') |
| 26 | + .replace(/\t/g, '\\t'); |
| 27 | + // Use double quotes when value contains a single quote to avoid escaping |
| 28 | + if (escaped.includes("'")) { |
| 29 | + return `"${escaped.replace(/"/g, '\\"')}"`; |
| 30 | + } |
| 31 | + return `'${escaped}'`; |
| 32 | +} |
| 33 | + |
| 34 | +function sortedTranslationsFileContent(lang, translations) { |
| 35 | + const lines = []; |
| 36 | + lines.push('/* eslint sort-keys: "error" */'); |
| 37 | + lines.push('export default {'); |
| 38 | + lines.push(` ${formatKey(lang)}: {`); |
| 39 | + |
| 40 | + const keys = Object.keys(translations).sort(); |
| 41 | + for (let j = 0; j < keys.length; j++) { |
| 42 | + const key = keys[j]; |
| 43 | + const formattedKey = formatKey(key); |
| 44 | + const formattedValue = formatValue(translations[key]); |
| 45 | + const singleLine = ` ${formattedKey}: ${formattedValue},`; |
| 46 | + if (singleLine.length <= PRINT_WIDTH) { |
| 47 | + lines.push(singleLine); |
| 48 | + } else { |
| 49 | + lines.push(` ${formattedKey}:`); |
| 50 | + lines.push(` ${formattedValue},`); |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + lines.push(' },'); |
| 55 | + lines.push('};'); |
| 56 | + lines.push(''); |
| 57 | + |
| 58 | + return lines.join('\n'); |
| 59 | +} |
| 60 | + |
| 61 | +async function main() { |
| 62 | + try { |
| 63 | + console.log('---------- Running sort-translations.mjs script ----------'); |
| 64 | + |
| 65 | + const files = fs |
| 66 | + .readdirSync(TRANSLATIONS_DIR) |
| 67 | + .filter(f => f.endsWith('.js')) |
| 68 | + .sort(); |
| 69 | + |
| 70 | + for (let i = 0; i < files.length; i++) { |
| 71 | + const file = files[i]; |
| 72 | + const filePath = path.join(TRANSLATIONS_DIR, file); |
| 73 | + console.log('Processing file', filePath); |
| 74 | + // eslint-disable-next-line no-await-in-loop |
| 75 | + const module = await import(filePath); |
| 76 | + const langData = module.default; |
| 77 | + const lang = Object.keys(langData)[0]; |
| 78 | + const sorted = sortedTranslationsFileContent(lang, langData[lang]); |
| 79 | + |
| 80 | + try { |
| 81 | + fs.writeFileSync(filePath, sorted, 'utf-8'); |
| 82 | + console.log('Sorted translations written to', filePath); |
| 83 | + } catch (error) { |
| 84 | + const message = error instanceof Error ? error.message : String(error); |
| 85 | + console.error( |
| 86 | + `Failed to write sorted translations to ${filePath}: ${message}`, |
| 87 | + ); |
| 88 | + process.exitCode = 1; |
| 89 | + } |
| 90 | + } |
| 91 | + } catch (error) { |
| 92 | + const message = error instanceof Error ? error.message : String(error); |
| 93 | + console.error(`Failed to process translations: ${message}`); |
| 94 | + process.exitCode = 1; |
| 95 | + } |
| 96 | +} |
| 97 | + |
| 98 | +main(); |
0 commit comments