mirror of https://github.com/vector-im/riot-web
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 * Delintpull/28217/head
parent
3d2d08b132
commit
d81f71f993
|
@ -174,7 +174,7 @@ An example of a watcher in action would be:
|
|||
class MyComponent extends React.Component {
|
||||
settingWatcherRef = null;
|
||||
|
||||
componentWillMount() {
|
||||
componentDidMount() {
|
||||
const callback = (settingName, roomId, level, newValAtLevel, newVal) => {
|
||||
this.setState({ color: newVal });
|
||||
};
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
"@matrix-org/analytics-events": "^0.6.0",
|
||||
"@matrix-org/matrix-wysiwyg": "^2.4.1",
|
||||
"@matrix-org/react-sdk-module-api": "^1.0.0",
|
||||
"@matrix-org/spec": "^1.7.0",
|
||||
"@sentry/browser": "^7.0.0",
|
||||
"@sentry/tracing": "^7.0.0",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
|
|
|
@ -16,9 +16,10 @@ limitations under the License.
|
|||
|
||||
import React from "react";
|
||||
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 AccessibleButton from "../elements/AccessibleButton";
|
||||
import { fixupColorFonts } from "../../../utils/FontManager";
|
||||
|
@ -42,19 +43,50 @@ interface IState {
|
|||
cancelling?: boolean;
|
||||
}
|
||||
|
||||
/** Convert the names of emojis returned by the js-sdk into the display names, which we use as
|
||||
* a base for our translations.
|
||||
const SasEmojiMap = new Map<
|
||||
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 {
|
||||
// Our translations (currently) have names like "Thumbs up".
|
||||
//
|
||||
// With legacy crypto, the js-sdk returns lower-case names ("thumbs up"). With Rust crypto, the js-sdk follows
|
||||
// the spec and returns title-case names ("Thumbs Up"). So, to convert both into names that match our i18n data,
|
||||
// we upcase the first character and downcase the rest.
|
||||
//
|
||||
// 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).
|
||||
return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
|
||||
export function tEmoji(mapping: EmojiMapping, locale: string): string {
|
||||
const name = mapping[1];
|
||||
const emoji = SasEmojiMap.get(name.toLowerCase());
|
||||
if (!emoji) {
|
||||
console.warn("Emoji not found for translation", name);
|
||||
return name;
|
||||
}
|
||||
|
||||
for (const key of getNormalizedLanguageKeys(locale)) {
|
||||
if (!!emoji.translations[key]) {
|
||||
return emoji.translations[key];
|
||||
}
|
||||
}
|
||||
|
||||
return emoji.description;
|
||||
}
|
||||
|
||||
export default class VerificationShowSas extends React.Component<IProps, IState> {
|
||||
|
@ -64,9 +96,7 @@ export default class VerificationShowSas extends React.Component<IProps, IState>
|
|||
this.state = {
|
||||
pending: false,
|
||||
};
|
||||
}
|
||||
|
||||
public componentWillMount(): void {
|
||||
// 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.
|
||||
// This is also done from LoggedInView.
|
||||
|
@ -84,13 +114,15 @@ export default class VerificationShowSas extends React.Component<IProps, IState>
|
|||
};
|
||||
|
||||
public render(): React.ReactNode {
|
||||
const locale = getUserLanguage();
|
||||
|
||||
let sasDisplay;
|
||||
let sasCaption;
|
||||
if (this.props.sas.emoji) {
|
||||
const emojiBlocks = this.props.sas.emoji.map((emoji, i) => (
|
||||
<div className="mx_VerificationShowSas_emojiSas_block" key={i}>
|
||||
<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>
|
||||
));
|
||||
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 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.",
|
||||
"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",
|
||||
"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.",
|
||||
|
|
|
@ -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:
|
||||
"@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":
|
||||
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"
|
||||
|
|
Loading…
Reference in New Issue