diff --git a/locales/de/translation.json b/locales/de/translation.json index 24efd75037d4a9192960d1da5e26a371b7a93895..3f2933b16e46259186f12a8c6a81b8ba6b091402 100644 --- a/locales/de/translation.json +++ b/locales/de/translation.json @@ -2,10 +2,10 @@ "translation": { "aboutThisItem": "Über dieses Element", "add": "Hinzufügen", + "addedFromUrl": "(via URL hinzugefügt)", "addManifestUrl": "Ort der Ressource", "addManifestUrlHelp": "Die URL einer IIIF-Ressource", "addResource": "Ressource hinzufügen", - "addedFromUrl": "(via URL hinzugefügt)", "annotations": "Annotationen", "book": "Buch", "bottom": "Unten", @@ -13,9 +13,9 @@ "canvasIndex": "Index", "closeAddResourceMenu": "Resourcenliste schließen", "closeAnnotationCompanionWindow": "Annotationsfenster schließen", + "closeCanvasNavigationCompanionWindow": "Hilfsfenster für die Leinwandnavigation schließen", "closeCompanionWindow": "Hilfsfenster schließen", "closeInfoCompanionWindow": "Informationshilfsfenster schließen", - "closeCanvasNavigationCompanionWindow": "Hilfsfenster für die Leinwandnavigation schließen", "closeWindow": "Fenster schließen", "closeWindowMenu": "Fenstermenü schließen", "currentItem": "Objekt", @@ -37,22 +37,22 @@ "numItems": "{{number}} Elemente", "off": "Keine", "openAnnotationCompanionWindow": "Annotationsfenster öffnen", - "openInfoCompanionWindow": "Informationshilfsfenster", "openCanvasNavigationCompanionWindow": "Hilfsfenster für die Leinwandnavigation schließen", - "openWindows": "Fenster öffnen", "openInCompanionWindow": "In Hilfsfenster öffnen", + "openInfoCompanionWindow": "Informationshilfsfenster", + "openWindows": "Fenster öffnen", "position": "Position", "previewWindowTitle": "Mirador", "previousCanvas": "Vorherigen Objekt", "resource": "Ressource", "right": "Rechts", - "single": "Einzeln", "settings": "Einstellungen", - "showZoomControls": "Zoomsteuerung anzeigen", "showingNumAnnotations": "{{number}} Annotationen werden angezeigt", + "showZoomControls": "Zoomsteuerung anzeigen", + "single": "Einzeln", "theme": "Farbschema", - "thumbnails": "Miniaturansicht", "thumbnailNavigation": "Miniaturansicht", + "thumbnails": "Miniaturansicht", "toggleWindowSideBar": "Seitenleiste umschalten", "tryAgain": "Wiederholen", "untitled": "[Unbenannt]", diff --git a/locales/en/translation.json b/locales/en/translation.json index e4a3d5451b5047146e7b03bcb5bc4396e922a8f1..a926afd4463fa86adb58cd49798dff8364fcfaec 100644 --- a/locales/en/translation.json +++ b/locales/en/translation.json @@ -2,10 +2,10 @@ "translation": { "aboutThisItem": "About this item", "add": "Add", + "addedFromUrl": "(Added from URL)", "addManifestUrl": "Resource location", "addManifestUrlHelp": "The URL of a IIIF resource", "addResource": "Add resource", - "addedFromUrl": "(Added from URL)", "annotations": "Annotations", "book": "Book", "bottom": "Bottom", @@ -13,9 +13,9 @@ "canvasIndex": "Index", "closeAddResourceMenu": "Close add resource panel", "closeAnnotationCompanionWindow": "Close annotation companion window", + "closeCanvasNavigationCompanionWindow": "Close canvas navigation companion window", "closeCompanionWindow": "Close this companion window", "closeInfoCompanionWindow": "Close information companion window", - "closeCanvasNavigationCompanionWindow": "Close canvas navigation companion window", "closeWindow": "Close window", "closeWindowMenu": "Close window options menu", "currentItem": "Current item", @@ -32,31 +32,31 @@ "light": "Light", "listAllOpenWindows": "List all open windows", "manifestError": "The resource cannot be added:", + "menu": "Menu", "miradorResources": "Mirador resources", "miradorViewer": "Mirador viewer", - "nextCanvas": "Next item", - "menu": "Menu", "mosaic": "Mosaic", + "nextCanvas": "Next item", "numItems": "{{number}} items", "off": "Off", "openAnnotationCompanionWindow": "Open annotation companion window", - "openInfoCompanionWindow": "Open information companion window", "openCanvasNavigationCompanionWindow": "Open canvas navigation companion window", - "openWindows": "Open windows", "openInCompanionWindow": "Open in companion window", + "openInfoCompanionWindow": "Open information companion window", + "openWindows": "Open windows", "position": "Position", "previewWindowTitle": "Mirador", "previousCanvas": "Previous item", "resource": "Resource", "right": "Right", - "single": "Single", + "selectWorkspaceMenu": "Select workspace type", "settings": "Settings", - "showZoomControls": "Show Zoom Controls", "showingNumAnnotations": "Showing {{number}} annotations", - "selectWorkspaceMenu": "Select workspace type", + "showZoomControls": "Show Zoom Controls", + "single": "Single", "theme": "Theme", - "thumbnails": "Thumbnails", "thumbnailNavigation": "Thumbnail carousel", + "thumbnails": "Thumbnails", "toggleWindowSideBar": "Toggle window sidebar", "tryAgain": "Try again", "untitled": "[Untitled]", @@ -64,10 +64,9 @@ "window": "Window: {{label}}", "windowMenu": "Window menu", "workspace": "Workspace", - "workspaceMenu": "Workspace menu", "workspaceFullScreen": "Full Screen", + "workspaceMenu": "Workspace menu", "workspaceSelectionTitle": "Select a workspace type", - "workspace": "Workspace", "zoomIn": "Zoom in", "zoomOut": "Zoom out", "zoomReset": "Reset zoom" diff --git a/package.json b/package.json index 26bbfe63c2e506eb838cbcb4e2a177687a9997b3..96013af403b7eb39db645b75c96c5aceb2a7fe68 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dist" ], "scripts": { - "lint": "node_modules/.bin/eslint ./ && node_modules/.bin/sass-lint -v ./src/styles/**/*", + "lint": "node_modules/.bin/eslint ./ && node_modules/.bin/sass-lint -v ./src/styles/**/* && node ./scripts/i18n-lint.js", "server": "node_modules/.bin/http-server", "test": "npm run build && npm run lint && npm run size && jest -c jest.json", "test:watch": "jest -c jest.json --watch", @@ -71,6 +71,7 @@ "babel-eslint": "10.0.1", "babel-jest": "^24.1.0", "babel-loader": "^8.0.4", + "chalk": "^2.4.2", "codecov": "^3.1.0", "concurrently": "^4.0.1", "css-loader": "^2.1.0", @@ -85,6 +86,7 @@ "eslint-plugin-jest": "^22.2.2", "eslint-plugin-jsx-a11y": "^6.2.0", "eslint-plugin-react": "^7.12.4", + "glob": "^7.1.3", "http-server": "^0.11.1", "jest": "^24.1.0", "jest-fetch-mock": "^2.1.1", diff --git a/scripts/i18n-lint.js b/scripts/i18n-lint.js new file mode 100644 index 0000000000000000000000000000000000000000..946189fa476c934958e06579c4f1363c91e27b1c --- /dev/null +++ b/scripts/i18n-lint.js @@ -0,0 +1,73 @@ +const glob = require('glob'); // eslint-disable-line import/no-extraneous-dependencies +const fs = require('fs'); +const chalk = require('chalk'); // eslint-disable-line import/no-extraneous-dependencies + +const { log } = console; +const globOpts = { cwd: 'locales' }; +const defaultLocaleFile = 'en/translation.json'; +const files = glob.sync('**/translation.json', globOpts); +const normalizedFiles = {}; +const errors = {}; + +/** + * Return a new copy of the array lowercased and sorted + */ +function lowerCaseSortedArray(arr) { + return arr.slice().map(v => v.toLowerCase()).sort(); +} + +/** + * Return the keys in an array that are not sorted (not considering case in sort) + */ +function unsortedKeys(arr) { + const sortedArray = lowerCaseSortedArray(arr); + + return arr.filter((v, i) => v.toLowerCase() !== sortedArray[i]); +} + +/** + * Return any keys in array 2 that are not in array 1 + * (values will be sorted and downcased for comparison) + */ +function missingKeys(arr1, arr2) { + const sortedDonwcasedLeftHandArray = lowerCaseSortedArray(arr1); + const sortedDonwcasedRightHandArray = lowerCaseSortedArray(arr2); + + return sortedDonwcasedLeftHandArray.filter((v, i) => v !== sortedDonwcasedRightHandArray[i]); +} + +files.forEach((fileName) => { + const fileContent = fs.readFileSync(`locales/${fileName}`); + normalizedFiles[fileName] = Object.keys(JSON.parse(fileContent.toString()).translation); +}); + +const completeKeys = normalizedFiles[defaultLocaleFile].slice().sort(); + + +Object.keys(normalizedFiles).forEach((fileName) => { + const unsorted = unsortedKeys(normalizedFiles[fileName]); + const missing = missingKeys(completeKeys, normalizedFiles[fileName]); + + if (unsorted.length) { + errors[fileName] = errors[fileName] || []; + errors[fileName].push('Keys are not sorted properly'); + errors[fileName].push( + `\tSorting starts to be incorrect around: ${unsorted[0]}`, + ); + } + + if (missing.length) { + errors[fileName] = errors[fileName] || []; + errors[fileName].push('Some keys from the default locale file are missing'); + errors[fileName].push( + `\tMissing keys: ${missing.join(', ')}`, + ); + } +}); + +Object.keys(errors).forEach((errorFileName) => { + log(chalk.red(`${chalk.inverse.bold(errorFileName)} has ${chalk.underline.bold('internationalization')} errors`)); + errors[errorFileName].forEach((error) => { + log(`\t${chalk.yellow(error)}`); + }); +});