this repo has no description
1import fs from 'node:fs';
2
3// Dependency from Lingui, not listed in package.json
4import PO from 'pofile';
5
6let listedLocales = [];
7try {
8 listedLocales = JSON.parse(
9 fs.readFileSync('src/data/listed-locales.json', 'utf8'),
10 );
11} catch (e) {}
12
13const DEFAULT_LANG = 'en';
14const IGNORE_LANGS = [DEFAULT_LANG, 'pseudo-LOCALE'];
15
16const files = fs.readdirSync('src/locales');
17const catalogs = {};
18
19const enCatalog = files.find((file) => file.endsWith('en.po'));
20const enContent = fs.readFileSync(`src/locales/${enCatalog}`, 'utf8');
21const enPo = PO.parse(enContent);
22const total = enPo.items.length;
23console.log('Total strings:', total);
24
25const codeMaps = {
26 'kab-KAB': 'kab',
27};
28
29files.forEach((file) => {
30 if (file.endsWith('.po')) {
31 const code = file.replace(/\.po$/, '');
32 if (IGNORE_LANGS.includes(code)) return;
33 const content = fs.readFileSync(`src/locales/${file}`, 'utf8');
34 const po = PO.parse(content);
35 const { items } = po;
36 // Percentage of translated strings
37 const translated = items.filter(
38 (item) => item.msgstr !== '' && item.msgstr[0] !== '',
39 ).length;
40 const percentage = Math.round((translated / total) * 100);
41 po.percentage = percentage;
42 if (percentage > 0) {
43 // Ignore empty catalogs
44 catalogs[codeMaps[code] || code] = percentage;
45 }
46 }
47});
48
49const regionMaps = {
50 'zh-CN': 'zh-Hans',
51 'zh-TW': 'zh-Hant',
52};
53
54function IDN(inputCode, outputCode) {
55 let result;
56 const regionlessInputCode =
57 regionMaps[inputCode] || inputCode.replace(/-[a-z]+$/i, '');
58 const regionlessOutputCode =
59 regionMaps[outputCode] || outputCode.replace(/-[a-z]+$/i, '');
60 const inputCodes =
61 regionlessInputCode !== inputCode
62 ? [inputCode, regionlessInputCode]
63 : [inputCode];
64 const outputCodes =
65 regionlessOutputCode !== outputCode
66 ? [regionlessOutputCode, outputCode]
67 : [outputCode];
68
69 for (const inputCode of inputCodes) {
70 for (const outputCode of outputCodes) {
71 try {
72 result = new Intl.DisplayNames([inputCode], {
73 type: 'language',
74 }).of(outputCode);
75 break;
76 } catch (e) {}
77 }
78 if (result) break;
79 }
80 return result;
81}
82
83const fullCatalogs = Object.entries(catalogs)
84 // sort by key
85 .sort((a, b) => a[0].localeCompare(b[0]))
86 .map(([code, completion]) => {
87 const nativeName = IDN(code, code);
88 const name = IDN('en', code);
89 return { code, nativeName, name, completion };
90 });
91
92// Set listed: true if completion > PERCENTAGE_THRESHOLD
93const PERCENTAGE_THRESHOLD = 50;
94const listedCatalogs = fullCatalogs.map((catalog) => ({
95 ...catalog,
96 // Once listed, always listed
97 // A locale may exceed percentage threshold today, but not tomorrow
98 // So it should't suddenly become unlisted
99 listed:
100 listedLocales.includes(catalog.code) ||
101 catalog.completion >= PERCENTAGE_THRESHOLD,
102}));
103
104// Sort by completion
105const sortedCatalogs = [...listedCatalogs].sort(
106 (a, b) => b.completion - a.completion,
107);
108
109console.table(sortedCatalogs);
110console.log(
111 `Total listed/unlisted: ${listedCatalogs.filter((c) => c.listed).length}/${
112 listedCatalogs.filter((c) => !c.listed).length
113 }`,
114);
115
116const path = 'src/data/catalogs.json';
117fs.writeFileSync(path, JSON.stringify(listedCatalogs, null, 2));
118console.log('File written:', path);
119
120const path2 = 'src/data/listed-locales.json';
121const codes = listedCatalogs.filter((c) => c.listed).map((c) => c.code);
122if (codes.length >= listedLocales.length) {
123 fs.writeFileSync(path2, JSON.stringify(codes, null, 2));
124 console.log('File written:', path2);
125} else {
126 console.error(
127 `Number of listed locales reduced from ${listedLocales.length} to ${codes.length}.`,
128 );
129}