Skip to content

Commit 991a74e

Browse files
authored
Hotfix Unicode CLDR database (#4404) (#4406)
* Hotfix Unicode CLDR database (#4404) * Hotfix Unicode CLDR database * Add entry * Add test * Update PR number
1 parent 1b842df commit 991a74e

7 files changed

+204
-2
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
2222

2323
## [Unreleased]
2424

25+
### Fixed
26+
27+
- QFE: Fixes [#4403](https://github.com/microsoft/BotFramework-WebChat/issues/4403). Patched Unicode CLDR database which caused file upload in Polish to appear blank, by [@compulim](https://github.com/compulim), in PR [#4406](https://github.com/microsoft/BotFramework-WebChat/pull/4406)
28+
2529
## [4.14.1] - 2021-09-07
2630

2731
### Fixed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<!DOCTYPE html>
2+
<html lang="en-US">
3+
<head>
4+
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
5+
<script crossorigin="anonymous" src="/test-harness.js"></script>
6+
<script crossorigin="anonymous" src="/test-page-object.js"></script>
7+
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
8+
</head>
9+
<body>
10+
<div id="webchat"></div>
11+
<script>
12+
run(async function () {
13+
const directLine = testHelpers.createDirectLineWithTranscript([
14+
{
15+
type: 'message',
16+
id: 'A0000001',
17+
timestamp: '2022-09-06T20:49:30.3628667Z',
18+
localTimestamp: '2022-09-06T13:49:29.166-07:00',
19+
localTimezone: 'America/Los_Angeles',
20+
channelId: 'directline',
21+
from: {
22+
role: 'user'
23+
},
24+
locale: 'en-US',
25+
attachments: [
26+
{
27+
contentType: 'application/zip',
28+
contentUrl:
29+
'https://docs.botframework.com/static/devportal/client/images/bot-framework-default-placeholder.png',
30+
name: 'some-file.zip'
31+
}
32+
],
33+
channelData: {
34+
attachmentSizes: [1024]
35+
}
36+
}
37+
]);
38+
39+
const store = testHelpers.createStore();
40+
41+
WebChat.renderWebChat(
42+
{
43+
directLine,
44+
locale: 'pl-PL',
45+
store
46+
},
47+
document.getElementById('webchat')
48+
);
49+
50+
await pageConditions.uiConnected();
51+
await host.snapshot();
52+
});
53+
</script>
54+
</body>
55+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */
2+
3+
describe('upload a file in Polish', () => {
4+
test('should render properly', () =>
5+
runHTML('localization.fileUpload.polish.html'));
6+
});

packages/api/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"prestart": "npm run build:babel",
3131
"start": "concurrently --kill-others --names \"babel,globalize,tsc\" \"npm run start:babel\" \"npm run start:globalize\" \"npm run start:typescript\"",
3232
"start:babel": "npm run build:babel -- --skip-initial-build --watch",
33-
"start:globalize": "node-dev --respawn scripts/createPrecompiledGlobalize.js",
33+
"start:globalize": "node-dev --respawn scripts/createPrecompiledGlobalize.mjs",
3434
"start:typescript": "npm run build:typescript -- --watch"
3535
},
3636
"devDependencies": {

packages/support/cldr-data/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@
3535
"files": [
3636
"src/index.js",
3737
"src/install.js",
38+
"src/patch.mjs",
3839
"urls.json"
3940
],
4041
"scripts": {
4142
"eslint": "npm run precommit",
42-
"install": "node ./src/install.js",
43+
"install": "node ./src/install.js && node ./src/patch.mjs",
4344
"precommit": "npm run precommit:eslint -- src",
4445
"precommit:eslint": "eslint",
4546
"prettier": "prettier --check src/**/*.{js,ts,tsx}"
+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { fileURLToPath } from 'url';
2+
import { resolve } from 'path';
3+
import fs from 'fs/promises';
4+
5+
// There is an issue in the Unicode CLDR database (v36):
6+
//
7+
// - Polish has 4 different plural types: "one", "few", "many", "other"
8+
// - However, some units, say "short/digital-kilobyte", only have "other" defined
9+
// - When we localize 1024 (number) into kilobytes, it use the "one" type
10+
// - Since "short/digital-kilobyte/one" is not defined in the database, `globalize` throw exception
11+
//
12+
// In all of our supported languages, we also observed the same issue in Portuguese as well.
13+
//
14+
// As a hotfix, we are patching the Unicode CLDR database for all `[long/short/narrow]/digital-*` rules to make sure it include all plurals needed for that language.
15+
//
16+
// For a long term fix, we should move forward to a newer version of CLDR database, which is outlined in https://github.com/rxaviers/cldr-data-npm/issues/78.
17+
18+
let FORBIDDEN_PROPERTY_NAMES;
19+
20+
function getForbiddenPropertyNames() {
21+
return (
22+
FORBIDDEN_PROPERTY_NAMES ||
23+
(FORBIDDEN_PROPERTY_NAMES = Object.freeze(
24+
Array.from(
25+
new Set([
26+
// As-of writing, `Object.prototype` includes:
27+
// __defineGetter__
28+
// __defineSetter__
29+
// __lookupGetter__
30+
// __lookupSetter
31+
// __proto__
32+
// constructor
33+
// hasOwnProperty
34+
// isPrototypeOf
35+
// propertyIsEnumerable
36+
// toLocaleString
37+
// toString
38+
// valueOf
39+
...Object.getOwnPropertyNames(Object.prototype),
40+
41+
'prototype'
42+
])
43+
)
44+
))
45+
);
46+
}
47+
48+
function isForbiddenPropertyName(propertyName) {
49+
return getForbiddenPropertyNames().includes(propertyName);
50+
}
51+
52+
function toDist(filename) {
53+
if (filename.includes('..')) {
54+
throw new Error('Filename cannot contains "..".');
55+
}
56+
57+
return resolve(fileURLToPath(import.meta.url), '../../dist/', filename);
58+
}
59+
60+
// ESLint "wrap-iife" rule is conflicting with Prettier.
61+
// eslint-disable-next-line wrap-iife
62+
(async function () {
63+
const plurals = JSON.parse(await fs.readFile(toDist('supplemental/plurals.json'), 'utf8'));
64+
65+
const languagePlurals = new Map();
66+
67+
Object.entries(plurals.supplemental['plurals-type-cardinal']).forEach(([language, pluralsTypeCardinal]) => {
68+
const plurals = ['other'];
69+
70+
languagePlurals.set(language, plurals);
71+
72+
if (!(`pluralRule-count-other` in pluralsTypeCardinal)) {
73+
throw new Error(`Language ${language} does not have plural type "other".`);
74+
}
75+
76+
['zero', 'one', 'two', 'few', 'many'].forEach(pluralType => {
77+
`pluralRule-count-${pluralType}` in pluralsTypeCardinal && plurals.push(pluralType);
78+
});
79+
});
80+
81+
const patchedLanguages = [];
82+
83+
await Promise.all(
84+
Array.from(languagePlurals.entries()).map(async ([language, supportedPluralTypes]) => {
85+
if (!/^[\w-]+$/u.test(language) && isForbiddenPropertyName(language)) {
86+
throw new Error(`Invalid language code "${language}".`);
87+
}
88+
89+
let units;
90+
91+
try {
92+
units = JSON.parse(await fs.readFile(toDist(`main/${language}/units.json`), 'utf8'));
93+
} catch (err) {
94+
if (err.code === 'ENOENT') {
95+
return;
96+
}
97+
98+
throw err;
99+
}
100+
101+
let numFound = 0;
102+
103+
['long', 'short', 'narrow'].forEach(form => {
104+
Object.entries(units.main[language].units[form]).forEach(([unitName, entry]) => {
105+
if (!unitName.startsWith('digital-')) {
106+
return;
107+
}
108+
109+
if ('unitPattern-count-other' in entry) {
110+
const { 'unitPattern-count-other': other } = entry;
111+
112+
supportedPluralTypes.forEach(pluralType => {
113+
const name = `unitPattern-count-${pluralType}`;
114+
115+
if (!(name in entry)) {
116+
entry[name] = other;
117+
numFound++;
118+
}
119+
});
120+
}
121+
});
122+
});
123+
124+
if (numFound) {
125+
patchedLanguages.push(`${language} (${numFound} issues)`);
126+
127+
// eslint-disable-next-line no-magic-numbers
128+
await fs.writeFile(toDist(`main/${language}/units.json`), JSON.stringify(units, null, 2));
129+
}
130+
})
131+
);
132+
133+
// We are display output in CLI.
134+
// eslint-disable-next-line no-console
135+
console.log(`Patched ${patchedLanguages.length} languages: ${patchedLanguages.join(', ')}.`);
136+
})();

0 commit comments

Comments
 (0)