Extension to return old Twitter layout from 2015.
1// Tests locale files
2
3const languageCodes = [
4 {"code":"ab","name":"Abkhaz","nativeName":"аҧсуа"},
5 {"code":"aa","name":"Afar","nativeName":"Afaraf"},
6 {"code":"af","name":"Afrikaans","nativeName":"Afrikaans"},
7 {"code":"ak","name":"Akan","nativeName":"Akan"},
8 {"code":"sq","name":"Albanian","nativeName":"Shqip"},
9 {"code":"am","name":"Amharic","nativeName":"አማርኛ"},
10 {"code":"ar","name":"Arabic","nativeName":"العربية"},
11 {"code":"an","name":"Aragonese","nativeName":"Aragonés"},
12 {"code":"hy","name":"Armenian","nativeName":"Հայերեն"},
13 {"code":"as","name":"Assamese","nativeName":"অসমীয়া"},
14 {"code":"av","name":"Avaric","nativeName":"авар мацӀ, магӀарул мацӀ"},
15 {"code":"ae","name":"Avestan","nativeName":"avesta"},
16 {"code":"ay","name":"Aymara","nativeName":"aymar aru"},
17 {"code":"az","name":"Azerbaijani","nativeName":"azərbaycan dili"},
18 {"code":"bm","name":"Bambara","nativeName":"bamanankan"},
19 {"code":"ba","name":"Bashkir","nativeName":"башҡорт теле"},
20 {"code":"eu","name":"Basque","nativeName":"euskara, euskera"},
21 {"code":"be","name":"Belarusian","nativeName":"Беларуская"},
22 {"code":"bn","name":"Bengali","nativeName":"বাংলা"},
23 {"code":"bh","name":"Bihari","nativeName":"भोजपुरी"},
24 {"code":"bi","name":"Bislama","nativeName":"Bislama"},
25 {"code":"bs","name":"Bosnian","nativeName":"bosanski jezik"},
26 {"code":"br","name":"Breton","nativeName":"brezhoneg"},
27 {"code":"bg","name":"Bulgarian","nativeName":"български език"},
28 {"code":"my","name":"Burmese","nativeName":"ဗမာစာ"},
29 {"code":"ca","name":"Catalan; Valencian","nativeName":"Català"},
30 {"code":"ch","name":"Chamorro","nativeName":"Chamoru"},
31 {"code":"ce","name":"Chechen","nativeName":"нохчийн мотт"},
32 {"code":"ny","name":"Chichewa; Chewa; Nyanja","nativeName":"chiCheŵa, chinyanja"},
33 {"code":"zh","name":"Chinese","nativeName":"中文 (Zhōngwén), 汉语, 漢語"},
34 {"code":"zh_CN","name":"Chinese","nativeName":"中文 (Zhōngwén), 汉语, 漢語"},
35 {"code":"cv","name":"Chuvash","nativeName":"чӑваш чӗлхи"},
36 {"code":"kw","name":"Cornish","nativeName":"Kernewek"},
37 {"code":"co","name":"Corsican","nativeName":"corsu, lingua corsa"},
38 {"code":"cr","name":"Cree","nativeName":"ᓀᐦᐃᔭᐍᐏᐣ"},
39 {"code":"hr","name":"Croatian","nativeName":"hrvatski"},
40 {"code":"cs","name":"Czech","nativeName":"česky, čeština"},
41 {"code":"da","name":"Danish","nativeName":"dansk"},
42 {"code":"dv","name":"Divehi; Dhivehi; Maldivian;","nativeName":"ދިވެހި"},
43 {"code":"nl","name":"Dutch","nativeName":"Nederlands, Vlaams"},
44 {"code":"en","name":"English","nativeName":"English"},
45 {"code":"eo","name":"Esperanto","nativeName":"Esperanto"},
46 {"code":"et","name":"Estonian","nativeName":"eesti, eesti keel"},
47 {"code":"ee","name":"Ewe","nativeName":"Eʋegbe"},
48 {"code":"fo","name":"Faroese","nativeName":"føroyskt"},
49 {"code":"fj","name":"Fijian","nativeName":"vosa Vakaviti"},
50 {"code":"fi","name":"Finnish","nativeName":"suomi, suomen kieli"},
51 {"code":"fr","name":"French","nativeName":"français, langue française"},
52 {"code":"ff","name":"Fula; Fulah; Pulaar; Pular","nativeName":"Fulfulde, Pulaar, Pular"},
53 {"code":"gl","name":"Galician","nativeName":"Galego"},
54 {"code":"ka","name":"Georgian","nativeName":"ქართული"},
55 {"code":"de","name":"German","nativeName":"Deutsch"},
56 {"code":"el","name":"Greek, Modern","nativeName":"Ελληνικά"},
57 {"code":"gn","name":"Guaraní","nativeName":"Avañeẽ"},
58 {"code":"gu","name":"Gujarati","nativeName":"ગુજરાતી"},
59 {"code":"ht","name":"Haitian; Haitian Creole","nativeName":"Kreyòl ayisyen"},
60 {"code":"ha","name":"Hausa","nativeName":"Hausa, هَوُسَ"},
61 {"code":"he","name":"Hebrew (modern)","nativeName":"עברית"},
62 {"code":"hz","name":"Herero","nativeName":"Otjiherero"},
63 {"code":"hi","name":"Hindi","nativeName":"हिन्दी, हिंदी"},
64 {"code":"ho","name":"Hiri Motu","nativeName":"Hiri Motu"},
65 {"code":"hu","name":"Hungarian","nativeName":"Magyar"},
66 {"code":"ia","name":"Interlingua","nativeName":"Interlingua"},
67 {"code":"id","name":"Indonesian","nativeName":"Bahasa Indonesia"},
68 {"code":"ie","name":"Interlingue","nativeName":"Originally called Occidental; then Interlingue after WWII"},
69 {"code":"ga","name":"Irish","nativeName":"Gaeilge"},
70 {"code":"ig","name":"Igbo","nativeName":"Asụsụ Igbo"},
71 {"code":"ik","name":"Inupiaq","nativeName":"Iñupiaq, Iñupiatun"},
72 {"code":"io","name":"Ido","nativeName":"Ido"},
73 {"code":"is","name":"Icelandic","nativeName":"Íslenska"},
74 {"code":"it","name":"Italian","nativeName":"Italiano"},
75 {"code":"iu","name":"Inuktitut","nativeName":"ᐃᓄᒃᑎᑐᑦ"},
76 {"code":"ja","name":"Japanese","nativeName":"日本語 (にほんご/にっぽんご)"},
77 {"code":"jv","name":"Javanese","nativeName":"basa Jawa"},
78 {"code":"kl","name":"Kalaallisut, Greenlandic","nativeName":"kalaallisut, kalaallit oqaasii"},
79 {"code":"kn","name":"Kannada","nativeName":"ಕನ್ನಡ"},
80 {"code":"kr","name":"Kanuri","nativeName":"Kanuri"},
81 {"code":"ks","name":"Kashmiri","nativeName":"कश्मीरी, كشميري"},
82 {"code":"kk","name":"Kazakh","nativeName":"Қазақ тілі"},
83 {"code":"km","name":"Khmer","nativeName":"ភាសាខ្មែរ"},
84 {"code":"ki","name":"Kikuyu, Gikuyu","nativeName":"Gĩkũyũ"},
85 {"code":"rw","name":"Kinyarwanda","nativeName":"Ikinyarwanda"},
86 {"code":"ky","name":"Kirghiz, Kyrgyz","nativeName":"кыргыз тили"},
87 {"code":"kv","name":"Komi","nativeName":"коми кыв"},
88 {"code":"kg","name":"Kongo","nativeName":"KiKongo"},
89 {"code":"ko","name":"Korean","nativeName":"한국어 (韓國語), 조선말 (朝鮮語)"},
90 {"code":"ku","name":"Kurdish","nativeName":"Kurdî, كوردی"},
91 {"code":"kj","name":"Kwanyama, Kuanyama","nativeName":"Kuanyama"},
92 {"code":"la","name":"Latin","nativeName":"latine, lingua latina"},
93 {"code":"lb","name":"Luxembourgish, Letzeburgesch","nativeName":"Lëtzebuergesch"},
94 {"code":"lg","name":"Luganda","nativeName":"Luganda"},
95 {"code":"li","name":"Limburgish, Limburgan, Limburger","nativeName":"Limburgs"},
96 {"code":"ln","name":"Lingala","nativeName":"Lingála"},
97 {"code":"lo","name":"Lao","nativeName":"ພາສາລາວ"},
98 {"code":"lt","name":"Lithuanian","nativeName":"lietuvių kalba"},
99 {"code":"lu","name":"Luba-Katanga","nativeName":""},
100 {"code":"lv","name":"Latvian","nativeName":"latviešu valoda"},
101 {"code":"gv","name":"Manx","nativeName":"Gaelg, Gailck"},
102 {"code":"mk","name":"Macedonian","nativeName":"македонски јазик"},
103 {"code":"mg","name":"Malagasy","nativeName":"Malagasy fiteny"},
104 {"code":"ms","name":"Malay","nativeName":"bahasa Melayu, بهاس ملايو"},
105 {"code":"ml","name":"Malayalam","nativeName":"മലയാളം"},
106 {"code":"mt","name":"Maltese","nativeName":"Malti"},
107 {"code":"mi","name":"Māori","nativeName":"te reo Māori"},
108 {"code":"mr","name":"Marathi (Marāṭhī)","nativeName":"मराठी"},
109 {"code":"mh","name":"Marshallese","nativeName":"Kajin M̧ajeļ"},
110 {"code":"mn","name":"Mongolian","nativeName":"монгол"},
111 {"code":"na","name":"Nauru","nativeName":"Ekakairũ Naoero"},
112 {"code":"nv","name":"Navajo, Navaho","nativeName":"Diné bizaad, Dinékʼehǰí"},
113 {"code":"nb","name":"Norwegian Bokmål","nativeName":"Norsk bokmål"},
114 {"code":"nd","name":"North Ndebele","nativeName":"isiNdebele"},
115 {"code":"ne","name":"Nepali","nativeName":"नेपाली"},
116 {"code":"ng","name":"Ndonga","nativeName":"Owambo"},
117 {"code":"nn","name":"Norwegian Nynorsk","nativeName":"Norsk nynorsk"},
118 {"code":"no","name":"Norwegian","nativeName":"Norsk"},
119 {"code":"ii","name":"Nuosu","nativeName":"ꆈꌠ꒿ Nuosuhxop"},
120 {"code":"nr","name":"South Ndebele","nativeName":"isiNdebele"},
121 {"code":"oc","name":"Occitan","nativeName":"Occitan"},
122 {"code":"oj","name":"Ojibwe, Ojibwa","nativeName":"ᐊᓂᔑᓈᐯᒧᐎᓐ"},
123 {"code":"cu","name":"Old Church Slavonic, Church Slavic, Church Slavonic, Old Bulgarian, Old Slavonic","nativeName":"ѩзыкъ словѣньскъ"},
124 {"code":"om","name":"Oromo","nativeName":"Afaan Oromoo"},
125 {"code":"or","name":"Oriya","nativeName":"ଓଡ଼ିଆ"},
126 {"code":"os","name":"Ossetian, Ossetic","nativeName":"ирон æвзаг"},
127 {"code":"pa","name":"Panjabi, Punjabi","nativeName":"ਪੰਜਾਬੀ, پنجابی"},
128 {"code":"pi","name":"Pāli","nativeName":"पाऴि"},
129 {"code":"fa","name":"Persian","nativeName":"فارسی"},
130 {"code":"pl","name":"Polish","nativeName":"polski"},
131 {"code":"ps","name":"Pashto, Pushto","nativeName":"پښتو"},
132 {"code":"pt","name":"Portuguese","nativeName":"Português"},
133 {"code":"pt_BR","name":"Portuguese (Brazil)","nativeName":"Português do Brasil"},
134 {"code":"qu","name":"Quechua","nativeName":"Runa Simi, Kichwa"},
135 {"code":"rm","name":"Romansh","nativeName":"rumantsch grischun"},
136 {"code":"rn","name":"Kirundi","nativeName":"kiRundi"},
137 {"code":"ro","name":"Romanian, Moldavian, Moldovan","nativeName":"română"},
138 {"code":"ru","name":"Russian","nativeName":"русский язык"},
139 {"code":"sa","name":"Sanskrit (Saṁskṛta)","nativeName":"संस्कृतम्"},
140 {"code":"sc","name":"Sardinian","nativeName":"sardu"},
141 {"code":"sd","name":"Sindhi","nativeName":"सिन्धी, سنڌي، سندھی"},
142 {"code":"se","name":"Northern Sami","nativeName":"Davvisámegiella"},
143 {"code":"sm","name":"Samoan","nativeName":"gagana faa Samoa"},
144 {"code":"sg","name":"Sango","nativeName":"yângâ tî sängö"},
145 {"code":"sr","name":"Serbian","nativeName":"српски језик"},
146 {"code":"gd","name":"Scottish Gaelic; Gaelic","nativeName":"Gàidhlig"},
147 {"code":"sn","name":"Shona","nativeName":"chiShona"},
148 {"code":"si","name":"Sinhala, Sinhalese","nativeName":"සිංහල"},
149 {"code":"sk","name":"Slovak","nativeName":"slovenčina"},
150 {"code":"sl","name":"Slovene","nativeName":"slovenščina"},
151 {"code":"so","name":"Somali","nativeName":"Soomaaliga, af Soomaali"},
152 {"code":"st","name":"Southern Sotho","nativeName":"Sesotho"},
153 {"code":"es","name":"Spanish; Castilian","nativeName":"español, castellano"},
154 {"code":"su","name":"Sundanese","nativeName":"Basa Sunda"},
155 {"code":"sw","name":"Swahili","nativeName":"Kiswahili"},
156 {"code":"ss","name":"Swati","nativeName":"SiSwati"},
157 {"code":"sv","name":"Swedish","nativeName":"svenska"},
158 {"code":"ta","name":"Tamil","nativeName":"தமிழ்"},
159 {"code":"te","name":"Telugu","nativeName":"తెలుగు"},
160 {"code":"tg","name":"Tajik","nativeName":"тоҷикӣ, toğikī, تاجیکی"},
161 {"code":"th","name":"Thai","nativeName":"ไทย"},
162 {"code":"ti","name":"Tigrinya","nativeName":"ትግርኛ"},
163 {"code":"bo","name":"Tibetan Standard, Tibetan, Central","nativeName":"བོད་ཡིག"},
164 {"code":"tk","name":"Turkmen","nativeName":"Türkmen, Түркмен"},
165 {"code":"tl","name":"Tagalog","nativeName":"Wikang Tagalog, ᜏᜒᜃᜅ᜔ ᜆᜄᜎᜓᜄ᜔"},
166 {"code":"tn","name":"Tswana","nativeName":"Setswana"},
167 {"code":"to","name":"Tonga (Tonga Islands)","nativeName":"faka Tonga"},
168 {"code":"tr","name":"Turkish","nativeName":"Türkçe"},
169 {"code":"ts","name":"Tsonga","nativeName":"Xitsonga"},
170 {"code":"tt","name":"Tatar","nativeName":"татарча, tatarça, تاتارچا"},
171 {"code":"tw","name":"Twi","nativeName":"Twi"},
172 {"code":"ty","name":"Tahitian","nativeName":"Reo Tahiti"},
173 {"code":"ug","name":"Uighur, Uyghur","nativeName":"Uyƣurqə, ئۇيغۇرچە"},
174 {"code":"uk","name":"Ukrainian","nativeName":"українська"},
175 {"code":"ur","name":"Urdu","nativeName":"اردو"},
176 {"code":"uz","name":"Uzbek","nativeName":"zbek, Ўзбек, أۇزبېك"},
177 {"code":"ve","name":"Venda","nativeName":"Tshivenḓa"},
178 {"code":"vi","name":"Vietnamese","nativeName":"Tiếng Việt"},
179 {"code":"vo","name":"Volapük","nativeName":"Volapük"},
180 {"code":"wa","name":"Walloon","nativeName":"Walon"},
181 {"code":"cy","name":"Welsh","nativeName":"Cymraeg"},
182 {"code":"wo","name":"Wolof","nativeName":"Wollof"},
183 {"code":"fy","name":"Western Frisian","nativeName":"Frysk"},
184 {"code":"xh","name":"Xhosa","nativeName":"isiXhosa"},
185 {"code":"yi","name":"Yiddish","nativeName":"ייִדיש"},
186 {"code":"yo","name":"Yoruba","nativeName":"Yorùbá"},
187 {"code":"za","name":"Zhuang, Chuang","nativeName":"Saɯ cueŋƅ, Saw cuengh"}
188];
189
190const fs = require('fs');
191const path = require('path');
192
193const validKey = /^[a-zA-Z0-9_@]+$/;
194const placeholdersRegex = /\$[a-zA-Z0-9_@]+\$/g;
195const validFieldKeys = ['name', 'message', 'description', 'example', 'note', 'placeholders'];
196
197const locales =
198 fs.readdirSync(path.join(__dirname, '_locales'), { withFileTypes: true })
199 .filter(dirent => dirent.isDirectory())
200 .map(dirent => dirent.name);
201
202const englishLocale = JSON.parse(fs.readFileSync(path.join(__dirname, '_locales', 'en', 'messages.json')));
203const englishLocaleArray = Object.keys(englishLocale);
204
205let errors = false;
206
207for(let localeName of locales) {
208 let locale;
209
210 try {
211 locale = JSON.parse(fs.readFileSync(path.join(__dirname, '_locales', localeName, 'messages.json')));
212 } catch(e) {
213 console.error(`❌ (Critical) Error parsing _locales/${localeName}/messages.json`, e);
214 errors = true;
215 continue;
216 }
217
218 if(localeName !== 'en') {
219 let array = Object.keys(locale);
220 let diff = englishLocaleArray.length - array.length;
221 if(diff !== 0) {
222 if(diff < 10) {
223 let missing = englishLocaleArray.filter(key => !array.includes(key));
224 console.log(`❌ Missing ${diff} keys in _locales/${localeName}/messages.json (${languageCodes.find(l => l.code === localeName)?.name}): ${missing.join(', ')}`);
225 } else {
226 console.log(`❌ Missing ${diff} keys in _locales/${localeName}/messages.json (${languageCodes.find(l => l.code === localeName)?.name})`);
227 }
228 } else {
229 console.log(`✅ All keys present in _locales/${localeName}/messages.json (${languageCodes.find(l => l.code === localeName)?.name})`);
230 }
231 }
232
233 let keys = Object.keys(locale);
234 for(let key of keys) {
235 if(!englishLocale[key]) {
236 console.error(`❌ Key "${key}" is not present in English for _locales/${localeName}/messages.json`);
237 errors = true;
238 }
239 if(!validKey.test(key)) {
240 console.error(`❌ Invalid key "${key}" in _locales/${localeName}/messages.json`);
241 errors = true;
242 }
243 if(key.length > 80) {
244 console.error(`❌ Key "${key}" is too long in _locales/${localeName}/messages.json`);
245 errors = true;
246 }
247 }
248
249 if(!locale.ext_description) {
250 console.error(`❌ Missing "ext_description" in _locales/${localeName}/messages.json`);
251 errors = true;
252 }
253
254 if(locale.ext_description.length > 132) {
255 console.error(`❌ "ext_description" is too long in _locales/${localeName}/messages.json`);
256 errors = true;
257 }
258
259 for(let fieldName in locale) {
260 let field = locale[fieldName];
261 let keys = Object.keys(field);
262
263 for(let key of keys) {
264 if(!validFieldKeys.includes(key)) {
265 console.error(`❌ Invalid key "${key}" on "${fieldName}" in _locales/${localeName}/messages.json`);
266 errors = true;
267 }
268 }
269
270 if(typeof field.message !== 'string') {
271 console.error(`❌ Missing "message" on "${fieldName}" in _locales/${localeName}/messages.json`);
272 errors = true;
273 }
274
275 let placeholders = field.message.match(placeholdersRegex);
276 if(placeholders) {
277 if(!field.placeholders) {
278 console.error(`❌ Missing "placeholders" on "${fieldName}" in _locales/${localeName}/messages.json`);
279 errors = true;
280 } else {
281 placeholders = placeholders.map(placeholder => placeholder.slice(1, -1).toLowerCase());
282 for(let placeholder of placeholders) {
283 if(!field.placeholders[placeholder]) {
284 console.error(`❌ Missing placeholder "${placeholder}" on "${fieldName}" in _locales/${localeName}/messages.json`);
285 errors = true;
286 }
287 if(!field.placeholders[placeholder].content) {
288 console.error(`❌ Missing placeholder content "${placeholder}" on "${fieldName}" in _locales/${localeName}/messages.json`);
289 errors = true;
290 }
291 }
292 }
293 }
294 }
295}
296
297if(errors) {
298 process.exit(1);
299} else {
300 console.log('\n✅ All locale files are valid.');
301}