Temp language commit

This commit is contained in:
Eduard Kuzmenko 2021-03-21 13:59:59 +04:00
parent 878e4a693d
commit 785dca7873
9 changed files with 140 additions and 39 deletions

View File

@ -1,6 +1,7 @@
import { i18n, LangPackKey } from "../lib/langPack";
import { ripple } from "./ripple";
const Button = (className: string, options: Partial<{noRipple: true, onlyMobile: true, icon: string, rippleSquare: true, text: string, disabled: boolean}> = {}) => {
const Button = (className: string, options: Partial<{noRipple: true, onlyMobile: true, icon: string, rippleSquare: true, text: LangPackKey, disabled: boolean}> = {}) => {
const button = document.createElement('button');
button.className = className + (options.icon ? ' tgico-' + options.icon : '');
@ -21,7 +22,7 @@ const Button = (className: string, options: Partial<{noRipple: true, onlyMobile:
}
if(options.text) {
button.append(options.text);
button.append(i18n(options.text));
}
return button;

View File

@ -1,11 +1,12 @@
import { attachClickEvent, AttachClickOptions, cancelEvent, CLICK_EVENT_NAME } from "../helpers/dom";
import ListenerSetter from "../helpers/listenerSetter";
import { i18n, LangPackKey } from "../lib/langPack";
import { closeBtnMenu } from "./misc";
import { ripple } from "./ripple";
export type ButtonMenuItemOptions = {
icon: string,
text: string,
text: LangPackKey,
onClick: (e: MouseEvent | TouchEvent) => void,
element?: HTMLElement,
options?: AttachClickOptions
@ -18,7 +19,7 @@ const ButtonMenuItem = (options: ButtonMenuItemOptions) => {
const {icon, text, onClick} = options;
const el = document.createElement('div');
el.className = 'btn-menu-item tgico-' + icon;
el.innerText = text;
el.append(i18n(text));
ripple(el);

View File

@ -1,6 +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 RichTextProcessor from "../lib/richtextprocessor";
let init = () => {
@ -59,7 +60,7 @@ export enum InputState {
export type InputFieldOptions = {
placeholder?: string,
label?: string,
label?: LangPackKey,
name?: string,
maxLength?: number,
showLengthOn?: number,
@ -97,7 +98,6 @@ class InputField {
this.container.innerHTML = `
<div ${placeholder ? `data-placeholder="${placeholder}"` : ''} contenteditable="true" class="input-field-input"></div>
${label ? `<label>${label}</label>` : ''}
`;
input = this.container.firstElementChild as HTMLElement;
@ -135,7 +135,6 @@ class InputField {
} else {
this.container.innerHTML = `
<input type="text" ${name ? `name="${name}"` : ''} ${placeholder ? `placeholder="${placeholder}"` : ''} autocomplete="off" ${label ? 'required=""' : ''} class="input-field-input">
${label ? `<label>${label}</label>` : ''}
`;
input = this.container.firstElementChild as HTMLElement;
@ -143,7 +142,9 @@ class InputField {
}
if(label) {
this.label = this.container.lastElementChild as HTMLLabelElement;
this.label = document.createElement('label');
i18n_({element: this.label, key: label});
this.container.append(this.label);
}
let processInput: () => void;

View File

@ -23,6 +23,7 @@ import AppNewChannelTab from "./tabs/newChannel";
import AppContactsTab from "./tabs/contacts";
import AppArchivedTab from "./tabs/archivedTab";
import AppAddMembersTab from "./tabs/addMembers";
import { i18n_, LangPackKey } from "../../lib/langPack";
export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown';
@ -413,7 +414,7 @@ export class SettingSection {
public caption: HTMLElement;
constructor(options: {
name?: string,
name?: LangPackKey,
caption?: string,
noDelimiter?: boolean
}) {
@ -432,7 +433,7 @@ export class SettingSection {
if(options.name) {
this.title = document.createElement('div');
this.title.classList.add('sidebar-left-h2', 'sidebar-left-section-name');
this.title.innerHTML = options.name;
i18n_({element: this.title, key: options.name});
this.content.append(this.title);
}
@ -451,7 +452,7 @@ export class SettingSection {
}
}
export const generateSection = (appendTo: Scrollable, name?: string, caption?: string) => {
export const generateSection = (appendTo: Scrollable, name?: LangPackKey, caption?: string) => {
const section = new SettingSection({name, caption});
appendTo.append(section.container);
return section.content;

View File

@ -16,6 +16,7 @@ import rootScope from "../../../lib/rootScope";
import AppEditFolderTab from "./editFolder";
import Row from "../../row";
import { SettingSection } from "..";
import { i18n_ } from "../../../lib/langPack";
export default class AppChatFoldersTab extends SliderSuperTab {
private createFolderBtn: HTMLElement;
@ -101,7 +102,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
protected init() {
this.container.classList.add('chat-folders-container');
this.title.innerText = 'Chat Folders';
this.setTitle('ChatList.Filter.List.Title');
this.scrollable.container.classList.add('chat-folders');
@ -110,20 +111,20 @@ export default class AppChatFoldersTab extends SliderSuperTab {
const caption = document.createElement('div');
caption.classList.add('caption');
caption.innerHTML = `Create folders for different groups of chats<br>and quickly switch between them.`;
i18n_({element: caption, key: 'ChatList.Filter.Header'});
this.createFolderBtn = Button('btn-primary btn-color-primary btn-create-folder', {
text: 'Create Folder',
text: 'ChatList.Filter.NewTitle',
icon: 'add'
});
this.foldersSection = new SettingSection({
name: 'Folders'
name: 'ChatList.Filter.List.Header'
});
this.foldersSection.container.style.display = 'none';
this.suggestedSection = new SettingSection({
name: 'Recommended folders'
name: 'ChatList.Filter.Recommended.Header'
});
this.suggestedSection.container.style.display = 'none';
@ -207,9 +208,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
suggestedFilters.forEach(filter => {
const div = this.renderFolder(filter);
const button = document.createElement('button');
button.classList.add('btn-primary', 'btn-color-primary');
button.innerText = 'Add';
const button = Button('btn-primary btn-color-primary', {text: 'ChatList.Filter.Recommended.Add'});
div.append(button);
this.suggestedSection.content.append(div);

View File

@ -5,6 +5,7 @@ import { SliderSuperTab } from "../../slider";
import { attachClickEvent } from "../../../helpers/dom";
import EditPeer from "../../editPeer";
import { UsernameInputField } from "../../usernameInputField";
import { i18n_ } from "../../../lib/langPack";
// TODO: аватарка не поменяется в этой вкладке после изменения почему-то (если поставить в другом клиенте, и потом тут проверить, для этого ещё вышел в чатлист)
@ -21,7 +22,7 @@ export default class AppEditProfileTab extends SliderSuperTab {
protected async init() {
this.container.classList.add('edit-profile-container');
this.title.innerText = 'Edit Profile';
this.setTitle('EditAccount.Title');
const inputFields: InputField[] = [];
@ -30,17 +31,17 @@ export default class AppEditProfileTab extends SliderSuperTab {
inputWrapper.classList.add('input-wrapper');
this.firstNameInputField = new InputField({
label: 'Name',
label: 'Login.Register.FirstName.Placeholder',
name: 'first-name',
maxLength: 70
});
this.lastNameInputField = new InputField({
label: 'Last Name',
label: 'Login.Register.LastName.Placeholder',
name: 'last-name',
maxLength: 64
});
this.bioInputField = new InputField({
label: 'Bio (optional)',
label: 'AccountSettings.Bio',
name: 'bio',
maxLength: 70
});
@ -49,7 +50,7 @@ export default class AppEditProfileTab extends SliderSuperTab {
const caption = document.createElement('div');
caption.classList.add('caption');
caption.innerHTML = 'Any details such as age, occupation or city. Example:<br>23 y.o. designer from San Francisco.';
i18n_({element: caption, key: 'Bio.Description'});
inputFields.push(this.firstNameInputField, this.lastNameInputField, this.bioInputField);
this.scrollable.append(inputWrapper, caption);
@ -68,14 +69,14 @@ export default class AppEditProfileTab extends SliderSuperTab {
{
const h2 = document.createElement('div');
h2.classList.add('sidebar-left-h2');
h2.innerText = 'Username';
i18n_({element: h2, key: 'EditAccount.Username'});
const inputWrapper = document.createElement('div');
inputWrapper.classList.add('input-wrapper');
this.usernameInputField = new UsernameInputField({
peerId: 0,
label: 'Username (optional)',
label: 'EditAccount.Username',
name: 'username',
plainText: true,
listenerSetter: this.listenerSetter,

View File

@ -32,7 +32,7 @@ export default class AppSettingsTab extends SliderSuperTab {
const btnMenu = ButtonMenuToggle({}, 'bottom-left', [{
icon: 'logout',
text: 'Log Out',
text: 'EditAccount.Logout',
onClick: () => {
apiManager.logOut();
}
@ -96,12 +96,12 @@ export default class AppSettingsTab extends SliderSuperTab {
buttonsDiv.classList.add('profile-buttons');
const className = 'profile-button btn-primary btn-transparent';
buttonsDiv.append(this.buttons.edit = Button(className, {icon: 'edit', text: 'Edit Profile'}));
buttonsDiv.append(this.buttons.folders = Button(className, {icon: 'folder', text: 'Chat Folders'}));
buttonsDiv.append(this.buttons.general = Button(className, {icon: 'settings', text: 'General Settings'}));
buttonsDiv.append(this.buttons.notifications = Button(className, {icon: 'unmute', text: 'Notifications'}));
buttonsDiv.append(this.buttons.privacy = Button(className, {icon: 'lock', text: 'Privacy and Security'}));
buttonsDiv.append(this.buttons.language = Button(className, {icon: 'language', text: 'Language', disabled: true}));
buttonsDiv.append(this.buttons.edit = Button(className, {icon: 'edit', text: 'EditAccount.Title'}));
buttonsDiv.append(this.buttons.folders = Button(className, {icon: 'folder', text: 'AccountSettings.Filters'}));
buttonsDiv.append(this.buttons.general = Button(className, {icon: 'settings', text: 'Telegram.GeneralSettingsViewController'}));
buttonsDiv.append(this.buttons.notifications = Button(className, {icon: 'unmute', text: 'AccountSettings.Notifications'}));
buttonsDiv.append(this.buttons.privacy = Button(className, {icon: 'lock', text: 'AccountSettings.PrivacyAndSecurity'}));
buttonsDiv.append(this.buttons.language = Button(className, {icon: 'language', text: 'AccountSettings.Language'}));
this.scrollable.append(this.avatarElem, this.nameDiv, this.phoneDiv, buttonsDiv);
this.scrollable.container.classList.add('profile-content-wrapper');

View File

@ -1,5 +1,6 @@
import EventListenerBase from "../helpers/eventListenerBase";
import ListenerSetter from "../helpers/listenerSetter";
import { i18n, LangPackKey } from "../lib/langPack";
import ButtonIcon from "./buttonIcon";
import Scrollable from "./scrollable";
import SidebarSlider from "./slider";
@ -92,6 +93,11 @@ export default class SliderSuperTab implements SliderTab {
this.listenerSetter.removeAll();
}
}
protected setTitle(key: LangPackKey) {
this.title.innerHTML = '';
this.title.append(i18n(key));
}
}
export class SliderSuperTabEventable extends SliderSuperTab {

View File

@ -1,3 +1,4 @@
import { MOUNT_CLASS_TO } from "../config/debug";
import { LangPackString } from "../layer";
import apiManager from "./mtproto/mtprotoworker";
@ -31,8 +32,26 @@ export const langPack: {[actionType: string]: string} = {
"messageActionBotAllowed": "You allowed this bot to message you when logged in {}"
};
export namespace Internationalization {
let strings: {[key: string]: LangPackString} = {};
namespace Strings {
export type Bio = 'Bio.Description';
export type LoginRegister = 'Login.Register.FirstName.Placeholder' | 'Login.Register.LastName.Placeholder';
export type EditAccount = 'EditAccount.Logout' | 'EditAccount.Title' | 'EditAccount.Title' | 'EditAccount.Username';
export type AccountSettings = 'AccountSettings.Filters' | 'AccountSettings.Notifications' | 'AccountSettings.PrivacyAndSecurity' | 'AccountSettings.Language' | 'AccountSettings.Bio';
export type Telegram = 'Telegram.GeneralSettingsViewController';
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 LangPackKey = AccountSettings | EditAccount | Telegram | ChatFilters | LoginRegister | Bio | string;
}
export type LangPackKey = Strings.LangPackKey;
namespace I18n {
let strings: Partial<{[key in LangPackKey]: LangPackString}> = {};
export function getLangPack(langCode: string) {
return apiManager.invokeApi('langpack.getLangPack', {
@ -41,14 +60,86 @@ export namespace Internationalization {
}).then(langPack => {
strings = {};
for(const string of langPack.strings) {
strings[string.key] = string;
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();
}
});
});
}
export function _(key: keyof typeof strings, ...args: any[]) {
let str = strings[key];
export function getString(key: LangPackKey, args?: any[]) {
const str = strings[key];
let out = '';
if(str) {
if(str._ === 'langPackStringPluralized') {
out = str.one_value;
} else if(str._ === 'langPackString') {
out = str.value;
} else {
out = '[' + key + ']';
}
} else {
out = '[' + key + ']';
}
return out;
}
const weakMap: WeakMap<HTMLElement, IntlElement> = new WeakMap();
export type IntlElementOptions = {
element?: HTMLElement,
property?: 'innerHTML' | 'placeholder'
key: LangPackKey,
args?: any[]
};
export class IntlElement {
public element: IntlElementOptions['element'];
public key: IntlElementOptions['key'];
public args: IntlElementOptions['args'];
public property: IntlElementOptions['property'] = 'innerHTML';
return str;
constructor(options: IntlElementOptions) {
this.element = options.element || document.createElement('span');
this.element.classList.add('i18n');
this.update(options);
weakMap.set(this.element, this);
}
public update(options?: IntlElementOptions) {
if(options) {
Object.assign(this, options);
}
(this.element as any)[this.property] = getString(this.key, this.args);
}
}
export function i18n(key: LangPackKey, args?: any[]) {
return new IntlElement({key, args}).element;
}
export function i18n_(options: IntlElementOptions) {
return new IntlElement(options).element;
}
}
export {I18n};
export default I18n;
const i18n = I18n.i18n;
export {i18n};
const i18n_ = I18n.i18n_;
export {i18n_};
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.I18n = I18n);