mirror of
https://github.com/element-hq/element-web
synced 2024-11-21 16:55:34 +03:00
Load SAS Emoji translations from @matrix-org/spec
(#11429)
* Remove last instance of componentWillMount * Load SAS Emoji translations from @matrix-org/spec * Fix import * Test normalisation on both sides * update comment for @richvdh * Delint
This commit is contained in:
parent
3d2d08b132
commit
d81f71f993
6 changed files with 87 additions and 147 deletions
|
@ -174,7 +174,7 @@ An example of a watcher in action would be:
|
||||||
class MyComponent extends React.Component {
|
class MyComponent extends React.Component {
|
||||||
settingWatcherRef = null;
|
settingWatcherRef = null;
|
||||||
|
|
||||||
componentWillMount() {
|
componentDidMount() {
|
||||||
const callback = (settingName, roomId, level, newValAtLevel, newVal) => {
|
const callback = (settingName, roomId, level, newValAtLevel, newVal) => {
|
||||||
this.setState({ color: newVal });
|
this.setState({ color: newVal });
|
||||||
};
|
};
|
||||||
|
|
|
@ -63,6 +63,7 @@
|
||||||
"@matrix-org/analytics-events": "^0.6.0",
|
"@matrix-org/analytics-events": "^0.6.0",
|
||||||
"@matrix-org/matrix-wysiwyg": "^2.4.1",
|
"@matrix-org/matrix-wysiwyg": "^2.4.1",
|
||||||
"@matrix-org/react-sdk-module-api": "^1.0.0",
|
"@matrix-org/react-sdk-module-api": "^1.0.0",
|
||||||
|
"@matrix-org/spec": "^1.7.0",
|
||||||
"@sentry/browser": "^7.0.0",
|
"@sentry/browser": "^7.0.0",
|
||||||
"@sentry/tracing": "^7.0.0",
|
"@sentry/tracing": "^7.0.0",
|
||||||
"@testing-library/react-hooks": "^8.0.1",
|
"@testing-library/react-hooks": "^8.0.1",
|
||||||
|
|
|
@ -16,9 +16,10 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Device } from "matrix-js-sdk/src/matrix";
|
import { Device } from "matrix-js-sdk/src/matrix";
|
||||||
import { GeneratedSas } from "matrix-js-sdk/src/crypto-api/verification";
|
import { GeneratedSas, EmojiMapping } from "matrix-js-sdk/src/crypto-api/verification";
|
||||||
|
import SasEmoji from "@matrix-org/spec/sas-emoji.json";
|
||||||
|
|
||||||
import { _t, _td } from "../../../languageHandler";
|
import { _t, getNormalizedLanguageKeys, getUserLanguage } from "../../../languageHandler";
|
||||||
import { PendingActionSpinner } from "../right_panel/EncryptionInfo";
|
import { PendingActionSpinner } from "../right_panel/EncryptionInfo";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import { fixupColorFonts } from "../../../utils/FontManager";
|
import { fixupColorFonts } from "../../../utils/FontManager";
|
||||||
|
@ -42,19 +43,50 @@ interface IState {
|
||||||
cancelling?: boolean;
|
cancelling?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convert the names of emojis returned by the js-sdk into the display names, which we use as
|
const SasEmojiMap = new Map<
|
||||||
* a base for our translations.
|
string, // lowercase
|
||||||
|
{
|
||||||
|
description: string;
|
||||||
|
translations: {
|
||||||
|
[normalizedLanguageKey: string]: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
>(
|
||||||
|
SasEmoji.map(({ description, translated_descriptions: translations }) => [
|
||||||
|
description.toLowerCase(),
|
||||||
|
{
|
||||||
|
description,
|
||||||
|
// Normalize the translation keys
|
||||||
|
translations: Object.keys(translations).reduce<Record<string, string>>((o, k) => {
|
||||||
|
for (const key of getNormalizedLanguageKeys(k)) {
|
||||||
|
o[key] = translations[k as keyof typeof translations]!;
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}, {}),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate given EmojiMapping into the target locale
|
||||||
|
* @param mapping - the given EmojiMapping to translate
|
||||||
|
* @param locale - the BCP 47 locale to translate to, will fall back to English as the base locale for Matrix SAS Emoji.
|
||||||
*/
|
*/
|
||||||
function capFirst(s: string): string {
|
export function tEmoji(mapping: EmojiMapping, locale: string): string {
|
||||||
// Our translations (currently) have names like "Thumbs up".
|
const name = mapping[1];
|
||||||
//
|
const emoji = SasEmojiMap.get(name.toLowerCase());
|
||||||
// With legacy crypto, the js-sdk returns lower-case names ("thumbs up"). With Rust crypto, the js-sdk follows
|
if (!emoji) {
|
||||||
// the spec and returns title-case names ("Thumbs Up"). So, to convert both into names that match our i18n data,
|
console.warn("Emoji not found for translation", name);
|
||||||
// we upcase the first character and downcase the rest.
|
return name;
|
||||||
//
|
}
|
||||||
// Once legacy crypto is dead, we could consider getting rid of this and just making the i18n data use the
|
|
||||||
// title-case names (which would also match the spec).
|
for (const key of getNormalizedLanguageKeys(locale)) {
|
||||||
return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
|
if (!!emoji.translations[key]) {
|
||||||
|
return emoji.translations[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return emoji.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class VerificationShowSas extends React.Component<IProps, IState> {
|
export default class VerificationShowSas extends React.Component<IProps, IState> {
|
||||||
|
@ -64,9 +96,7 @@ export default class VerificationShowSas extends React.Component<IProps, IState>
|
||||||
this.state = {
|
this.state = {
|
||||||
pending: false,
|
pending: false,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
public componentWillMount(): void {
|
|
||||||
// As this component is also used before login (during complete security),
|
// As this component is also used before login (during complete security),
|
||||||
// also make sure we have a working emoji font to display the SAS emojis here.
|
// also make sure we have a working emoji font to display the SAS emojis here.
|
||||||
// This is also done from LoggedInView.
|
// This is also done from LoggedInView.
|
||||||
|
@ -84,13 +114,15 @@ export default class VerificationShowSas extends React.Component<IProps, IState>
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
|
const locale = getUserLanguage();
|
||||||
|
|
||||||
let sasDisplay;
|
let sasDisplay;
|
||||||
let sasCaption;
|
let sasCaption;
|
||||||
if (this.props.sas.emoji) {
|
if (this.props.sas.emoji) {
|
||||||
const emojiBlocks = this.props.sas.emoji.map((emoji, i) => (
|
const emojiBlocks = this.props.sas.emoji.map((emoji, i) => (
|
||||||
<div className="mx_VerificationShowSas_emojiSas_block" key={i}>
|
<div className="mx_VerificationShowSas_emojiSas_block" key={i}>
|
||||||
<div className="mx_VerificationShowSas_emojiSas_emoji">{emoji[0]}</div>
|
<div className="mx_VerificationShowSas_emojiSas_emoji">{emoji[0]}</div>
|
||||||
<div className="mx_VerificationShowSas_emojiSas_label">{_t(capFirst(emoji[1]))}</div>
|
<div className="mx_VerificationShowSas_emojiSas_label">{tEmoji(emoji, locale)}</div>
|
||||||
</div>
|
</div>
|
||||||
));
|
));
|
||||||
sasDisplay = (
|
sasDisplay = (
|
||||||
|
@ -171,69 +203,3 @@ export default class VerificationShowSas extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// List of Emoji strings from the js-sdk, for i18n
|
|
||||||
_td("Dog");
|
|
||||||
_td("Cat");
|
|
||||||
_td("Lion");
|
|
||||||
_td("Horse");
|
|
||||||
_td("Unicorn");
|
|
||||||
_td("Pig");
|
|
||||||
_td("Elephant");
|
|
||||||
_td("Rabbit");
|
|
||||||
_td("Panda");
|
|
||||||
_td("Rooster");
|
|
||||||
_td("Penguin");
|
|
||||||
_td("Turtle");
|
|
||||||
_td("Fish");
|
|
||||||
_td("Octopus");
|
|
||||||
_td("Butterfly");
|
|
||||||
_td("Flower");
|
|
||||||
_td("Tree");
|
|
||||||
_td("Cactus");
|
|
||||||
_td("Mushroom");
|
|
||||||
_td("Globe");
|
|
||||||
_td("Moon");
|
|
||||||
_td("Cloud");
|
|
||||||
_td("Fire");
|
|
||||||
_td("Banana");
|
|
||||||
_td("Apple");
|
|
||||||
_td("Strawberry");
|
|
||||||
_td("Corn");
|
|
||||||
_td("Pizza");
|
|
||||||
_td("Cake");
|
|
||||||
_td("Heart");
|
|
||||||
_td("Smiley");
|
|
||||||
_td("Robot");
|
|
||||||
_td("Hat");
|
|
||||||
_td("Glasses");
|
|
||||||
_td("Spanner");
|
|
||||||
_td("Santa");
|
|
||||||
_td("Thumbs up");
|
|
||||||
_td("Umbrella");
|
|
||||||
_td("Hourglass");
|
|
||||||
_td("Clock");
|
|
||||||
_td("Gift");
|
|
||||||
_td("Light bulb");
|
|
||||||
_td("Book");
|
|
||||||
_td("Pencil");
|
|
||||||
_td("Paperclip");
|
|
||||||
_td("Scissors");
|
|
||||||
_td("Lock");
|
|
||||||
_td("Key");
|
|
||||||
_td("Hammer");
|
|
||||||
_td("Telephone");
|
|
||||||
_td("Flag");
|
|
||||||
_td("Train");
|
|
||||||
_td("Bicycle");
|
|
||||||
_td("Aeroplane");
|
|
||||||
_td("Rocket");
|
|
||||||
_td("Trophy");
|
|
||||||
_td("Ball");
|
|
||||||
_td("Guitar");
|
|
||||||
_td("Trumpet");
|
|
||||||
_td("Bell");
|
|
||||||
_td("Anchor");
|
|
||||||
_td("Headphones");
|
|
||||||
_td("Folder");
|
|
||||||
_td("Pin");
|
|
||||||
|
|
|
@ -1221,69 +1221,6 @@
|
||||||
"They don't match": "They don't match",
|
"They don't match": "They don't match",
|
||||||
"They match": "They match",
|
"They match": "They match",
|
||||||
"To be secure, do this in person or use a trusted way to communicate.": "To be secure, do this in person or use a trusted way to communicate.",
|
"To be secure, do this in person or use a trusted way to communicate.": "To be secure, do this in person or use a trusted way to communicate.",
|
||||||
"Dog": "Dog",
|
|
||||||
"Cat": "Cat",
|
|
||||||
"Lion": "Lion",
|
|
||||||
"Horse": "Horse",
|
|
||||||
"Unicorn": "Unicorn",
|
|
||||||
"Pig": "Pig",
|
|
||||||
"Elephant": "Elephant",
|
|
||||||
"Rabbit": "Rabbit",
|
|
||||||
"Panda": "Panda",
|
|
||||||
"Rooster": "Rooster",
|
|
||||||
"Penguin": "Penguin",
|
|
||||||
"Turtle": "Turtle",
|
|
||||||
"Fish": "Fish",
|
|
||||||
"Octopus": "Octopus",
|
|
||||||
"Butterfly": "Butterfly",
|
|
||||||
"Flower": "Flower",
|
|
||||||
"Tree": "Tree",
|
|
||||||
"Cactus": "Cactus",
|
|
||||||
"Mushroom": "Mushroom",
|
|
||||||
"Globe": "Globe",
|
|
||||||
"Moon": "Moon",
|
|
||||||
"Cloud": "Cloud",
|
|
||||||
"Fire": "Fire",
|
|
||||||
"Banana": "Banana",
|
|
||||||
"Apple": "Apple",
|
|
||||||
"Strawberry": "Strawberry",
|
|
||||||
"Corn": "Corn",
|
|
||||||
"Pizza": "Pizza",
|
|
||||||
"Cake": "Cake",
|
|
||||||
"Heart": "Heart",
|
|
||||||
"Smiley": "Smiley",
|
|
||||||
"Robot": "Robot",
|
|
||||||
"Hat": "Hat",
|
|
||||||
"Glasses": "Glasses",
|
|
||||||
"Spanner": "Spanner",
|
|
||||||
"Santa": "Santa",
|
|
||||||
"Thumbs up": "Thumbs up",
|
|
||||||
"Umbrella": "Umbrella",
|
|
||||||
"Hourglass": "Hourglass",
|
|
||||||
"Clock": "Clock",
|
|
||||||
"Gift": "Gift",
|
|
||||||
"Light bulb": "Light bulb",
|
|
||||||
"Book": "Book",
|
|
||||||
"Pencil": "Pencil",
|
|
||||||
"Paperclip": "Paperclip",
|
|
||||||
"Scissors": "Scissors",
|
|
||||||
"Lock": "Lock",
|
|
||||||
"Key": "Key",
|
|
||||||
"Hammer": "Hammer",
|
|
||||||
"Telephone": "Telephone",
|
|
||||||
"Flag": "Flag",
|
|
||||||
"Train": "Train",
|
|
||||||
"Bicycle": "Bicycle",
|
|
||||||
"Aeroplane": "Aeroplane",
|
|
||||||
"Rocket": "Rocket",
|
|
||||||
"Trophy": "Trophy",
|
|
||||||
"Ball": "Ball",
|
|
||||||
"Guitar": "Guitar",
|
|
||||||
"Trumpet": "Trumpet",
|
|
||||||
"Bell": "Bell",
|
|
||||||
"Anchor": "Anchor",
|
|
||||||
"Headphones": "Headphones",
|
|
||||||
"Folder": "Folder",
|
|
||||||
"Welcome": "Welcome",
|
"Welcome": "Welcome",
|
||||||
"Secure messaging for friends and family": "Secure messaging for friends and family",
|
"Secure messaging for friends and family": "Secure messaging for friends and family",
|
||||||
"With free end-to-end encrypted messaging, and unlimited voice and video calls, %(brand)s is a great way to stay in touch.": "With free end-to-end encrypted messaging, and unlimited voice and video calls, %(brand)s is a great way to stay in touch.",
|
"With free end-to-end encrypted messaging, and unlimited voice and video calls, %(brand)s is a great way to stay in touch.": "With free end-to-end encrypted messaging, and unlimited voice and video calls, %(brand)s is a great way to stay in touch.",
|
||||||
|
|
31
test/components/views/VerificationShowSas-test.tsx
Normal file
31
test/components/views/VerificationShowSas-test.tsx
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { EmojiMapping } from "matrix-js-sdk/src/crypto-api/verification";
|
||||||
|
|
||||||
|
import { tEmoji } from "../../../src/components/views/verification/VerificationShowSas";
|
||||||
|
|
||||||
|
describe("tEmoji", () => {
|
||||||
|
it.each([
|
||||||
|
["en-GB", "Dog"],
|
||||||
|
["en", "Dog"],
|
||||||
|
["de-DE", "Hund"],
|
||||||
|
["pt", "Cachorro"],
|
||||||
|
])("should handle locale %s", (locale, expectation) => {
|
||||||
|
const emoji: EmojiMapping = ["🐶", "Dog"];
|
||||||
|
expect(tEmoji(emoji, locale)).toEqual(expectation);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1874,6 +1874,11 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.17.9"
|
"@babel/runtime" "^7.17.9"
|
||||||
|
|
||||||
|
"@matrix-org/spec@^1.7.0":
|
||||||
|
version "1.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@matrix-org/spec/-/spec-1.7.0.tgz#8a6b93edf0d99f8a6e0a25eea8613b5ada3e6b56"
|
||||||
|
integrity sha512-sLRdmk64dNd7X+jXgWFEatJbf2BOFX/a1VxHqWWTerzZntKsjKzz42sD2Mj1QWrsGp01u99fRNU8oy4DcmFn3w==
|
||||||
|
|
||||||
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3":
|
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3":
|
||||||
version "2.1.8-no-fsevents.3"
|
version "2.1.8-no-fsevents.3"
|
||||||
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b"
|
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b"
|
||||||
|
|
Loading…
Reference in a new issue