unicode-input-toolconverter 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc.json +3 -0
- package/.editorconfig +16 -0
- package/.nojekyll +0 -0
- package/.nyc_output/out.json +173804 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/CHANGES.md +11 -0
- package/COPYING.LESSER.txt +180 -0
- package/COPYING.txt +687 -0
- package/README.md +65 -0
- package/_locales/en-US/messages.json +3126 -0
- package/_locales/hu-HU/messages.json +3126 -0
- package/_locales/pt-BR/messages.json +3126 -0
- package/_locales/sv-SE/messages.json +3126 -0
- package/babel.config.json +5 -0
- package/browser_action/characterSelection.js +91 -0
- package/browser_action/charrefConverters.js +389 -0
- package/browser_action/chartBuild.js +155 -0
- package/browser_action/encodingBehaviors.js +72 -0
- package/browser_action/encodings.js +97 -0
- package/browser_action/entities.js +12 -0
- package/browser_action/entityBehaviors.js +96 -0
- package/browser_action/index-es.html +15 -0
- package/browser_action/index-instrumented.html +16 -0
- package/browser_action/index-pages.html +16 -0
- package/browser_action/index.html +16 -0
- package/browser_action/index.iife.min.js +2 -0
- package/browser_action/index.iife.min.js.map +1 -0
- package/browser_action/index.instrumented.iife.min.js +2 -0
- package/browser_action/index.instrumented.iife.min.js.map +1 -0
- package/browser_action/index.js +95 -0
- package/browser_action/preferences/prefDefaults.js +52 -0
- package/browser_action/service-worker/sw-activateCallback.js +53 -0
- package/browser_action/service-worker/sw-locales.json +6 -0
- package/browser_action/service-worker/sw-resources.json +73 -0
- package/browser_action/service-worker/sw-unicode-data.json +3 -0
- package/browser_action/styles/unicode-dialog.css +391 -0
- package/browser_action/templateUtils/elements.js +12 -0
- package/browser_action/templateUtils/fill.js +3 -0
- package/browser_action/templateUtils/validation.js +6 -0
- package/browser_action/templates/chartBuild.js +185 -0
- package/browser_action/templates/index.js +1304 -0
- package/browser_action/templatesElementCustomization/widgets.js +56 -0
- package/browser_action/unicode/UnicodeConverter.js +793 -0
- package/browser_action/unicode/charrefunicodeDb.js +258 -0
- package/browser_action/unicode/getScriptInfoForCodePoint.js +646 -0
- package/browser_action/unicode/hangul.js +253 -0
- package/browser_action/unicode/lastScriptNames.json +5 -0
- package/browser_action/unicode/parseUnihanFromTextFileStrings.js +56 -0
- package/browser_action/unicode/unicodeFieldInfo.js +120 -0
- package/browser_action/unicode/unicodeScripts.js +4912 -0
- package/browser_action/unicode/unihan.js +129 -0
- package/browser_action/unicode/unihanDbPopulate.js +26 -0
- package/browser_action/unicodecharref.js +1480 -0
- package/browser_action/utils/DOMUtils.js +122 -0
- package/browser_action/utils/FetchUtils.js +19 -0
- package/browser_action/utils/TextUtils.js +28 -0
- package/browser_action/utils/TypedArrayUtils.js +21 -0
- package/browser_action/utils/semicolonSeparatedToArray.js +12 -0
- package/browser_action/utils/setupServiceWorker.js +193 -0
- package/cypress.config.js +21 -0
- package/download/entities/copyright-software-20021231.html +70 -0
- package/download/entities/html5-uppercase.ent +54 -0
- package/download/entities/htmlmathml-f.ent +2164 -0
- package/download/entities/isoamsa.ent +201 -0
- package/download/entities/isoamsb.ent +177 -0
- package/download/entities/isoamsc.ent +77 -0
- package/download/entities/isoamsn.ent +148 -0
- package/download/entities/isoamso.ent +107 -0
- package/download/entities/isoamsr.ent +238 -0
- package/download/entities/isobox.ent +95 -0
- package/download/entities/isocyr1.ent +122 -0
- package/download/entities/isocyr2.ent +81 -0
- package/download/entities/isodia.ent +69 -0
- package/download/entities/isogrk1.ent +104 -0
- package/download/entities/isogrk2.ent +75 -0
- package/download/entities/isogrk3.ent +98 -0
- package/download/entities/isogrk4.ent +98 -0
- package/download/entities/isolat1.ent +117 -0
- package/download/entities/isolat2.ent +176 -0
- package/download/entities/isomfrk.ent +107 -0
- package/download/entities/isomopf.ent +81 -0
- package/download/entities/isomscr.ent +107 -0
- package/download/entities/isonum.ent +131 -0
- package/download/entities/isopub.ent +140 -0
- package/download/entities/isotech.ent +216 -0
- package/download/entities/mmlalias.ent +598 -0
- package/download/entities/mmlextra.ent +154 -0
- package/download/entities/predefined.ent +52 -0
- package/download/entities/w3centities-f.ent +2276 -0
- package/download/entities/xhtml1-lat1.ent +143 -0
- package/download/entities/xhtml1-special.ent +78 -0
- package/download/entities/xhtml1-symbol.ent +171 -0
- package/eslint.config.js +66 -0
- package/icons/openWindow16.png +0 -0
- package/icons/openWindow24.png +0 -0
- package/lgtm.yml +5 -0
- package/lib/background.html +11 -0
- package/lib/background.js +13 -0
- package/lib/overlay.css +6 -0
- package/manifest.json +37 -0
- package/package.json +121 -0
- package/polyfills/browser-polyfill.min.js +8 -0
- package/polyfills/browser-polyfill.min.js.map +1 -0
- package/server.js +61 -0
- package/sw.js +259 -0
- package/tools/entities-import.js +62 -0
- package/tools/findEsResources.js +30 -0
- package/tools/list-locales.js +17 -0
- package/tools/parseUnicodeCharts.js +428 -0
- package/tools/ucd-import.js +24 -0
- package/tools/unicode-charts.html +596 -0
- package/tools/unihan-import.js +72 -0
- package/unicode_copyright.txt +46 -0
- package/vendor/camelcase/index.js +110 -0
- package/vendor/fflate/esm/browser.js +2665 -0
- package/vendor/intl-dom/dist/index.esm.js +2962 -0
- package/vendor/jamilih/dist/jml-es.js +2359 -0
- package/vendor/jquery/dist/jquery.js +10716 -0
- package/vendor/json-6/dist/index.mjs +1783 -0
- package/vendor/miller-columns/dist/index-es.min.js +1 -0
- package/vendor/miller-columns/miller-columns.css +75 -0
- package/vendor/simple-prefs/dist/index.esm.js +286 -0
- package/web-ext-config.cjs +13 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// As the Unicode server does not have CORS enabled, we can't adapt
|
|
2
|
+
// this file for the browser, but we're keeping this file for demonstrating
|
|
3
|
+
// an approach for live-obtaining the live version.
|
|
4
|
+
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
|
|
7
|
+
// eslint-disable-next-line no-shadow -- Clearer
|
|
8
|
+
import fetch from 'node-fetch';
|
|
9
|
+
|
|
10
|
+
import {load} from 'cheerio';
|
|
11
|
+
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
|
|
14
|
+
const targetDir = `${process.cwd()}/download/entities`;
|
|
15
|
+
const entityBasePath = 'https://www.w3.org/2003/entities/2007/';
|
|
16
|
+
|
|
17
|
+
const getText = async (file) => {
|
|
18
|
+
return await (await fetch(
|
|
19
|
+
file
|
|
20
|
+
)).text();
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// const targetDir = `${process.cwd()}/download/entities`;
|
|
24
|
+
|
|
25
|
+
if (args.includes('download')) {
|
|
26
|
+
const htmlText = await getText(entityBasePath);
|
|
27
|
+
const $ = load(htmlText);
|
|
28
|
+
const elems = $('a[href$=".ent"]');
|
|
29
|
+
const entityFiles = [];
|
|
30
|
+
// eslint-disable-next-line unicorn/no-for-loop -- Not iterable
|
|
31
|
+
for (let i = 0; i < elems.length; i++) {
|
|
32
|
+
entityFiles.push(elems[i].attribs.href);
|
|
33
|
+
}
|
|
34
|
+
const entityFileTexts = await Promise.all(
|
|
35
|
+
entityFiles.map(async (entityFile) => {
|
|
36
|
+
if ([
|
|
37
|
+
// We don't really need these as express parameter entities
|
|
38
|
+
'htmlmathml.ent',
|
|
39
|
+
'w3centities.ent'
|
|
40
|
+
].includes(entityFile)) {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
file: `${targetDir}/${entityFile}`,
|
|
45
|
+
text: await getText(`${entityBasePath}${entityFile}`)
|
|
46
|
+
};
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// eslint-disable-next-line no-console -- CLI
|
|
51
|
+
console.log('entity file file count', entityFileTexts.length);
|
|
52
|
+
|
|
53
|
+
await Promise.all(entityFileTexts.map(({file, text}) => {
|
|
54
|
+
if (!file) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
return fs.writeFile(file, text);
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
// eslint-disable-next-line no-console -- CLI
|
|
61
|
+
console.log('Complete!');
|
|
62
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const queryModule = {
|
|
2
|
+
[
|
|
3
|
+
// Entity files
|
|
4
|
+
'ArrayExpression > Literal[value="optgroup"] + ObjectExpression + ' +
|
|
5
|
+
'ArrayExpression:has(CallExpression > Literal[value=/^ent_/]) ' +
|
|
6
|
+
'ObjectExpression Literal'
|
|
7
|
+
] (node) {
|
|
8
|
+
return [
|
|
9
|
+
`/download/entities/${node.value}.ent`
|
|
10
|
+
];
|
|
11
|
+
},
|
|
12
|
+
// Dynamically imported stylesheets
|
|
13
|
+
'Property[key.name="stylesheets"]' (node) {
|
|
14
|
+
return (node.value.elements || []).flatMap((element) => {
|
|
15
|
+
if (element.type === 'Literal') {
|
|
16
|
+
return element.value;
|
|
17
|
+
}
|
|
18
|
+
// if (element.type === 'ArrayExpression') {
|
|
19
|
+
return element.elements.flatMap((arrayItem) => {
|
|
20
|
+
if (arrayItem.type === 'Literal') {
|
|
21
|
+
return arrayItem.value;
|
|
22
|
+
}
|
|
23
|
+
// Ignore
|
|
24
|
+
return [];
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default queryModule;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
|
|
3
|
+
const dirs = await fs.readdir('_locales');
|
|
4
|
+
|
|
5
|
+
const json = dirs.filter((dir) => {
|
|
6
|
+
return dir !== '.DS_Store';
|
|
7
|
+
}).map((dir) => {
|
|
8
|
+
return `/_locales/${dir}/messages.json`;
|
|
9
|
+
}).sort();
|
|
10
|
+
|
|
11
|
+
// eslint-disable-next-line no-console -- CLI
|
|
12
|
+
console.log('json', json);
|
|
13
|
+
|
|
14
|
+
fs.writeFile(
|
|
15
|
+
'browser_action/service-worker/sw-locales.json',
|
|
16
|
+
JSON.stringify(json, null, 2) + '\n'
|
|
17
|
+
);
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line no-shadow -- Remove?
|
|
5
|
+
import fetch from 'node-fetch';
|
|
6
|
+
import jsdom from 'jsdom';
|
|
7
|
+
|
|
8
|
+
// For `localize` `true`, need to provide strings for all
|
|
9
|
+
// possible; could auto-add to locale files if missing
|
|
10
|
+
const localize = true;
|
|
11
|
+
|
|
12
|
+
const {JSDOM} = jsdom;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} key
|
|
16
|
+
* @returns {string}
|
|
17
|
+
*/
|
|
18
|
+
function getChromeSafeLocaleKey (key) {
|
|
19
|
+
return key.replaceAll(
|
|
20
|
+
String.raw`\n`, ''
|
|
21
|
+
).replaceAll(/\s+/gu, ' '). // Getting some extra WS
|
|
22
|
+
replaceAll(/\W/gu, '_');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let text;
|
|
26
|
+
const lastScriptNamesFile = new URL(
|
|
27
|
+
'../browser_action/unicode/lastScriptNames.json', import.meta.url
|
|
28
|
+
);
|
|
29
|
+
const chartsURL = 'https://unicode.org/charts/';
|
|
30
|
+
const chartsFile = new URL('unicode-charts.html', import.meta.url);
|
|
31
|
+
if (process.argv[2] === 'retrieve') {
|
|
32
|
+
const resp = await fetch(chartsURL);
|
|
33
|
+
text = await resp.text();
|
|
34
|
+
// eslint-disable-next-line no-console -- CLI
|
|
35
|
+
console.log('retrieved', text);
|
|
36
|
+
await fs.writeFile(chartsFile, text);
|
|
37
|
+
// eslint-disable-next-line no-console -- CLI
|
|
38
|
+
console.log('saved file');
|
|
39
|
+
} else {
|
|
40
|
+
text = await fs.readFile(chartsFile, 'utf8');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const doc = new JSDOM(text).window.document;
|
|
44
|
+
const scriptMaps = [...doc.querySelectorAll('table.map')];
|
|
45
|
+
|
|
46
|
+
const uniqueTextPlaceholder = localize
|
|
47
|
+
? '___placeholder___'
|
|
48
|
+
: ''; // We know it is not present inside Unicode script names!
|
|
49
|
+
|
|
50
|
+
const cleanupText = (txt) => {
|
|
51
|
+
return txt.trim().
|
|
52
|
+
replace(/(?<initWS>\s)\s+/u, '$<initWS>').
|
|
53
|
+
replace(/\s*\((?:ASCII|Odia)\)$/u, '').
|
|
54
|
+
replace(/\s\(\d\.?\d*MB\)$/u, ''); // Remove MB size
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const scriptsAndStartRanges = [];
|
|
58
|
+
|
|
59
|
+
const jamilih = scriptMaps.map((scriptMap) => {
|
|
60
|
+
const majorHeading = cleanupText(
|
|
61
|
+
scriptMap.previousElementSibling.textContent
|
|
62
|
+
);
|
|
63
|
+
// const scriptGroups = [...scriptMap.querySelectorAll('table td p')];
|
|
64
|
+
|
|
65
|
+
const scriptGroups = [...scriptMap.querySelectorAll('table td p.sg')];
|
|
66
|
+
// sg, mb, pb/sb
|
|
67
|
+
let lastChildren;
|
|
68
|
+
return ['li', [
|
|
69
|
+
majorHeading,
|
|
70
|
+
['ul', scriptGroups.map((scriptGroup) => {
|
|
71
|
+
return ['li', [
|
|
72
|
+
['b', [
|
|
73
|
+
uniqueTextPlaceholder + cleanupText(scriptGroup.textContent)
|
|
74
|
+
]],
|
|
75
|
+
(() => {
|
|
76
|
+
const lists = [];
|
|
77
|
+
do {
|
|
78
|
+
const a = scriptGroup.querySelector('a');
|
|
79
|
+
const title = a && a.title;
|
|
80
|
+
let cleanedText;
|
|
81
|
+
if (scriptGroup.matches('.mb')) {
|
|
82
|
+
cleanedText = cleanupText(scriptGroup.textContent);
|
|
83
|
+
const children = [uniqueTextPlaceholder + cleanedText];
|
|
84
|
+
lastChildren = children;
|
|
85
|
+
lists.push(
|
|
86
|
+
['li', {title}, children]
|
|
87
|
+
);
|
|
88
|
+
} else if (scriptGroup.matches('.pb,.sb')) {
|
|
89
|
+
cleanedText = cleanupText(scriptGroup.textContent);
|
|
90
|
+
const children = [
|
|
91
|
+
['i', [
|
|
92
|
+
uniqueTextPlaceholder + cleanedText
|
|
93
|
+
]]
|
|
94
|
+
];
|
|
95
|
+
if (!lastChildren) { // A few rare cases to handle, e.g., "Other"
|
|
96
|
+
lists.push(
|
|
97
|
+
['li', {title}, children]
|
|
98
|
+
);
|
|
99
|
+
} else {
|
|
100
|
+
if (!lastChildren[1]) {
|
|
101
|
+
lastChildren[1] = ['ul', []];
|
|
102
|
+
}
|
|
103
|
+
lastChildren[1][1].push(
|
|
104
|
+
['li', {title}, children]
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (cleanedText && title) {
|
|
109
|
+
scriptsAndStartRanges.push({
|
|
110
|
+
script: getChromeSafeLocaleKey(cleanedText),
|
|
111
|
+
startRange: title.replace(/-.*$/u, '')
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
scriptGroup = scriptGroup.nextElementSibling;
|
|
115
|
+
} while (scriptGroup && !scriptGroup.matches('p.sg'));
|
|
116
|
+
lastChildren = null;
|
|
117
|
+
if (!lists.length) { // Just be safe
|
|
118
|
+
return '';
|
|
119
|
+
}
|
|
120
|
+
return ['ul', lists];
|
|
121
|
+
})()
|
|
122
|
+
]];
|
|
123
|
+
})]
|
|
124
|
+
]];
|
|
125
|
+
});
|
|
126
|
+
// console.log('m', majorHeading, scriptGroups);
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @typedef {JSON} LocaleFileContents
|
|
130
|
+
* @todo Indicate more precise format
|
|
131
|
+
*/
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @param {string[]} localeFiles
|
|
135
|
+
* @param {LocaleFileContents} localeFileContents
|
|
136
|
+
* @returns {Promise<void>}
|
|
137
|
+
*/
|
|
138
|
+
async function saveLocalesWithoutDupes (localeFiles, localeFileContents) {
|
|
139
|
+
return await Promise.all(
|
|
140
|
+
localeFiles.map((localeFile, i) => {
|
|
141
|
+
return fs.writeFile(
|
|
142
|
+
localeFile, JSON.stringify(localeFileContents[i], null, 2) + '\n'
|
|
143
|
+
);
|
|
144
|
+
})
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @param {string[]} newScriptNames
|
|
150
|
+
* @param {LocaleFileContents} localeFileContents
|
|
151
|
+
* @returns {Promise<void>}
|
|
152
|
+
*/
|
|
153
|
+
async function deleteUnusedScriptNames (newScriptNames, localeFileContents) {
|
|
154
|
+
const {lastScriptNames} = JSON.parse(
|
|
155
|
+
await fs.readFile(lastScriptNamesFile, 'utf8')
|
|
156
|
+
);
|
|
157
|
+
lastScriptNames.forEach((lastScriptName) => {
|
|
158
|
+
if (!newScriptNames.includes(lastScriptName)) {
|
|
159
|
+
localeFileContents.forEach((lfc) => {
|
|
160
|
+
delete lfc.body[lastScriptName];
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// We only actually need the ESLint `indent` rule for the localized
|
|
167
|
+
// version, but add anyway
|
|
168
|
+
await fs.writeFile(
|
|
169
|
+
'browser_action/unicode/unicodeScripts.js',
|
|
170
|
+
`/* eslint-disable @stylistic/comma-spacing, quotes, indent */
|
|
171
|
+
// Do not edit this file; this is an auto-generated file used to
|
|
172
|
+
// build a hierarchy of script names. It is built by
|
|
173
|
+
// \`parseUnicodeCharts.js\` using ${chartsURL}.
|
|
174
|
+
function unicodeScripts (_) {
|
|
175
|
+
return ` + (localize
|
|
176
|
+
? await (async () => {
|
|
177
|
+
const localesDir = '_locales';
|
|
178
|
+
const dirs = (
|
|
179
|
+
await fs.readdir(localesDir)
|
|
180
|
+
).filter((f) => !f.includes('.'));
|
|
181
|
+
const localeFiles = dirs.map(
|
|
182
|
+
(dir) => path.join(localesDir, dir, 'messages.json')
|
|
183
|
+
);
|
|
184
|
+
const localeFileContents = (
|
|
185
|
+
await Promise.all(localeFiles.map((localeFile) => {
|
|
186
|
+
return fs.readFile(localeFile, 'utf8');
|
|
187
|
+
}))
|
|
188
|
+
).map((fileContents) => {
|
|
189
|
+
return JSON.parse(fileContents);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const dirResults = (
|
|
193
|
+
await recurseDirectory({directory: 'browser_action'})
|
|
194
|
+
).filter(({keys}) => {
|
|
195
|
+
return keys.length;
|
|
196
|
+
});
|
|
197
|
+
// console.log('dirResults', JSON.stringify(dirResults, null, 2));
|
|
198
|
+
const keyMap = {};
|
|
199
|
+
dirResults.forEach(({path: pth, keys}) => {
|
|
200
|
+
keys.forEach((key) => {
|
|
201
|
+
localeFileContents.forEach((lfc, i) => {
|
|
202
|
+
if (!(key in lfc.body)) {
|
|
203
|
+
if (!keyMap[key]) {
|
|
204
|
+
keyMap[key] = {paths: [], locales: []};
|
|
205
|
+
}
|
|
206
|
+
if (!keyMap[key].paths.includes(pth)) {
|
|
207
|
+
keyMap[key].paths.push(pth);
|
|
208
|
+
}
|
|
209
|
+
if (!keyMap[key].locales.includes(localeFiles[i])) {
|
|
210
|
+
keyMap[key].locales.push(localeFiles[i]);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
/*
|
|
218
|
+
Object.entries(keyMap).forEach(([key, {paths, locales}]) => {
|
|
219
|
+
// Working:
|
|
220
|
+
// console.log(
|
|
221
|
+
// `key "${key}" found in paths [${paths.join(', ')}] is ` +
|
|
222
|
+
// `missing in locales: [${locales.join(', ')}]\n`
|
|
223
|
+
// );
|
|
224
|
+
});
|
|
225
|
+
*/
|
|
226
|
+
|
|
227
|
+
localeFileContents.forEach((lfc, i) => {
|
|
228
|
+
if (i !== 0) { // Just test one for now
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
Object.keys(lfc.body).forEach((localeKey) => {
|
|
232
|
+
if (!dirResults.some(({
|
|
233
|
+
keys
|
|
234
|
+
// path: pth
|
|
235
|
+
}) => {
|
|
236
|
+
// console.log('keys', keys);
|
|
237
|
+
return keys.includes(localeKey);
|
|
238
|
+
})) {
|
|
239
|
+
// Working:
|
|
240
|
+
// console.log(`Locale key "${localeKey}" not present in keys
|
|
241
|
+
// used in any of the found file paths.`);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const newScriptNames = [];
|
|
247
|
+
const ret = JSON.stringify(['ul', jamilih], null, 2).replaceAll(
|
|
248
|
+
new RegExp(
|
|
249
|
+
String.raw`^(\s*)"` + uniqueTextPlaceholder + '(.*)"(,)?$',
|
|
250
|
+
'gum'
|
|
251
|
+
),
|
|
252
|
+
(_, initialWS, key, possibleComma = '') => {
|
|
253
|
+
const chromeSafeLocaleKey = getChromeSafeLocaleKey(key);
|
|
254
|
+
|
|
255
|
+
newScriptNames.push(chromeSafeLocaleKey);
|
|
256
|
+
// Insert `chromeSafeLocaleKey` if not present;
|
|
257
|
+
// Todo: Hard-code-test each locale to make sure no other
|
|
258
|
+
// l10n errors for missing keys, script or otherwise
|
|
259
|
+
localeFileContents.forEach((lfc) => {
|
|
260
|
+
/*
|
|
261
|
+
// Works
|
|
262
|
+
localeFileContents.forEach((lfc2, j) => {
|
|
263
|
+
if (i === j) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
Object.entries(lfc.body).forEach(([key, val]) => {
|
|
267
|
+
if (!(key in lfc2)) {
|
|
268
|
+
console.log(`key, ${key}, not present in ${localeFiles[j]}`);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
*/
|
|
273
|
+
if (lfc.body.langCode.message !== 'hu-HU') {
|
|
274
|
+
// return;
|
|
275
|
+
}
|
|
276
|
+
if (!(chromeSafeLocaleKey in lfc.body)) {
|
|
277
|
+
lfc.body[chromeSafeLocaleKey] = {
|
|
278
|
+
message: key
|
|
279
|
+
};
|
|
280
|
+
// console.log(1, chromeSafeLocaleKey, key);
|
|
281
|
+
// fs.writeFile(localeFiles[i], JSON.stringify(lfc, null, 2));
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
/*
|
|
285
|
+
// Find change in *values*
|
|
286
|
+
if (
|
|
287
|
+
lfc.body[chromeSafeLocaleKey] &&
|
|
288
|
+
lfc.body[chromeSafeLocaleKey].message !== key
|
|
289
|
+
) {
|
|
290
|
+
console.log(key, lfc.body[chromeSafeLocaleKey].message);
|
|
291
|
+
}
|
|
292
|
+
*/
|
|
293
|
+
return `${initialWS}_("${chromeSafeLocaleKey}")${possibleComma}`;
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
await deleteUnusedScriptNames(newScriptNames, localeFileContents);
|
|
297
|
+
await saveLocalesWithoutDupes(localeFiles, localeFileContents);
|
|
298
|
+
|
|
299
|
+
/* eslint-disable @stylistic/max-len -- JSON */
|
|
300
|
+
await fs.writeFile(lastScriptNamesFile, `
|
|
301
|
+
{
|
|
302
|
+
"$comment": "Do not edit this file; this is an auto-generated file used to track script names, some of which may end up needing to be deleted from locale files if no longer in use",
|
|
303
|
+
"lastScriptNames": ${JSON.stringify(newScriptNames)}
|
|
304
|
+
}
|
|
305
|
+
`);
|
|
306
|
+
/* eslint-enable @stylistic/max-len -- JSON */
|
|
307
|
+
return ret;
|
|
308
|
+
})()
|
|
309
|
+
: JSON.stringify(['ul', jamilih], null, 2)
|
|
310
|
+
) +
|
|
311
|
+
`;
|
|
312
|
+
}
|
|
313
|
+
export default unicodeScripts;
|
|
314
|
+
`
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
scriptsAndStartRanges.sort((
|
|
318
|
+
{startRange: aStartRange}, {startRange: bStartRange}
|
|
319
|
+
) => {
|
|
320
|
+
return Number.parseInt(aStartRange, 16) >
|
|
321
|
+
Number.parseInt(bStartRange, 16)
|
|
322
|
+
? 1
|
|
323
|
+
: -1;
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
let ifElseBlocks = '';
|
|
327
|
+
let lastStartRange = '0000';
|
|
328
|
+
let lastScript = 'null';
|
|
329
|
+
scriptsAndStartRanges.forEach(({script, startRange}) => {
|
|
330
|
+
if (startRange === '0000' || !lastScript) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
// Todo: Add plane, privateuse, surrogate info
|
|
334
|
+
ifElseBlocks += `
|
|
335
|
+
} else if (num < 0x${startRange}) {
|
|
336
|
+
codePointStart = '${lastStartRange}';
|
|
337
|
+
script = _('${lastScript}');${
|
|
338
|
+
// Not in chart:
|
|
339
|
+
// startRange === 'DB80' ? "surrogate = _('High_Private_Use_Surrogate')" :
|
|
340
|
+
lastStartRange === 'D800'
|
|
341
|
+
? `
|
|
342
|
+
surrogate = _('High_Surrogate');`
|
|
343
|
+
: lastStartRange === 'DC00'
|
|
344
|
+
? `
|
|
345
|
+
surrogate = _('Low_Surrogate');`
|
|
346
|
+
: ['E000', 'F0000', '100000'].includes(lastStartRange)
|
|
347
|
+
? `
|
|
348
|
+
privateuse = true;`
|
|
349
|
+
: ''
|
|
350
|
+
}`;
|
|
351
|
+
lastStartRange = startRange;
|
|
352
|
+
lastScript = script;
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* @param {PlainObject} cfg
|
|
357
|
+
* @param {string} cfg.directory
|
|
358
|
+
* @param {string} [cfg.basePath]
|
|
359
|
+
* @param {{path, keys}} cfg.results
|
|
360
|
+
* @returns {Promise<{path, keys}|void>}
|
|
361
|
+
*/
|
|
362
|
+
async function recurseDirectory ({directory, basePath = './', results}) {
|
|
363
|
+
const dirPath = path.join(basePath, directory);
|
|
364
|
+
const filesAndDirs = await fs.readdir(dirPath);
|
|
365
|
+
let finalPromise = false;
|
|
366
|
+
if (!results) {
|
|
367
|
+
finalPromise = true;
|
|
368
|
+
results = [];
|
|
369
|
+
}
|
|
370
|
+
await Promise.all(
|
|
371
|
+
filesAndDirs.filter((fileOrDir) => {
|
|
372
|
+
// Is a directory or JavaScript file
|
|
373
|
+
return !fileOrDir.includes('.') || (/\.js$/u).test(fileOrDir);
|
|
374
|
+
}).map(async (fileOrDir) => {
|
|
375
|
+
if (!fileOrDir.includes('.')) { // Should check `isDirectory()` or such
|
|
376
|
+
return recurseDirectory({
|
|
377
|
+
directory: fileOrDir,
|
|
378
|
+
basePath: dirPath,
|
|
379
|
+
results
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
const pth = path.join(dirPath, fileOrDir);
|
|
383
|
+
const contents = await fs.readFile(pth, 'utf8');
|
|
384
|
+
|
|
385
|
+
const i18nRegex = // /_\((?:['"]([^'"]*)['"])\)/g;
|
|
386
|
+
// Todo: Works to find variable forms:
|
|
387
|
+
/_\((?:['"](?<key1>[^'"]*)['"]|(?<key2>[^"')][^{)]*|[^{)]*[^"')]))\)/gu;
|
|
388
|
+
|
|
389
|
+
const keys = [];
|
|
390
|
+
let result;
|
|
391
|
+
while ((result = (i18nRegex.exec(contents))) !== null) {
|
|
392
|
+
keys.push(result.groups.key1 || result.groups.key2);
|
|
393
|
+
}
|
|
394
|
+
results.push({path: pth, keys});
|
|
395
|
+
return undefined;
|
|
396
|
+
})
|
|
397
|
+
);
|
|
398
|
+
if (finalPromise) {
|
|
399
|
+
return results;
|
|
400
|
+
}
|
|
401
|
+
return undefined;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// console.log('scriptsAndStartRanges', scriptsAndStartRanges);
|
|
405
|
+
|
|
406
|
+
const output = `
|
|
407
|
+
// Todo: Auto-generate this function
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* @param {PositiveInteger} num
|
|
411
|
+
* @param {IntlDom} _
|
|
412
|
+
* @returns {{
|
|
413
|
+
* codePointStart: string, script: string, plane: PositiveInteger,
|
|
414
|
+
* privateuse: boolean, surrogate: boolean|string
|
|
415
|
+
* }}
|
|
416
|
+
*/
|
|
417
|
+
export default function getScriptInfoForCodePoint (num, _) {
|
|
418
|
+
let privateuse = false, surrogate = false;
|
|
419
|
+
let plane = num >= 0x10000 && num <= 0x1FFFF ? 1 : 0;
|
|
420
|
+
let script = '', codePointStart = '';
|
|
421
|
+
${ifElseBlocks.slice('} else '.length) + '\n }'}
|
|
422
|
+
|
|
423
|
+
return {codePointStart, script, plane, privateuse, surrogate};
|
|
424
|
+
}
|
|
425
|
+
`;
|
|
426
|
+
|
|
427
|
+
// eslint-disable-next-line no-console -- Debugging
|
|
428
|
+
console.log('output', output);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// As the Unicode server does not have CORS enabled, we can't adapt
|
|
2
|
+
// this file for the browser, but we're keeping this file for demonstrating
|
|
3
|
+
// an approach for live-obtaining the live version.
|
|
4
|
+
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
|
|
7
|
+
import download from 'download';
|
|
8
|
+
import extract from 'extract-zip';
|
|
9
|
+
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
|
|
12
|
+
const targetDir = `${process.cwd()}/download/UCD`;
|
|
13
|
+
const ucdZip = `${targetDir}/UCD.zip`;
|
|
14
|
+
|
|
15
|
+
if (args.includes('download')) {
|
|
16
|
+
await download(
|
|
17
|
+
'https://www.unicode.org/Public/UCD/latest/ucd/UCD.zip', targetDir
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (args.includes('extract') || args.includes('download')) {
|
|
22
|
+
await extract(ucdZip, {dir: targetDir});
|
|
23
|
+
await fs.unlink(ucdZip);
|
|
24
|
+
}
|