Language tab
Save language
This commit is contained in:
parent
323b37d5e5
commit
585eb78ea2
@ -10,6 +10,7 @@ import Scrollable from "./scrollable";
|
||||
import { FocusDirection } from "../helpers/fastSmoothScroll";
|
||||
import CheckboxField from "./checkboxField";
|
||||
import appProfileManager from "../lib/appManagers/appProfileManager";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
|
||||
type PeerType = 'contacts' | 'dialogs' | 'channelParticipants';
|
||||
|
||||
@ -69,7 +70,7 @@ export default class AppSelectPeers {
|
||||
rippleEnabled?: boolean,
|
||||
avatarSize?: AppSelectPeers['avatarSize'],
|
||||
}) {
|
||||
Object.assign(this, options);
|
||||
safeAssign(this, options);
|
||||
|
||||
this.container.classList.add('selector');
|
||||
|
||||
@ -440,7 +441,7 @@ export default class AppSelectPeers {
|
||||
});
|
||||
}
|
||||
|
||||
public add(peerId: any, title?: string, scroll = true) {
|
||||
public add(peerId: any, title?: string | HTMLElement, scroll = true) {
|
||||
//console.trace('add');
|
||||
this.selected.add(peerId);
|
||||
|
||||
@ -467,7 +468,12 @@ export default class AppSelectPeers {
|
||||
}
|
||||
|
||||
if(title) {
|
||||
div.innerHTML = title;
|
||||
if(typeof(title) === 'string') {
|
||||
div.innerHTML = title;
|
||||
} else {
|
||||
div.innerHTML = '';
|
||||
div.append(title);
|
||||
}
|
||||
}
|
||||
|
||||
div.insertAdjacentElement('afterbegin', avatarEl);
|
||||
|
@ -4,6 +4,7 @@ import AvatarElement from "./avatar";
|
||||
import InputField from "./inputField";
|
||||
import ListenerSetter from "../helpers/listenerSetter";
|
||||
import Button from "./button";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
|
||||
export default class EditPeer {
|
||||
public nextBtn: HTMLButtonElement;
|
||||
@ -23,7 +24,7 @@ export default class EditPeer {
|
||||
listenerSetter: ListenerSetter,
|
||||
doNotEditAvatar?: boolean,
|
||||
}) {
|
||||
Object.assign(this, options);
|
||||
safeAssign(this, options);
|
||||
|
||||
this.nextBtn = Button('btn-circle btn-corner tgico-check');
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { getRichValue, isInputEmpty } from "../helpers/dom";
|
||||
import { debounce } from "../helpers/schedulers";
|
||||
import { checkRTL } from "../helpers/string";
|
||||
import { i18n_, LangPackKey } from "../lib/langPack";
|
||||
import { i18n, LangPackKey } from "../lib/langPack";
|
||||
import RichTextProcessor from "../lib/richtextprocessor";
|
||||
|
||||
let init = () => {
|
||||
@ -143,7 +143,7 @@ class InputField {
|
||||
|
||||
if(label) {
|
||||
this.label = document.createElement('label');
|
||||
i18n_({element: this.label, key: label});
|
||||
this.label.append(i18n(label));
|
||||
this.container.append(this.label);
|
||||
}
|
||||
|
||||
@ -234,9 +234,10 @@ class InputField {
|
||||
}
|
||||
}
|
||||
|
||||
public setState(state: InputState, label?: string) {
|
||||
public setState(state: InputState, label?: LangPackKey) {
|
||||
if(label) {
|
||||
this.label.innerHTML = label;
|
||||
this.label.innerHTML = '';
|
||||
this.label.append(i18n(label));
|
||||
}
|
||||
|
||||
this.input.classList.toggle('error', !!(state & InputState.Error));
|
||||
|
@ -2,6 +2,7 @@ import { isInDOM, cancelEvent, attachClickEvent } from "../helpers/dom";
|
||||
import { CancellablePromise } from "../helpers/cancellablePromise";
|
||||
import SetTransition from "./singleTransition";
|
||||
import { fastRaf } from "../helpers/schedulers";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
|
||||
const TRANSITION_TIME = 200;
|
||||
|
||||
@ -34,7 +35,7 @@ export default class ProgressivePreloader {
|
||||
attachMethod: ProgressivePreloader['attachMethod']
|
||||
}>) {
|
||||
if(options) {
|
||||
Object.assign(this, options);
|
||||
safeAssign(this, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,8 @@ import AppContactsTab from "./tabs/contacts";
|
||||
import AppArchivedTab from "./tabs/archivedTab";
|
||||
import AppAddMembersTab from "./tabs/addMembers";
|
||||
import { i18n_, LangPackKey } from "../../lib/langPack";
|
||||
import ButtonMenuToggle from "../buttonMenuToggle";
|
||||
import ButtonMenu, { ButtonMenuItemOptions } from "../buttonMenu";
|
||||
|
||||
export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown';
|
||||
|
||||
@ -34,14 +36,6 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
private inputSearch: InputSearch;
|
||||
|
||||
private menuEl: HTMLElement;
|
||||
private buttons: {
|
||||
newGroup: HTMLButtonElement,
|
||||
contacts: HTMLButtonElement,
|
||||
archived: HTMLButtonElement,
|
||||
saved: HTMLButtonElement,
|
||||
settings: HTMLButtonElement,
|
||||
help: HTMLButtonElement
|
||||
} = {} as any;
|
||||
public archivedCount: HTMLSpanElement;
|
||||
|
||||
private newBtnMenu: HTMLElement;
|
||||
@ -68,58 +62,87 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
const sidebarHeader = this.sidebarEl.querySelector('.item-main .sidebar-header');
|
||||
sidebarHeader.append(this.inputSearch.container);
|
||||
|
||||
const onNewGroupClick = () => {
|
||||
new AppAddMembersTab(this).open({
|
||||
peerId: 0,
|
||||
type: 'chat',
|
||||
skippable: false,
|
||||
takeOut: (peerIds) => {
|
||||
new AppNewGroupTab(this).open(peerIds);
|
||||
},
|
||||
title: 'Add Members',
|
||||
placeholder: 'Add People...'
|
||||
});
|
||||
};
|
||||
|
||||
const onContactsClick = () => {
|
||||
new AppContactsTab(this).open();
|
||||
};
|
||||
|
||||
this.toolsBtn = this.sidebarEl.querySelector('.sidebar-tools-button') as HTMLButtonElement;
|
||||
this.backBtn = this.sidebarEl.querySelector('.sidebar-back-button') as HTMLButtonElement;
|
||||
|
||||
const btnArchive: ButtonMenuItemOptions = {
|
||||
icon: 'archive',
|
||||
text: 'Archived',
|
||||
onClick: () => {
|
||||
new AppArchivedTab(this).open();
|
||||
}
|
||||
};
|
||||
|
||||
const btnMenu = ButtonMenu([{
|
||||
icon: 'newgroup',
|
||||
text: 'New Group',
|
||||
onClick: onNewGroupClick
|
||||
}, {
|
||||
icon: 'user',
|
||||
text: 'Contacts',
|
||||
onClick: onContactsClick
|
||||
}, btnArchive, {
|
||||
icon: 'savedmessages',
|
||||
text: 'Saved',
|
||||
onClick: () => {
|
||||
setTimeout(() => { // menu doesn't close if no timeout (lol)
|
||||
appImManager.setPeer(appImManager.myId);
|
||||
}, 0);
|
||||
}
|
||||
}, {
|
||||
icon: 'settings',
|
||||
text: 'Settings',
|
||||
onClick: () => {
|
||||
new AppSettingsTab(this).open();
|
||||
}
|
||||
}, {
|
||||
icon: 'help btn-disabled',
|
||||
text: 'Help',
|
||||
onClick: () => {
|
||||
|
||||
}
|
||||
}]);
|
||||
|
||||
btnMenu.classList.add('bottom-right');
|
||||
|
||||
this.toolsBtn.append(btnMenu);
|
||||
|
||||
this.menuEl = this.toolsBtn.querySelector('.btn-menu');
|
||||
this.newBtnMenu = this.sidebarEl.querySelector('#new-menu');
|
||||
|
||||
this.inputSearch.input.addEventListener('focus', () => this.initSearch(), {once: true});
|
||||
|
||||
parseMenuButtonsTo(this.buttons, this.menuEl.children);
|
||||
parseMenuButtonsTo(this.newButtons, this.newBtnMenu.firstElementChild.children);
|
||||
|
||||
this.archivedCount = this.buttons.archived.querySelector('.archived-count') as HTMLSpanElement;
|
||||
this.archivedCount = document.createElement('span');
|
||||
this.archivedCount.className = 'archived-count badge badge-24 badge-gray';
|
||||
|
||||
attachClickEvent(this.buttons.saved, (e) => {
|
||||
///////this.log('savedbtn click');
|
||||
setTimeout(() => { // menu doesn't close if no timeout (lol)
|
||||
appImManager.setPeer(appImManager.myId);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
attachClickEvent(this.buttons.archived, (e) => {
|
||||
new AppArchivedTab(this).open();
|
||||
});
|
||||
btnArchive.element.append(this.archivedCount);
|
||||
|
||||
[this.newButtons.privateChat, this.buttons.contacts].forEach(btn => {
|
||||
attachClickEvent(btn, (e) => {
|
||||
new AppContactsTab(this).open();
|
||||
});
|
||||
});
|
||||
|
||||
attachClickEvent(this.buttons.settings, (e) => {
|
||||
new AppSettingsTab(this).open();
|
||||
});
|
||||
attachClickEvent(this.newButtons.privateChat, onContactsClick);
|
||||
|
||||
attachClickEvent(this.newButtons.channel, (e) => {
|
||||
new AppNewChannelTab(this).open();
|
||||
});
|
||||
|
||||
[this.newButtons.group, this.buttons.newGroup].forEach(btn => {
|
||||
attachClickEvent(btn, (e) => {
|
||||
new AppAddMembersTab(this).open({
|
||||
peerId: 0,
|
||||
type: 'chat',
|
||||
skippable: false,
|
||||
takeOut: (peerIds) => {
|
||||
new AppNewGroupTab(this).open(peerIds);
|
||||
},
|
||||
title: 'Add Members',
|
||||
placeholder: 'Add People...'
|
||||
});
|
||||
});
|
||||
});
|
||||
attachClickEvent(this.newButtons.group, onNewGroupClick);
|
||||
|
||||
rootScope.on('dialogs_archived_unread', (e) => {
|
||||
this.archivedCount.innerText = '' + formatNumber(e.count, 1);
|
||||
|
@ -13,6 +13,7 @@ import ButtonMenuToggle from "../../buttonMenuToggle";
|
||||
import { ButtonMenuItemOptions } from "../../buttonMenu";
|
||||
import Button from "../../button";
|
||||
import AppIncludedChatsTab from "./includedChats";
|
||||
import { i18n, i18n_, LangPackKey } from "../../../lib/langPack";
|
||||
|
||||
const MAX_FOLDER_NAME_LENGTH = 12;
|
||||
|
||||
@ -38,7 +39,8 @@ export default class AppEditFolderTab extends SliderSuperTab {
|
||||
this.container.classList.add('edit-folder-container');
|
||||
this.caption = document.createElement('div');
|
||||
this.caption.classList.add('caption');
|
||||
this.caption.innerHTML = `Choose chats and types of chats that will<br>appear and never appear in this folder.`;
|
||||
this.caption.append(i18n(`Choose chats and types of chats that will
|
||||
appear and never appear in this folder.`));
|
||||
this.stickerContainer = document.createElement('div');
|
||||
this.stickerContainer.classList.add('sticker-container');
|
||||
|
||||
@ -72,13 +74,13 @@ export default class AppEditFolderTab extends SliderSuperTab {
|
||||
|
||||
inputWrapper.append(this.nameInputField.container);
|
||||
|
||||
const generateList = (className: string, h2Text: string, buttons: {icon: string, name?: string, withRipple?: true, text: string}[], to: any) => {
|
||||
const generateList = (className: string, h2Text: LangPackKey, buttons: {icon: string, name?: string, withRipple?: true, text: string}[], to: any) => {
|
||||
const container = document.createElement('div');
|
||||
container.classList.add('folder-list', className);
|
||||
|
||||
const h2 = document.createElement('div');
|
||||
h2.classList.add('sidebar-left-h2');
|
||||
h2.innerHTML = h2Text;
|
||||
i18n_({element: h2, key: h2Text});
|
||||
|
||||
const categories = document.createElement('div');
|
||||
categories.classList.add('folder-categories');
|
||||
@ -102,46 +104,46 @@ export default class AppEditFolderTab extends SliderSuperTab {
|
||||
return container;
|
||||
};
|
||||
|
||||
this.include_peers = generateList('folder-list-included', 'Included chats', [{
|
||||
this.include_peers = generateList('folder-list-included', 'ChatList.Filter.Include.Header', [{
|
||||
icon: 'add primary',
|
||||
text: 'Add Chats',
|
||||
text: 'ChatList.Filter.Include.AddChat',
|
||||
withRipple: true
|
||||
}, {
|
||||
text: 'Contacts',
|
||||
text: 'ChatList.Filter.Contacts',
|
||||
icon: 'newprivate',
|
||||
name: 'contacts'
|
||||
}, {
|
||||
text: 'Non-Contacts',
|
||||
text: 'ChatList.Filter.NonContacts',
|
||||
icon: 'noncontacts',
|
||||
name: 'non_contacts'
|
||||
}, {
|
||||
text: 'Groups',
|
||||
text: 'ChatList.Filter.Groups',
|
||||
icon: 'group',
|
||||
name: 'groups'
|
||||
}, {
|
||||
text: 'Channels',
|
||||
text: 'ChatList.Filter.Channels',
|
||||
icon: 'channel',
|
||||
name: 'broadcasts'
|
||||
}, {
|
||||
text: 'Bots',
|
||||
text: 'ChatList.Filter.Bots',
|
||||
icon: 'bots',
|
||||
name: 'bots'
|
||||
}], this.flags);
|
||||
|
||||
this.exclude_peers = generateList('folder-list-excluded', 'Excluded chats', [{
|
||||
this.exclude_peers = generateList('folder-list-excluded', 'ChatList.Filter.Exclude.Header', [{
|
||||
icon: 'minus primary',
|
||||
text: 'Remove Chats',
|
||||
text: 'ChatList.Filter.Exclude.AddChat',
|
||||
withRipple: true
|
||||
}, {
|
||||
text: 'Muted',
|
||||
text: 'ChatList.Filter.MutedChats',
|
||||
icon: 'mute',
|
||||
name: 'exclude_muted'
|
||||
}, {
|
||||
text: 'Archived',
|
||||
text: 'ChatList.Filter.Archive',
|
||||
icon: 'archive',
|
||||
name: 'exclude_archived'
|
||||
}, {
|
||||
text: 'Read',
|
||||
text: 'ChatList.Filter.ReadChats',
|
||||
icon: 'readchats',
|
||||
name: 'exclude_read'
|
||||
}], this.flags);
|
||||
@ -225,7 +227,7 @@ export default class AppEditFolderTab extends SliderSuperTab {
|
||||
|
||||
private onCreateOpen() {
|
||||
this.caption.style.display = '';
|
||||
this.title.innerText = 'New Folder';
|
||||
this.setTitle('New Folder');
|
||||
this.menuBtn.classList.add('hide');
|
||||
this.confirmBtn.classList.remove('hide');
|
||||
this.nameInputField.value = '';
|
||||
@ -238,7 +240,7 @@ export default class AppEditFolderTab extends SliderSuperTab {
|
||||
|
||||
private onEditOpen() {
|
||||
this.caption.style.display = 'none';
|
||||
this.title.innerText = this.type === 'create' ? 'New Folder' : 'Edit Folder';
|
||||
this.setTitle(this.type === 'create' ? 'New Folder' : 'Edit Folder');
|
||||
|
||||
if(this.type === 'edit') {
|
||||
this.menuBtn.classList.remove('hide');
|
||||
|
@ -5,7 +5,7 @@ import { SliderSuperTab } from "../../slider";
|
||||
import { attachClickEvent } from "../../../helpers/dom";
|
||||
import EditPeer from "../../editPeer";
|
||||
import { UsernameInputField } from "../../usernameInputField";
|
||||
import { i18n_ } from "../../../lib/langPack";
|
||||
import { i18n, i18n_ } from "../../../lib/langPack";
|
||||
|
||||
// TODO: аватарка не поменяется в этой вкладке после изменения почему-то (если поставить в другом клиенте, и потом тут проверить, для этого ещё вышел в чатлист)
|
||||
|
||||
@ -93,8 +93,21 @@ export default class AppEditProfileTab extends SliderSuperTab {
|
||||
|
||||
const caption = document.createElement('div');
|
||||
caption.classList.add('caption');
|
||||
caption.innerHTML = `You can choose a username on Telegram. If you do, other people will be able to find you by this username and contact you without knowing your phone number.<br><br>You can use a-z, 0-9 and underscores. Minimum length is 5 characters.<br><br><div class="profile-url-container">This link opens a chat with you:
|
||||
<br><a class="profile-url" href="#" target="_blank"></a></div>`;
|
||||
caption.append(i18n('UsernameSettings.ChangeDescription'));
|
||||
caption.append(document.createElement('br'), document.createElement('br'));
|
||||
|
||||
const profileUrlContainer = this.profileUrlContainer = document.createElement('div');
|
||||
profileUrlContainer.classList.add('profile-url-container');
|
||||
profileUrlContainer.append(i18n('This link opens a chat with you:'));
|
||||
|
||||
const profileUrlAnchor = this.profileUrlAnchor = document.createElement('a');
|
||||
profileUrlAnchor.classList.add('profile-url');
|
||||
profileUrlAnchor.href = '#';
|
||||
profileUrlAnchor.target = '_blank';
|
||||
|
||||
profileUrlContainer.append(profileUrlAnchor);
|
||||
|
||||
caption.append(profileUrlContainer);
|
||||
|
||||
this.profileUrlContainer = caption.querySelector('.profile-url-container');
|
||||
this.profileUrlAnchor = this.profileUrlContainer.lastElementChild as HTMLAnchorElement;
|
||||
|
@ -10,6 +10,7 @@ import ButtonIcon from "../../buttonIcon";
|
||||
import CheckboxField from "../../checkboxField";
|
||||
import Button from "../../button";
|
||||
import AppEditFolderTab from "./editFolder";
|
||||
import { i18n, LangPackKey, _i18n } from "../../../lib/langPack";
|
||||
|
||||
export default class AppIncludedChatsTab extends SliderSuperTab {
|
||||
private editFolderTab: AppEditFolderTab;
|
||||
@ -123,7 +124,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
|
||||
dom.containerEl.append(this.checkbox(selected));
|
||||
if(selected) dom.listEl.classList.add('active');
|
||||
|
||||
let subtitle = '';
|
||||
let subtitle: LangPackKey;
|
||||
|
||||
if(peerId > 0) {
|
||||
if(peerId === rootScope.myId) {
|
||||
@ -137,7 +138,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
|
||||
subtitle = appPeersManager.isBroadcast(peerId) ? 'Channel' : 'Group';
|
||||
}
|
||||
|
||||
dom.lastMessageSpan.innerHTML = subtitle;
|
||||
_i18n(dom.lastMessageSpan, subtitle);
|
||||
});
|
||||
};
|
||||
|
||||
@ -148,14 +149,14 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
|
||||
}
|
||||
|
||||
this.confirmBtn.style.display = this.type === 'excluded' ? '' : 'none';
|
||||
this.title.innerText = this.type === 'included' ? 'Included Chats' : 'Excluded Chats';
|
||||
this.setTitle(this.type === 'included' ? 'Included Chats' : 'Excluded Chats');
|
||||
|
||||
const filter = this.filter;
|
||||
|
||||
const fragment = document.createDocumentFragment();
|
||||
const dd = document.createElement('div');
|
||||
dd.classList.add('sidebar-left-h2');
|
||||
dd.innerText = 'Chat types';
|
||||
_i18n(dd, 'ChatList.Add.TopSeparator');
|
||||
|
||||
const categories = document.createElement('div');
|
||||
categories.classList.add('folder-categories');
|
||||
@ -163,17 +164,17 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
|
||||
let details: {[flag: string]: {ico: string, text: string}};
|
||||
if(this.type === 'excluded') {
|
||||
details = {
|
||||
exclude_muted: {ico: 'mute', text: 'Muted'},
|
||||
exclude_archived: {ico: 'archive', text: 'Archived'},
|
||||
exclude_read: {ico: 'readchats', text: 'Read'}
|
||||
exclude_muted: {ico: 'mute', text: 'ChatList.Filter.MutedChats'},
|
||||
exclude_archived: {ico: 'archive', text: 'ChatList.Filter.Archive'},
|
||||
exclude_read: {ico: 'readchats', text: 'ChatList.Filter.ReadChats'}
|
||||
};
|
||||
} else {
|
||||
details = {
|
||||
contacts: {ico: 'newprivate', text: 'Contacts'},
|
||||
non_contacts: {ico: 'noncontacts', text: 'Non-Contacts'},
|
||||
groups: {ico: 'group', text: 'Groups'},
|
||||
broadcasts: {ico: 'newchannel', text: 'Channels'},
|
||||
bots: {ico: 'bots', text: 'Bots'}
|
||||
contacts: {ico: 'newprivate', text: 'ChatList.Filter.Contacts'},
|
||||
non_contacts: {ico: 'noncontacts', text: 'ChatList.Filter.NonContacts'},
|
||||
groups: {ico: 'group', text: 'ChatList.Filter.Groups'},
|
||||
broadcasts: {ico: 'newchannel', text: 'ChatList.Filter.Channels'},
|
||||
bots: {ico: 'bots', text: 'ChatList.Filter.Bots'}
|
||||
};
|
||||
}
|
||||
|
||||
@ -191,7 +192,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
|
||||
|
||||
const d = document.createElement('div');
|
||||
d.classList.add('sidebar-left-h2');
|
||||
d.innerText = 'Chats';
|
||||
_i18n(d, 'ChatList.Add.BottomSeparator');
|
||||
|
||||
fragment.append(dd, categories, hr, d);
|
||||
|
||||
@ -210,7 +211,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
|
||||
|
||||
const _add = this.selector.add.bind(this.selector);
|
||||
this.selector.add = (peerId, title, scroll) => {
|
||||
const div = _add(peerId, details[peerId]?.text, scroll);
|
||||
const div = _add(peerId, details[peerId] ? i18n(details[peerId].text) : undefined, scroll);
|
||||
if(details[peerId]) {
|
||||
div.querySelector('avatar-element').classList.add('tgico-' + details[peerId].ico);
|
||||
}
|
||||
@ -256,4 +257,4 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
|
||||
|
||||
return super.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
107
src/components/sidebarLeft/tabs/language.ts
Normal file
107
src/components/sidebarLeft/tabs/language.ts
Normal file
@ -0,0 +1,107 @@
|
||||
import { SettingSection } from "..";
|
||||
import { randomLong } from "../../../helpers/random";
|
||||
import I18n from "../../../lib/langPack";
|
||||
import RadioField from "../../radioField";
|
||||
import Row, { RadioFormFromRows } from "../../row";
|
||||
import { SliderSuperTab } from "../../slider"
|
||||
|
||||
export default class AppLanguageTab extends SliderSuperTab {
|
||||
protected init() {
|
||||
this.container.classList.add('language-container');
|
||||
this.setTitle('Telegram.LanguageViewController');
|
||||
|
||||
const section = new SettingSection({});
|
||||
|
||||
const radioRows: Map<string, Row> = new Map();
|
||||
|
||||
let r = [{
|
||||
code: 'en',
|
||||
text: 'English',
|
||||
subtitle: 'English'
|
||||
}, {
|
||||
code: 'be',
|
||||
text: 'Belarusian',
|
||||
subtitle: 'Беларуская'
|
||||
}, {
|
||||
code: 'ca',
|
||||
text: 'Catalan',
|
||||
subtitle: 'Català'
|
||||
}, {
|
||||
code: 'nl',
|
||||
text: 'Dutch',
|
||||
subtitle: 'Nederlands'
|
||||
}, {
|
||||
code: 'fr',
|
||||
text: 'French',
|
||||
subtitle: 'Français'
|
||||
}, {
|
||||
code: 'de',
|
||||
text: 'German',
|
||||
subtitle: 'Deutsch'
|
||||
}, {
|
||||
code: 'it',
|
||||
text: 'Italian',
|
||||
subtitle: 'Italiano'
|
||||
}, {
|
||||
code: 'ms',
|
||||
text: 'Malay',
|
||||
subtitle: 'Bahasa Melayu'
|
||||
}, {
|
||||
code: 'pl',
|
||||
text: 'Polish',
|
||||
subtitle: 'Polski'
|
||||
}, {
|
||||
code: 'pt',
|
||||
text: 'Portuguese (Brazil)',
|
||||
subtitle: 'Português (Brasil)'
|
||||
}, {
|
||||
code: 'ru',
|
||||
text: 'Russian',
|
||||
subtitle: 'Русский'
|
||||
}, {
|
||||
code: 'es',
|
||||
text: 'Spanish',
|
||||
subtitle: 'Español'
|
||||
}, {
|
||||
code: 'tr',
|
||||
text: 'Turkish',
|
||||
subtitle: 'Türkçe'
|
||||
}, {
|
||||
code: 'uk',
|
||||
text: 'Ukrainian',
|
||||
subtitle: 'Українська'
|
||||
}];
|
||||
|
||||
const random = randomLong();
|
||||
r.forEach(({code, text, subtitle}) => {
|
||||
const row = new Row({
|
||||
radioField: new RadioField({
|
||||
text,
|
||||
name: random,
|
||||
value: code
|
||||
}),
|
||||
subtitle
|
||||
});
|
||||
|
||||
radioRows.set(code, row);
|
||||
});
|
||||
|
||||
const form = RadioFormFromRows([...radioRows.values()], (value) => {
|
||||
I18n.getLangPack(value);
|
||||
});
|
||||
|
||||
I18n.getCacheLangPack().then(langPack => {
|
||||
const row = radioRows.get(langPack.lang_code);
|
||||
if(!row) {
|
||||
console.error('no row', row, langPack);
|
||||
return;
|
||||
}
|
||||
|
||||
row.radioField.setValueSilently(true);
|
||||
});
|
||||
|
||||
section.content.append(form);
|
||||
|
||||
this.scrollable.append(section.container);
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ import AppEditProfileTab from "./editProfile";
|
||||
import AppChatFoldersTab from "./chatFolders";
|
||||
import AppNotificationsTab from "./notifications";
|
||||
import PeerTitle from "../../peerTitle";
|
||||
import AppLanguageTab from "./language";
|
||||
//import AppMediaViewer from "../../appMediaViewerNew";
|
||||
|
||||
export default class AppSettingsTab extends SliderSuperTab {
|
||||
@ -28,7 +29,7 @@ export default class AppSettingsTab extends SliderSuperTab {
|
||||
|
||||
init() {
|
||||
this.container.classList.add('settings-container');
|
||||
this.title.innerText = 'Settings';
|
||||
this.setTitle('Settings');
|
||||
|
||||
const btnMenu = ButtonMenuToggle({}, 'bottom-left', [{
|
||||
icon: 'logout',
|
||||
@ -130,6 +131,10 @@ export default class AppSettingsTab extends SliderSuperTab {
|
||||
this.buttons.privacy.addEventListener('click', () => {
|
||||
new AppPrivacyAndSecurityTab(this.slider).open();
|
||||
});
|
||||
|
||||
this.buttons.language.addEventListener('click', () => {
|
||||
new AppLanguageTab(this.slider).open();
|
||||
});
|
||||
}
|
||||
|
||||
public fillElements() {
|
||||
|
@ -3,6 +3,7 @@ import { horizontalMenu } from "./horizontalMenu";
|
||||
import { TransitionSlider } from "./transition";
|
||||
import appNavigationController, { NavigationItem } from "./appNavigationController";
|
||||
import SliderSuperTab, { SliderSuperTabConstructable, SliderTab } from "./sliderTab";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
|
||||
const TRANSITION_TIME = 250;
|
||||
|
||||
@ -24,7 +25,7 @@ export default class SidebarSlider {
|
||||
canHideFirst?: SidebarSlider['canHideFirst'],
|
||||
navigationType: SidebarSlider['navigationType']
|
||||
}) {
|
||||
Object.assign(this, options);
|
||||
safeAssign(this, options);
|
||||
|
||||
if(!this.tabs) {
|
||||
this.tabs = new Map();
|
||||
|
@ -1,6 +1,7 @@
|
||||
import ListenerSetter from "../helpers/listenerSetter";
|
||||
import { debounce } from "../helpers/schedulers";
|
||||
import appChatsManager from "../lib/appManagers/appChatsManager";
|
||||
import { LangPackKey } from "../lib/langPack";
|
||||
import apiManager from "../lib/mtproto/mtprotoworker";
|
||||
import RichTextProcessor from "../lib/richtextprocessor";
|
||||
import InputField, { InputFieldOptions, InputState } from "./inputField";
|
||||
@ -12,9 +13,9 @@ export class UsernameInputField extends InputField {
|
||||
peerId: number,
|
||||
listenerSetter: ListenerSetter,
|
||||
onChange?: () => void,
|
||||
invalidText: string,
|
||||
takenText: string,
|
||||
availableText: string,
|
||||
invalidText: LangPackKey,
|
||||
takenText: LangPackKey,
|
||||
availableText: LangPackKey,
|
||||
head?: string
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Scrollable from "../components/scrollable";
|
||||
import { safeAssign } from "./object";
|
||||
|
||||
export default class ScrollableLoader {
|
||||
public loading = false;
|
||||
@ -11,7 +12,7 @@ export default class ScrollableLoader {
|
||||
scrollable: ScrollableLoader['scrollable'],
|
||||
getPromise: ScrollableLoader['getPromise']
|
||||
}) {
|
||||
Object.assign(this, options);
|
||||
safeAssign(this, options);
|
||||
|
||||
options.scrollable.onScrolledBottom = () => {
|
||||
this.load();
|
||||
|
@ -120,3 +120,13 @@ export function validateInitObject(initObject: any, currentObject: any) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function safeAssign(object: any, fromObject: any) {
|
||||
if(!fromObject) return;
|
||||
|
||||
for(let i in fromObject) {
|
||||
if(fromObject[i] !== undefined) {
|
||||
object[i] = fromObject[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,16 +99,7 @@
|
||||
<div class="sidebar-header">
|
||||
<div class="sidebar-header__btn-container">
|
||||
<div class="animated-menu-icon"></div>
|
||||
<div class="btn-icon btn-menu-toggle rp sidebar-tools-button is-visible">
|
||||
<div class="btn-menu bottom-right">
|
||||
<div class="btn-menu-item menu-newGroup tgico-newgroup rp">New Group</div>
|
||||
<div class="btn-menu-item menu-contacts tgico-user rp">Contacts</div>
|
||||
<div class="btn-menu-item menu-archived tgico-archive rp">Archived <span class="archived-count badge badge-24 badge-gray"></span></div>
|
||||
<div class="btn-menu-item menu-saved tgico-savedmessages rp">Saved</div>
|
||||
<div class="btn-menu-item menu-settings tgico-settings rp">Settings</div>
|
||||
<div class="btn-menu-item menu-help tgico-help rp btn-disabled">Help</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-icon btn-menu-toggle rp sidebar-tools-button is-visible"></div>
|
||||
<div class="btn-icon rp sidebar-back-button"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Database from '../config/database';
|
||||
import { blobConstruct } from '../helpers/blob';
|
||||
import { safeAssign } from '../helpers/object';
|
||||
import { logger } from './logger';
|
||||
|
||||
/**
|
||||
@ -36,7 +37,7 @@ export default class IDBStorage {
|
||||
public storeName: string;
|
||||
|
||||
constructor(options: IDBOptions) {
|
||||
Object.assign(this, options);
|
||||
safeAssign(this, options);
|
||||
|
||||
this.openDatabase(true);
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { MOUNT_CLASS_TO } from "../config/debug";
|
||||
import { LangPackString } from "../layer";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
import { LangPackDifference, LangPackString } from "../layer";
|
||||
import apiManager from "./mtproto/mtprotoworker";
|
||||
import sessionStorage from "./sessionStorage";
|
||||
|
||||
export const langPack: {[actionType: string]: string} = {
|
||||
"messageActionChatCreate": "created the group",
|
||||
@ -41,9 +43,15 @@ namespace Strings {
|
||||
|
||||
export type AccountSettings = 'AccountSettings.Filters' | 'AccountSettings.Notifications' | 'AccountSettings.PrivacyAndSecurity' | 'AccountSettings.Language' | 'AccountSettings.Bio';
|
||||
|
||||
export type Telegram = 'Telegram.GeneralSettingsViewController' | 'Telegram.NotificationSettingsViewController';
|
||||
export type Telegram = 'Telegram.GeneralSettingsViewController' | 'Telegram.NotificationSettingsViewController' | 'Telegram.LanguageViewController';
|
||||
|
||||
export type ChatFilters = 'ChatList.Filter.Header' | 'ChatList.Filter.NewTitle' | 'ChatList.Filter.List.Header' | 'ChatList.Filter.Recommended.Header' | 'ChatList.Filter.Recommended.Add' | 'ChatList.Filter.List.Title';
|
||||
export type ChatList = ChatListFilter;
|
||||
export type ChatListAdd = 'ChatList.Add.TopSeparator' | 'ChatList.Add.BottomSeparator';
|
||||
export type ChatListFilterIncluded = 'ChatList.Filter.Include.Header' | 'ChatList.Filter.Include.AddChat';
|
||||
export type ChatListFilterExcluded = 'ChatList.Filter.Exclude.Header' | 'ChatList.Filter.Exclude.AddChat';
|
||||
export type ChatListFilterList = 'ChatList.Filter.List.Header' | 'ChatList.Filter.List.Title';
|
||||
export type ChatListFilterRecommended = 'ChatList.Filter.Recommended.Header' | 'ChatList.Filter.Recommended.Add';
|
||||
export type ChatListFilter = ChatListAdd | ChatListFilterIncluded | ChatListFilterExcluded | ChatListFilterList | ChatListFilterRecommended | 'ChatList.Filter.Header' | 'ChatList.Filter.NewTitle' | 'ChatList.Filter.NonContacts' | 'ChatList.Filter.Contacts' | 'ChatList.Filter.Groups' | 'ChatList.Filter.Channels' | 'ChatList.Filter.Bots';
|
||||
|
||||
export type AutoDownloadSettings = 'AutoDownloadSettings.TypePrivateChats' | 'AutoDownloadSettings.TypeChannels';
|
||||
|
||||
@ -51,37 +59,68 @@ namespace Strings {
|
||||
|
||||
export type Suggest = 'Suggest.Localization.Other';
|
||||
|
||||
export type LangPackKey = string | AccountSettings | EditAccount | Telegram | ChatFilters | LoginRegister | Bio | AutoDownloadSettings | DataAndStorage | Suggest;
|
||||
export type UsernameSettings = 'UsernameSettings.ChangeDescription';
|
||||
|
||||
export type LangPackKey = string | AccountSettings | EditAccount | Telegram | ChatList | LoginRegister | Bio | AutoDownloadSettings | DataAndStorage | Suggest | UsernameSettings;
|
||||
}
|
||||
|
||||
export type LangPackKey = Strings.LangPackKey;
|
||||
|
||||
namespace I18n {
|
||||
let strings: Partial<{[key in LangPackKey]: LangPackString}> = {};
|
||||
export const strings: Map<LangPackKey, LangPackString> = new Map();
|
||||
|
||||
let lastRequestedLangCode: string;
|
||||
export function getCacheLangPack(): Promise<LangPackDifference> {
|
||||
return sessionStorage.get('langPack').then((langPack: LangPackDifference) => {
|
||||
if(!langPack) {
|
||||
return getLangPack('en');
|
||||
}
|
||||
|
||||
if(!lastRequestedLangCode) {
|
||||
lastRequestedLangCode = langPack.lang_code;
|
||||
}
|
||||
|
||||
applyLangPack(langPack);
|
||||
return langPack;
|
||||
});
|
||||
}
|
||||
|
||||
export function getLangPack(langCode: string) {
|
||||
lastRequestedLangCode = langCode;
|
||||
return apiManager.invokeApi('langpack.getLangPack', {
|
||||
lang_code: langCode,
|
||||
lang_pack: 'macos'
|
||||
}).then(langPack => {
|
||||
strings = {};
|
||||
for(const string of langPack.strings) {
|
||||
strings[string.key as LangPackKey] = string;
|
||||
}
|
||||
|
||||
const elements = Array.from(document.querySelectorAll(`.i18n`)) as HTMLElement[];
|
||||
elements.forEach(element => {
|
||||
const instance = weakMap.get(element);
|
||||
|
||||
if(instance) {
|
||||
instance.update();
|
||||
}
|
||||
return sessionStorage.set({langPack}).then(() => {
|
||||
applyLangPack(langPack);
|
||||
return langPack;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function applyLangPack(langPack: LangPackDifference) {
|
||||
if(langPack.lang_code !== lastRequestedLangCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
strings.clear();
|
||||
|
||||
for(const string of langPack.strings) {
|
||||
strings.set(string.key as LangPackKey, string);
|
||||
}
|
||||
|
||||
const elements = Array.from(document.querySelectorAll(`.i18n`)) as HTMLElement[];
|
||||
elements.forEach(element => {
|
||||
const instance = weakMap.get(element);
|
||||
|
||||
if(instance) {
|
||||
instance.update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function getString(key: LangPackKey, args?: any[]) {
|
||||
const str = strings[key];
|
||||
const str = strings.get(key);
|
||||
let out = '';
|
||||
|
||||
if(str) {
|
||||
@ -103,7 +142,7 @@ namespace I18n {
|
||||
|
||||
export type IntlElementOptions = {
|
||||
element?: HTMLElement,
|
||||
property?: 'innerHTML' | 'placeholder'
|
||||
property?: 'innerText' | 'innerHTML' | 'placeholder'
|
||||
key: LangPackKey,
|
||||
args?: any[]
|
||||
};
|
||||
@ -111,7 +150,7 @@ namespace I18n {
|
||||
public element: IntlElementOptions['element'];
|
||||
public key: IntlElementOptions['key'];
|
||||
public args: IntlElementOptions['args'];
|
||||
public property: IntlElementOptions['property'] = 'innerHTML';
|
||||
public property: IntlElementOptions['property'] = 'innerText';
|
||||
|
||||
constructor(options: IntlElementOptions) {
|
||||
this.element = options.element || document.createElement('span');
|
||||
@ -122,9 +161,7 @@ namespace I18n {
|
||||
}
|
||||
|
||||
public update(options?: IntlElementOptions) {
|
||||
if(options) {
|
||||
Object.assign(this, options);
|
||||
}
|
||||
safeAssign(this, options);
|
||||
|
||||
(this.element as any)[this.property] = getString(this.key, this.args);
|
||||
}
|
||||
@ -137,6 +174,10 @@ namespace I18n {
|
||||
export function i18n_(options: IntlElementOptions) {
|
||||
return new IntlElement(options).element;
|
||||
}
|
||||
|
||||
export function _i18n(element: HTMLElement, key: LangPackKey, args?: any[], property?: IntlElementOptions['property']) {
|
||||
return new IntlElement({element, key, args, property}).element;
|
||||
}
|
||||
}
|
||||
|
||||
export {I18n};
|
||||
@ -148,4 +189,7 @@ export {i18n};
|
||||
const i18n_ = I18n.i18n_;
|
||||
export {i18n_};
|
||||
|
||||
const _i18n = I18n._i18n;
|
||||
export {_i18n};
|
||||
|
||||
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.I18n = I18n);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { MOUNT_CLASS_TO } from '../config/debug';
|
||||
import { LangPackDifference } from '../layer';
|
||||
import type { State } from './appManagers/appStateManager';
|
||||
import AppStorage from './storage';
|
||||
|
||||
@ -19,6 +20,7 @@ const sessionStorage = new AppStorage<{
|
||||
top: number
|
||||
}
|
||||
},
|
||||
langPack: LangPackDifference
|
||||
} & State>({
|
||||
storeName: 'session'
|
||||
});
|
||||
|
@ -569,7 +569,7 @@
|
||||
align-items: center;
|
||||
margin: 15px auto 1rem;
|
||||
border-radius: 30px;
|
||||
padding: 0 12px;
|
||||
padding: 0 24px 0 12px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@ -581,8 +581,9 @@
|
||||
.row {
|
||||
.btn-primary {
|
||||
height: 30px;
|
||||
padding: 0 12px;
|
||||
font-size: 15px;
|
||||
width: 52px;
|
||||
width: auto;
|
||||
transition: width 0.2s;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
|
Loading…
x
Reference in New Issue
Block a user