1+ const fs = require ( 'fs' ) ;
2+ const path = require ( 'path' ) ;
3+
4+ // Read in parameters to choose which type of validation to run
5+ let args = process . argv . slice ( 2 ) ;
6+ let runType = args [ 0 ] ;
7+
8+ // Define primary locale
9+ const primaryLocale = 'en-GB.json' ;
10+
11+ const checkLocaleUsage = ( ) => {
12+ // get a list of all UV extensions
13+ const uvExtensions = getUVExtensions ( ) ;
14+
15+ // Read primary locale file.
16+ let localeData = readLocaleFile ( primaryLocale ) ;
17+ // replace all values with a 0
18+ // we will increment the value for each key found in the extension config files
19+ let localeKeys = Object . keys ( localeData ) . reduce ( ( acc , key ) => {
20+ acc [ key ] = 0 ;
21+ return acc ;
22+ } , { } ) ;
23+
24+ // Loop through all UV extensions.
25+ uvExtensions . forEach ( extension => {
26+ let config = readExtensionConfig ( extension ) ;
27+ // Recursively extract all locale values from any "content" objects.
28+ let contentLocaleValues = extractContentLocaleValues ( config ) ;
29+ // For each locale value found, if it exists in the primary locale keys, increment its count.
30+ contentLocaleValues . forEach ( val => {
31+ if ( val in localeKeys ) {
32+ localeKeys [ val ] ++ ;
33+ }
34+ } ) ;
35+ } ) ;
36+
37+ console . log ( "Final locale keys count:" , localeKeys ) ;
38+ console . log ( "Unused locale keys:" , getUnusedLocaleKeys ( localeKeys ) ) ;
39+ } ;
40+
41+
42+ const checkHardCodedStrings = ( ) => {
43+ let missing = [ ] ;
44+ // get a list of all UV extensions
45+ const uvExtensions = getUVExtensions ( ) ;
46+
47+ // Loop through all UV extensions.
48+ uvExtensions . forEach ( extension => {
49+ let config = readExtensionConfig ( extension ) ;
50+ // Recursively extract all locale values from any "content" objects.
51+ let contentLocaleValues = extractContentLocaleValues ( config ) ;
52+ // For each locale value found, if it exists in the primary locale keys, increment its count.
53+ contentLocaleValues . forEach ( val => {
54+ // if val does not start with $
55+ let hasStartingDelimiter = keyStartsWithDollar ( val ) ;
56+ if ( ! hasStartingDelimiter ) {
57+ missing . push ( { extension : extension , key : val } ) ;
58+ }
59+ } ) ;
60+ } ) ;
61+
62+ console . log ( "missing locale keys:" , missing ) ;
63+ } ;
64+
65+ const keyStartsWithDollar = ( val ) => {
66+ if ( val . substring ( 0 , 1 ) !== '$' ) {
67+ return false
68+ }
69+
70+ return true ;
71+ } ;
72+
73+ const getUVExtensions = ( ) => {
74+ let extensions = [ ] ;
75+ // get a list of all UV extensions
76+ const directoryPath = path . join ( __dirname , '../src/content-handlers/iiif/extensions/' ) ;
77+
78+ try {
79+ const files = fs . readdirSync ( directoryPath ) ;
80+
81+ files . forEach ( function ( file ) {
82+ let dir = file . substring ( 0 , 2 ) ;
83+ // only get directories that start with 'uv'
84+ if ( dir === 'uv' ) {
85+ extensions . push ( file ) ;
86+ }
87+ } ) ;
88+ } catch ( err ) {
89+ console . log ( 'Unable to scan directory: ' + err ) ;
90+ }
91+
92+ return extensions ;
93+ }
94+
95+ const getUVLocales = ( ) => {
96+ const localeDir = path . join ( __dirname , '../src/locales' ) ;
97+ const localeFiles = fs . readdirSync ( localeDir ) ;
98+ return localeFiles ;
99+ } ;
100+
101+ const getUnusedLocaleKeys = ( localeKeys ) => {
102+ return Object . entries ( localeKeys ) . filter ( ( [ key , value ] ) => value === 0 ) ;
103+ }
104+
105+ // Recursively extract all values in any "content" object that are strings starting with "$"
106+ const extractContentLocaleValues = ( obj ) => {
107+ let values = [ ] ;
108+ for ( const [ prop , value ] of Object . entries ( obj ) ) {
109+ if ( prop === 'content' && typeof value === 'object' && value !== null ) {
110+ Object . values ( value ) . forEach ( item => {
111+ if ( typeof item === 'string' ) {
112+ values . push ( item ) ;
113+ }
114+ } ) ;
115+ } else if ( typeof value === 'object' && value !== null ) {
116+ values = values . concat ( extractContentLocaleValues ( value ) ) ;
117+ }
118+ }
119+ return values ;
120+ } ;
121+
122+ const readLocaleFile = ( lang ) => {
123+ const localeDir = path . join ( __dirname , '../src/locales' ) ;
124+ const localeFiles = fs . readdirSync ( localeDir ) . filter ( file => file . endsWith ( `${ lang } ` ) ) ;
125+ if ( localeFiles . length === 0 ) {
126+ throw new Error ( `No locale file found for language ${ lang } ` ) ;
127+ }
128+ const filePath = path . join ( localeDir , localeFiles [ 0 ] ) ;
129+ const fileContent = fs . readFileSync ( filePath , 'utf8' ) ;
130+ return JSON . parse ( fileContent ) ;
131+ } ;
132+
133+ const readExtensionConfig = ( extension ) => {
134+ const configPath = path . join ( __dirname , `../src/content-handlers/iiif/extensions/${ extension } /config/config.json` ) ;
135+ const configContent = fs . readFileSync ( configPath , 'utf8' ) ;
136+ return JSON . parse ( configContent ) ;
137+ } ;
138+
139+ const missingTranslations = ( ) => {
140+ const primaryLocaleData = readLocaleFile ( primaryLocale ) ;
141+ const allLocales = getUVLocales ( ) ;
142+
143+ for ( let locale of allLocales ) {
144+ if ( locale === primaryLocale ) {
145+ continue ;
146+ }
147+ const localeData = readLocaleFile ( locale ) ;
148+ // Compare primary locale keys with each other locale's keys.
149+ // If a key is missing in the other locale, log an error.
150+ for ( let key of Object . keys ( primaryLocaleData ) ) {
151+ if ( ! ( key in localeData ) ) {
152+ console . error ( `Key "${ key } " missing in locale "${ locale } "` ) ;
153+ }
154+ }
155+ }
156+ } ;
157+
158+ switch ( runType ) {
159+ case 'checkLocaleUsage' :
160+ checkLocaleUsage ( ) ;
161+ break ;
162+ case 'missingTranslations' :
163+ missingTranslations ( ) ;
164+ break ;
165+ case 'hardCodedStrings' :
166+ checkHardCodedStrings ( ) ;
167+ break ;
168+ default :
169+ console . error ( 'No parameter or no valid run type. Please use checkLocaleUsage, missingTranslations or hardCodedStrings' ) ;
170+ break ;
171+ }
0 commit comments