Added translation to custom emoji + stickers

This commit is contained in:
Dylan 2022-08-07 13:28:41 +09:30
parent 7627d43e92
commit e4600106b8
7 changed files with 128 additions and 51 deletions

View file

@ -740,6 +740,57 @@
"restricted_unsupported": "Restricted (Unsupported: room required upgrade)",
"public": "Public (anyone can join)"
},
"RoomEmojis": {
"header": "Create Pack",
"submit_button": "Create pack",
"name_placeholder": "Pack Name",
"no_packs": "No emoji or sticker packs."
},
"ImagePack": {
"rename_title": "Rename",
"rename_button": "Rename",
"shortcode_label": "Shortcode",
"shortcode_name": "shortcode",
"delete_item_title": "Delete",
"delete_item_message": "Are you sure that you want to delete {{key}}?",
"delete_item_button": "Delete",
"delete_pack_title": "Delete Pack",
"delete_pack_message": "Are you sure that you want to delete {{pack_name}}?",
"delete_pack_button": "Delete",
"image_title": "Image",
"image_shortcode": "Shortcode",
"image_usage": "Usage",
"view_more": "View {{count}} more",
"view_less": "View less",
"use_globally": "Use globally",
"use_globally_description": "Add this pack to your account to use in all rooms.",
"no_global_packs": "No global packs",
"default_personal_pack_name": "Personal"
},
"ImagePackItem": {
"rename_tooltip": "Rename",
"delete_tooltip": "Delete"
},
"ImagePackUsageSelector": {
"header": "Usage",
"type_emoji": "Emoji",
"type_sticker": "Sticker",
"type_both": "Both"
},
"ImagePackUpload": {
"remove_file_tooltip": "Remove file",
"import_image_button": "Import image",
"shortcode_placeholder": "shortcode",
"upload_button": "Upload",
"upload_button_progress": "Uploading..."
},
"ImagePackProfile": {
"attribution_prompt": "Attribution",
"name_prompt": "Name",
"edit_tooltip": "Edit",
"usage_header": "Usage",
"pack_usage": "Pack usage"
},
"SpaceAddExisting": {
"adding_items_one": "Adding one item...",
"adding_items_other": "Adding {{count}} items...",

View file

@ -3,6 +3,7 @@ import React, {
} from 'react';
import PropTypes from 'prop-types';
import './ImagePack.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix';
import { openReusableDialog } from '../../../client/action/navigation';
@ -19,12 +20,14 @@ import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
import ImagePackProfile from './ImagePackProfile';
import ImagePackItem from './ImagePackItem';
import ImagePackUpload from './ImagePackUpload';
import '../../i18n';
const renameImagePackItem = (shortcode) => new Promise((resolve) => {
let isCompleted = false;
const { t } = useTranslation();
openReusableDialog(
<Text variant="s1" weight="medium">Rename</Text>,
<Text variant="s1" weight="medium">{t('Molecules.ImagePack.rename_title')}</Text>,
(requestClose) => (
<div style={{ padding: 'var(--sp-normal)' }}>
<form
@ -39,13 +42,13 @@ const renameImagePackItem = (shortcode) => new Promise((resolve) => {
>
<Input
value={shortcode}
name="shortcode"
label="Shortcode"
name={t('Molecules.ImagePack.shortcode_name')}
label={t('Molecules.ImagePack.shortcode_label')}
autoFocus
required
/>
<div style={{ height: 'var(--sp-normal)' }} />
<Button variant="primary" type="submit">Rename</Button>
<Button variant="primary" type="submit">{t('Molecules.ImagePack.rename_button')}</Button>
</form>
</div>
),
@ -94,11 +97,12 @@ function useRoomImagePack(roomId, stateKey) {
}
function useUserImagePack() {
const { t } = useTranslation();
const mx = initMatrix.matrixClient;
const packEvent = mx.getAccountData('im.ponies.user_emotes');
const pack = useMemo(() => (
ImagePackBuilder.parsePack(mx.getUserId(), packEvent?.getContent() ?? {
pack: { display_name: 'Personal' },
pack: { display_name: t('Molecules.ImagePack.default_personal_pack_name') },
images: {},
})
), []);
@ -115,6 +119,7 @@ function useUserImagePack() {
function useImagePackHandles(pack, sendPackContent) {
const [, forceUpdate] = useReducer((count) => count + 1, 0);
const { t } = useTranslation();
const getNewKey = (key) => {
if (typeof key !== 'string') return undefined;
@ -161,9 +166,9 @@ function useImagePackHandles(pack, sendPackContent) {
};
const handleDeleteItem = async (key) => {
const isConfirmed = await confirmDialog(
'Delete',
`Are you sure that you want to delete "${key}"?`,
'Delete',
t('Molecules.ImagePack.delete_item_title'),
t('Molecules.ImagePack.delete_item_message', { key }),
t('Molecules.ImagePack.delete_item_button'),
'danger',
);
if (!isConfirmed) return;
@ -227,6 +232,7 @@ function ImagePack({ roomId, stateKey, handlePackDelete }) {
const room = mx.getRoom(roomId);
const [viewMore, setViewMore] = useState(false);
const [isGlobal, setIsGlobal] = useState(isGlobalPack(roomId, stateKey));
const { t } = useTranslation();
const { pack, sendPackContent } = useRoomImagePack(roomId, stateKey);
@ -251,9 +257,9 @@ function ImagePack({ roomId, stateKey, handlePackDelete }) {
const handleDeletePack = async () => {
const isConfirmed = await confirmDialog(
'Delete Pack',
`Are you sure that you want to delete "${pack.displayName}"?`,
'Delete',
t('Molecules.ImagePack.delete_pack_title'),
t('Molecules.ImagePack.delete_pack_message', { pack_name: pack.displayName }),
t('Molecules.ImagePack.delete_pack_button'),
'danger',
);
if (!isConfirmed) return;
@ -279,9 +285,9 @@ function ImagePack({ roomId, stateKey, handlePackDelete }) {
{ images.length === 0 ? null : (
<div>
<div className="image-pack__header">
<Text variant="b3">Image</Text>
<Text variant="b3">Shortcode</Text>
<Text variant="b3">Usage</Text>
<Text variant="b3">{t('Molecules.ImagePack.image_title')}</Text>
<Text variant="b3">{t('Molecules.ImagePack.image_shortcode')}</Text>
<Text variant="b3">{t('Molecules.ImagePack.image_usage')}</Text>
</div>
{images.map(([shortcode, image]) => (
<ImagePackItem
@ -302,19 +308,19 @@ function ImagePack({ roomId, stateKey, handlePackDelete }) {
<Button onClick={() => setViewMore(!viewMore)}>
{
viewMore
? 'View less'
: `View ${pack.images.size - 2} more`
? t('Molecules.ImagePack.view_less')
: t('Molecules.ImagePack.view_more', { count: pack.images.size - 2 })
}
</Button>
)}
{ handlePackDelete && <Button variant="danger" onClick={handleDeletePack}>Delete Pack</Button>}
{ handlePackDelete && <Button variant="danger" onClick={handleDeletePack}>{t('Molecules.ImagePack.delete_pack_title')}</Button>}
</div>
)}
<div className="image-pack__global">
<Checkbox variant="positive" onToggle={handleGlobalChange} isActive={isGlobal} />
<div>
<Text variant="b2">Use globally</Text>
<Text variant="b3">Add this pack to your account to use in all rooms.</Text>
<Text variant="b2">{t('Molecules.ImagePack.use_globally')}</Text>
<Text variant="b3">{t('Molecules.ImagePack.use_globally_description')}</Text>
</div>
</div>
</div>
@ -333,6 +339,7 @@ ImagePack.propTypes = {
function ImagePackUser() {
const mx = initMatrix.matrixClient;
const [viewMore, setViewMore] = useState(false);
const { t } = useTranslation();
const { pack, sendPackContent } = useUserImagePack();
@ -363,9 +370,9 @@ function ImagePackUser() {
{ images.length === 0 ? null : (
<div>
<div className="image-pack__header">
<Text variant="b3">Image</Text>
<Text variant="b3">Shortcode</Text>
<Text variant="b3">Usage</Text>
<Text variant="b3">{t('Molecules.ImagePack.image_title')}</Text>
<Text variant="b3">{t('Molecules.ImagePack.image_shortcode')}</Text>
<Text variant="b3">{t('Molecules.ImagePack.image_usage')}</Text>
</div>
{images.map(([shortcode, image]) => (
<ImagePackItem
@ -385,8 +392,9 @@ function ImagePackUser() {
<Button onClick={() => setViewMore(!viewMore)}>
{
viewMore
? 'View less'
: `View ${pack.images.size - 2} more`
? t('Molecules.ImagePack.view_less')
: t('Molecules.ImagePack.view_more', { count: pack.images.size - 2 })
}
</Button>
</div>
@ -427,6 +435,7 @@ function useGlobalImagePack() {
function ImagePackGlobal() {
const mx = initMatrix.matrixClient;
const roomIdToStateKeys = useGlobalImagePack();
const { t } = useTranslation();
const handleChange = (roomId, stateKey) => {
removeGlobalImagePack(mx, roomId, stateKey);
@ -457,7 +466,7 @@ function ImagePackGlobal() {
})
);
})
: <div className="image-pack-global__empty"><Text>No global packs</Text></div>
: <div className="image-pack-global__empty"><Text>{t('Molecules.ImagePack.no_global_packs')}</Text></div>
}
</div>
</div>

View file

@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import './ImagePackItem.scss';
import { useTranslation } from 'react-i18next';
import { openReusableContextMenu } from '../../../client/action/navigation';
import { getEventCords } from '../../../util/common';
@ -16,9 +17,12 @@ import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.s
import PencilIC from '../../../../public/res/ic/outlined/pencil.svg';
import BinIC from '../../../../public/res/ic/outlined/bin.svg';
import '../../i18n';
function ImagePackItem({
url, shortcode, usage, onUsageChange, onDelete, onRename,
}) {
const { t } = useTranslation();
const handleUsageSelect = (event) => {
openReusableContextMenu(
'bottom',
@ -43,15 +47,15 @@ function ImagePackItem({
</div>
<div className="image-pack-item__usage">
<div className="image-pack-item__btn">
{onRename && <IconButton tooltip="Rename" size="extra-small" src={PencilIC} onClick={() => onRename(shortcode)} />}
{onDelete && <IconButton tooltip="Delete" size="extra-small" src={BinIC} onClick={() => onDelete(shortcode)} />}
{onRename && <IconButton tooltip={t('Molecules.ImagePackItem.rename_tooltip')} size="extra-small" src={PencilIC} onClick={() => onRename(shortcode)} />}
{onDelete && <IconButton tooltip={t('Molecules.ImagePackItem.delete_tooltip')} size="extra-small" src={BinIC} onClick={() => onDelete(shortcode)} />}
</div>
<Button onClick={onUsageChange ? handleUsageSelect : undefined}>
{onUsageChange && <RawIcon src={ChevronBottomIC} size="extra-small" />}
<Text variant="b2">
{usage === 'emoticon' && 'Emoji'}
{usage === 'sticker' && 'Sticker'}
{usage === 'both' && 'Both'}
{usage === 'emoticon' && t('Molecules.ImagePackUsageSelector.type_emoji')}
{usage === 'sticker' && t('Molecules.ImagePackUsageSelector.type_sticker')}
{usage === 'both' && t('Molecules.ImagePackUsageSelector.type_both')}
</Text>
</Button>
</div>

View file

@ -2,6 +2,7 @@ import React, { useState } from 'react';
import PropTypes from 'prop-types';
import './ImagePackProfile.scss';
import { useTranslation } from 'react-i18next';
import { openReusableContextMenu } from '../../../client/action/navigation';
import { getEventCords } from '../../../util/common';
@ -16,11 +17,14 @@ import ImagePackUsageSelector from './ImagePackUsageSelector';
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
import PencilIC from '../../../../public/res/ic/outlined/pencil.svg';
import '../../i18n';
function ImagePackProfile({
avatarUrl, displayName, attribution, usage,
onUsageChange, onAvatarChange, onEditProfile,
}) {
const [isEdit, setIsEdit] = useState(false);
const { t } = useTranslation();
const handleSubmit = (e) => {
e.preventDefault();
@ -70,18 +74,18 @@ function ImagePackProfile({
isEdit
? (
<form onSubmit={handleSubmit}>
<Input name="nameInput" label="Name" value={displayName} required />
<Input name="attributionInput" label="Attribution" value={attribution} resizable />
<Input name="nameInput" label={t('Molecules.ImagePackProfile.name_prompt')} value={displayName} required />
<Input name="attributionInput" label={t('Molecules.ImagePackProfile.attribution_prompt')} value={attribution} resizable />
<div>
<Button variant="primary" type="submit">Save</Button>
<Button onClick={() => setIsEdit(false)}>Cancel</Button>
<Button variant="primary" type="submit">{t('common.save')}</Button>
<Button onClick={() => setIsEdit(false)}>{t('common.cancel')}</Button>
</div>
</form>
) : (
<>
<div>
<Text>{displayName}</Text>
{onEditProfile && <IconButton size="extra-small" onClick={() => setIsEdit(true)} src={PencilIC} tooltip="Edit" />}
{onEditProfile && <IconButton size="extra-small" onClick={() => setIsEdit(true)} src={PencilIC} tooltip={t('Molecules.ImagePackProfile.edit_tooltip')} />}
</div>
{attribution && <Text variant="b3">{attribution}</Text>}
</>
@ -89,15 +93,15 @@ function ImagePackProfile({
}
</div>
<div className="image-pack-profile__usage">
<Text variant="b3">Pack usage</Text>
<Text variant="b3">{t('Molecules.ImagePackProfile.pack_usage')}</Text>
<Button
onClick={onUsageChange ? handleUsageSelect : undefined}
iconSrc={onUsageChange ? ChevronBottomIC : null}
>
<Text>
{usage === 'emoticon' && 'Emoji'}
{usage === 'sticker' && 'Sticker'}
{usage === 'both' && 'Both'}
{usage === 'emoticon' && t('Molecules.ImagePackUsageSelector.type_emoji')}
{usage === 'sticker' && t('Molecules.ImagePackUsageSelector.type_sticker')}
{usage === 'both' && t('Molecules.ImagePackUsageSelector.type_both')}
</Text>
</Button>
</div>

View file

@ -2,6 +2,7 @@ import React, { useState, useRef } from 'react';
import PropTypes from 'prop-types';
import './ImagePackUpload.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix';
import { scaleDownImage } from '../../../util/common';
@ -10,6 +11,7 @@ import Button from '../../atoms/button/Button';
import Input from '../../atoms/input/Input';
import IconButton from '../../atoms/button/IconButton';
import CirclePlusIC from '../../../../public/res/ic/outlined/circle-plus.svg';
import '../../i18n';
function ImagePackUpload({ onUpload }) {
const mx = initMatrix.matrixClient;
@ -17,6 +19,7 @@ function ImagePackUpload({ onUpload }) {
const shortcodeRef = useRef(null);
const [imgFile, setImgFile] = useState(null);
const [progress, setProgress] = useState(false);
const { t } = useTranslation();
const handleSubmit = async (evt) => {
evt.preventDefault();
@ -55,14 +58,14 @@ function ImagePackUpload({ onUpload }) {
imgFile
? (
<div className="image-pack-upload__file">
<IconButton onClick={handleRemove} src={CirclePlusIC} tooltip="Remove file" />
<IconButton onClick={handleRemove} src={CirclePlusIC} tooltip={t('Molecules.ImagePackUpload.remove_file_tooltip')} />
<Text>{imgFile.name}</Text>
</div>
)
: <Button onClick={() => inputRef.current.click()}>Import image</Button>
: <Button onClick={() => inputRef.current.click()}>{t('Molecules.ImagePackUpload.import_image_button')}</Button>
}
<Input forwardRef={shortcodeRef} name="shortcodeInput" placeholder="shortcode" required />
<Button disabled={progress} variant="primary" type="submit">{progress ? 'Uploading...' : 'Upload'}</Button>
<Input forwardRef={shortcodeRef} name="shortcodeInput" placeholder={t('Molecules.ImagePackUpload.shortcode_placeholder')} required />
<Button disabled={progress} variant="primary" type="submit">{progress ? t('Molecules.ImagePackUpload.upload_button_progress') : t('Molecules.ImagePackUpload.upload_button')}</Button>
</form>
);
}

View file

@ -1,33 +1,36 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { MenuHeader, MenuItem } from '../../atoms/context-menu/ContextMenu';
import CheckIC from '../../../../public/res/ic/outlined/check.svg';
import '../../i18n';
function ImagePackUsageSelector({ usage, onSelect }) {
const { t } = useTranslation();
return (
<div>
<MenuHeader>Usage</MenuHeader>
<MenuHeader>{t('Molecules.ImagePackUsageSelector.header')}</MenuHeader>
<MenuItem
iconSrc={usage === 'emoticon' ? CheckIC : undefined}
variant={usage === 'emoticon' ? 'positive' : 'surface'}
onClick={() => onSelect('emoticon')}
>
Emoji
{t('Molecules.ImagePackUsageSelector.type_emoji')}
</MenuItem>
<MenuItem
iconSrc={usage === 'sticker' ? CheckIC : undefined}
variant={usage === 'sticker' ? 'positive' : 'surface'}
onClick={() => onSelect('sticker')}
>
Sticker
{t('Molecules.ImagePackUsageSelector.type_sticker')}
</MenuItem>
<MenuItem
iconSrc={usage === 'both' ? CheckIC : undefined}
variant={usage === 'both' ? 'positive' : 'surface'}
onClick={() => onSelect('both')}
>
Both
{t('Molecules.ImagePackUsageSelector.type_both')}
</MenuItem>
</div>
);

View file

@ -1,6 +1,7 @@
import React, { useReducer, useEffect } from 'react';
import PropTypes from 'prop-types';
import './RoomEmojis.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix';
import { suffixRename } from '../../../util/common';
@ -10,6 +11,7 @@ import Text from '../../atoms/text/Text';
import Input from '../../atoms/input/Input';
import Button from '../../atoms/button/Button';
import ImagePack from '../image-pack/ImagePack';
import '../../i18n';
function useRoomPacks(room) {
const mx = initMatrix.matrixClient;
@ -79,6 +81,7 @@ function RoomEmojis({ roomId }) {
const room = mx.getRoom(roomId);
const { usablePacks, createPack, deletePack } = useRoomPacks(room);
const { t } = useTranslation();
const myPowerlevel = room.getMember(mx.getUserId())?.powerLevel || 0;
const canChange = room.currentState.hasSufficientPowerLevelFor('state_default', myPowerlevel);
@ -97,10 +100,10 @@ function RoomEmojis({ roomId }) {
<div className="room-emojis">
{ canChange && (
<div className="room-emojis__add-pack">
<MenuHeader>Create Pack</MenuHeader>
<MenuHeader>{t('Molecules.RoomEmojis.header')}</MenuHeader>
<form onSubmit={handlePackCreate}>
<Input name="nameInput" placeholder="Pack Name" required />
<Button variant="primary" type="submit">Create pack</Button>
<Input name="nameInput" placeholder={t('Molecules.RoomEmojis.name_placeholder')} required />
<Button variant="primary" type="submit">{t('Molecules.RoomEmojis.submit_button')}</Button>
</form>
</div>
)}
@ -115,7 +118,7 @@ function RoomEmojis({ roomId }) {
/>
)) : (
<div className="room-emojis__empty">
<Text>No emoji or sticker pack.</Text>
<Text>{t('Molecules.RoomEmojis.no_packs')}</Text>
</div>
)
}