Merge branch 'feature/translations' of github.com:ONLYOFFICE/AppServer into feature/translations

This commit is contained in:
Artem Tarasov 2021-05-21 15:47:08 +03:00
commit 90a04bcfc7
12 changed files with 85 additions and 49 deletions

View File

@ -48,7 +48,9 @@ namespace Frontend.Translations.Tests
var translationFile = new TranslationFile(path); var translationFile = new TranslationFile(path);
translationFile.Translations = jsonTranslation.Properties().Select(p => new TranslationItem(p.Name, (string)p.Value)).ToList(); translationFile.Translations = jsonTranslation.Properties()
.Select(p => new TranslationItem(p.Name, (string)p.Value))
.ToList();
TranslationFiles.Add(translationFile); TranslationFiles.Add(translationFile);
} }
@ -56,19 +58,19 @@ namespace Frontend.Translations.Tests
var javascriptFiles = (from wsPath in Workspaces var javascriptFiles = (from wsPath in Workspaces
let clientDir = Path.Combine(BasePath, wsPath) let clientDir = Path.Combine(BasePath, wsPath)
from file in Directory.EnumerateFiles(clientDir, "*.js", SearchOption.AllDirectories) from file in Directory.EnumerateFiles(clientDir, "*.js", SearchOption.AllDirectories)
where file.Contains("src\\") where !file.Contains("dist\\")
select file) select file)
.ToList(); .ToList();
javascriptFiles.AddRange(from wsPath in Workspaces javascriptFiles.AddRange(from wsPath in Workspaces
let clientDir = Path.Combine(BasePath, wsPath) let clientDir = Path.Combine(BasePath, wsPath)
from file in Directory.EnumerateFiles(clientDir, "*.jsx", SearchOption.AllDirectories) from file in Directory.EnumerateFiles(clientDir, "*.jsx", SearchOption.AllDirectories)
where file.Contains("src\\") where !file.Contains("dist\\")
select file); select file);
JavaScriptFiles = new List<JavaScriptFile>(); JavaScriptFiles = new List<JavaScriptFile>();
var pattern1 = "[.{\\s\\(]t\\(\\s*[\"\'`]([a-zA-Z0-9_.:_\\$\\s{}/_-]+)[\"\'`]\\s*[\\),]"; var pattern1 = "[.{\\s\\(]t\\(\\s*[\"\'`]([a-zA-Z0-9_.:_\\s{}/_-]+)[\"\'`]\\s*[\\),]";
var pattern2 = "i18nKey=\"([a-zA-Z0-9_.-]+)\""; var pattern2 = "i18nKey=\"([a-zA-Z0-9_.-]+)\"";
var regexp = new Regex($"({pattern1})|({pattern2})", RegexOptions.Multiline | RegexOptions.ECMAScript); var regexp = new Regex($"({pattern1})|({pattern2})", RegexOptions.Multiline | RegexOptions.ECMAScript);
@ -79,7 +81,11 @@ namespace Frontend.Translations.Tests
var matches = regexp.Matches(jsFileText); var matches = regexp.Matches(jsFileText);
var translationKeys = matches.Select(m => m.Groups[2].Value == "" ? m.Groups[4].Value : m.Groups[2].Value).ToList(); var translationKeys = matches
.Select(m => m.Groups[2].Value == ""
? m.Groups[4].Value
: m.Groups[2].Value)
.ToList();
if (!translationKeys.Any()) if (!translationKeys.Any())
continue; continue;
@ -117,11 +123,18 @@ namespace Frontend.Translations.Tests
.ToList(); .ToList();
var enGroup = groupedByLng.Find(f => f.Lng == "en"); var enGroup = groupedByLng.Find(f => f.Lng == "en");
var expectedCount = enGroup.Count;
foreach (var lng in groupedByLng.Where(g => g.Lng != "en")) var otherLngs = groupedByLng.Where(g => g.Lng != "en");
{
Assert.AreEqual(enGroup.Count, lng.Count, "language '{0}' is not equals 'en' by translated files count", lng.Lng); var incompleteList = otherLngs
} .Where(lng => lng.Count != expectedCount)
.Select(lng => $"{lng.Lng} (Count={lng.Count})")
.ToList();
Assert.AreEqual(0, incompleteList.Count,
"languages '{0}' are not equal 'en' (Count= {1}) by translated files count",
string.Join(", ", incompleteList), expectedCount);
//Assert.AreEqual(true, groupedByLng.All(g => g.Count == enGroup.Count)); //Assert.AreEqual(true, groupedByLng.All(g => g.Count == enGroup.Count));
} }
@ -137,17 +150,24 @@ namespace Frontend.Translations.Tests
Keys = grp Keys = grp
.SelectMany(k => k.Translations) .SelectMany(k => k.Translations)
.OrderByDescending(itm => itm.Key) .OrderByDescending(itm => itm.Key)
.ToList()
}) })
.ToList(); .ToList();
var enGroup = groupedByLng.Find(f => f.Lng == "en"); var enGroup = groupedByLng.Find(f => f.Lng == "en");
var expectedCount = enGroup.Keys.Count(); var expectedCount = enGroup.Keys.Count;
foreach (var lng in groupedByLng.Where(g => g.Lng != "en")) var otherLngs = groupedByLng.Where(g => g.Lng != "en");
{
Assert.AreEqual(expectedCount, lng.Keys.Count(), "language '{0}' is not equals 'en' by translated keys count", lng.Lng); var incompleteList = otherLngs
} .Where(lng => lng.Keys.Count != expectedCount)
.Select(lng => $"{lng.Lng} (Count={lng.Keys.Count})")
.ToList();
Assert.AreEqual(0, incompleteList.Count,
"languages '{0}' are not equal 'en' (Count= {1}) by translated keys count",
string.Join(", ", incompleteList), expectedCount);
//Assert.AreEqual(true, groupedByLng.All(g => g.Keys.Count() == enGroup.Keys.Count())); //Assert.AreEqual(true, groupedByLng.All(g => g.Keys.Count() == enGroup.Keys.Count()));
} }
@ -167,7 +187,9 @@ namespace Frontend.Translations.Tests
var notFoundJsKeys = allJsTranslationKeys.Except(allEnKeys); var notFoundJsKeys = allJsTranslationKeys.Except(allEnKeys);
Assert.AreEqual(0, notFoundJsKeys.Count(), "Some i18n-keys are not exist in translations in 'en' language: Keys: '{0}'", string.Join(", ", notFoundJsKeys)); Assert.AreEqual(0, notFoundJsKeys.Count(),
"Some i18n-keys are not exist in translations in 'en' language: Keys: '{0}'",
string.Join(", ", notFoundJsKeys));
} }
[Test] [Test]
@ -187,7 +209,9 @@ namespace Frontend.Translations.Tests
var notFoundi18nKeys = allEnKeys.Except(allJsTranslationKeys); var notFoundi18nKeys = allEnKeys.Except(allJsTranslationKeys);
Assert.AreEqual(0, notFoundi18nKeys.Count(), "Some i18n-keys are not found in js: Keys: '{0}'", string.Join(", ", notFoundi18nKeys)); Assert.AreEqual(0, notFoundi18nKeys.Count(),
"Some i18n-keys are not found in js: Keys: '{0}'",
string.Join(", ", notFoundi18nKeys));
} }
} }
} }

View File

@ -110,20 +110,20 @@ export const ConflictResolveType = Object.freeze({
}); });
export const providersData = Object.freeze({ export const providersData = Object.freeze({
Google: { Google: {
label: "SignInWithGoogle", label: "Google",
icon: "/static/images/share.google.react.svg", icon: "/static/images/share.google.react.svg",
}, },
Facebook: { Facebook: {
label: "SignInWithFacebook", label: "Facebook",
icon: "/static/images/share.facebook.react.svg", icon: "/static/images/share.facebook.react.svg",
}, },
Twitter: { Twitter: {
label: "SignInWithTwitter", label: "Twitter",
icon: "/static/images/share.twitter.react.svg", icon: "/static/images/share.twitter.react.svg",
iconOptions: { color: "#2AA3EF" }, iconOptions: { color: "#2AA3EF" },
}, },
LinkedIn: { LinkedIn: {
label: "SignInWithLinkedIn", label: "LinkedIn",
icon: "/static/images/share.linkedin.react.svg", icon: "/static/images/share.linkedin.react.svg",
}, },
}); });

View File

@ -224,3 +224,16 @@ export function toCommunityHostname(hostname) {
return communityHostname; return communityHostname;
} }
export function getProviderTranslation(provider, t) {
switch (provider) {
case "Google":
return t("SignInWithGoogle");
case "Facebook":
return t("SignInWithFacebook");
case "Twitter":
return t("SignInWithTwitter");
case "LinkedIn":
return t("SignInWithLinkedIn");
}
}

View File

@ -9,7 +9,11 @@ import Link from "@appserver/components/link";
import ProfileInfo from "./ProfileInfo/ProfileInfo"; import ProfileInfo from "./ProfileInfo/ProfileInfo";
import toastr from "studio/toastr"; import toastr from "studio/toastr";
import React from "react"; import React from "react";
import { combineUrl, isMe } from "@appserver/common/utils"; import {
combineUrl,
isMe,
getProviderTranslation,
} from "@appserver/common/utils";
import styled from "styled-components"; import styled from "styled-components";
import { withRouter } from "react-router"; import { withRouter } from "react-router";
@ -208,7 +212,7 @@ class SectionBodyContent extends React.PureComponent {
<FacebookButton <FacebookButton
noHover={true} noHover={true}
iconName={icon} iconName={icon}
label={t(label)} label={getProviderTranslation(label, t)}
className="socialButton" className="socialButton"
$iconOptions={iconOptions} $iconOptions={iconOptions}
/> />
@ -216,7 +220,7 @@ class SectionBodyContent extends React.PureComponent {
<SocialButton <SocialButton
noHover={true} noHover={true}
iconName={icon} iconName={icon}
label={t(label)} label={getProviderTranslation(label, t)}
className="socialButton" className="socialButton"
$iconOptions={iconOptions} $iconOptions={iconOptions}
/> />

View File

@ -69,10 +69,6 @@
"ProductsAndInstruments": "Module und Tools", "ProductsAndInstruments": "Module und Tools",
"ProductUserOpportunities": "Profile und Gruppen anschauen", "ProductUserOpportunities": "Profile und Gruppen anschauen",
"ProjectsProduct": "Projekte", "ProjectsProduct": "Projekte",
"ProjectsUserCapabilityCreate": "Meilensteine, Aufgaben, Diskussionen, Dokumente erstellen und bearbeiten",
"ProjectsUserCapabilityForm": "Projektteam bilden und Zugriffsrechte bestimmen",
"ProjectsUserCapabilityTrack": "Zeit für Aufgaben verfolgen, Berichte generieren",
"ProjectsUserCapabilityView": "Projekte anschauen und an Diskussionen teilnehmen",
"RestoreDefaultButton": "Standardmäßige Einstellungen", "RestoreDefaultButton": "Standardmäßige Einstellungen",
"SetDefaultTitle": "Standardtitel setzen", "SetDefaultTitle": "Standardtitel setzen",
"SetPeopleAdmin": "Administrator des Moduls Personen auswählen", "SetPeopleAdmin": "Administrator des Moduls Personen auswählen",

View File

@ -78,10 +78,6 @@
"ProductsAndInstruments": "Modules \u0026 tools", "ProductsAndInstruments": "Modules \u0026 tools",
"ProductUserOpportunities": "View profiles and groups", "ProductUserOpportunities": "View profiles and groups",
"ProjectsProduct": "Projects", "ProjectsProduct": "Projects",
"ProjectsUserCapabilityCreate": "Create and edit milestones, tasks, discussions, documents",
"ProjectsUserCapabilityForm": "Form a project team and set access rights",
"ProjectsUserCapabilityTrack": "Track time for tasks, generate reports",
"ProjectsUserCapabilityView": "View projects and take part in discussions",
"RegistrationDate": "Registration date", "RegistrationDate": "Registration date",
"RestoreDefaultButton": "Restore to Default", "RestoreDefaultButton": "Restore to Default",

View File

@ -78,10 +78,6 @@
"ProductsAndInstruments": "Модули и инструменты", "ProductsAndInstruments": "Модули и инструменты",
"ProductUserOpportunities": "Просматривать профили и группы", "ProductUserOpportunities": "Просматривать профили и группы",
"ProjectsProduct": "Проекты", "ProjectsProduct": "Проекты",
"ProjectsUserCapabilityCreate": "Создавать и редактировать вехи, задачи, обсуждения, документы",
"ProjectsUserCapabilityForm": "Формировать команду проекта и устанавливать права доступа",
"ProjectsUserCapabilityTrack": "Учитывать время выполнения задач, генерировать отчеты",
"ProjectsUserCapabilityView": "Просматривать проекты и участвовать в обсуждениях",
"RegistrationDate": "Дата регистрации", "RegistrationDate": "Дата регистрации",
"RestoreDefaultButton": "Настройки по умолчанию", "RestoreDefaultButton": "Настройки по умолчанию",
"SetDefaultTitle": "Установить заголовок по умолчанию", "SetDefaultTitle": "Установить заголовок по умолчанию",

View File

@ -186,7 +186,7 @@ class Form extends React.PureComponent {
size="big" size="big"
tabIndex={2} tabIndex={2}
label={ label={
isLoading ? t("Common:LoadingProcessing") : t("Common:OkButton") isLoading ? t("Common:LoadingProcessing") : t("Common:OKButton")
} }
isDisabled={isLoading} isDisabled={isLoading}
isLoading={isLoading} isLoading={isLoading}

View File

@ -18,7 +18,11 @@ import FacebookButton from "@appserver/components/facebook-button";
import EmailInput from "@appserver/components/email-input"; import EmailInput from "@appserver/components/email-input";
import { getAuthProviders } from "@appserver/common/api/settings"; import { getAuthProviders } from "@appserver/common/api/settings";
import PageLayout from "@appserver/common/components/PageLayout"; import PageLayout from "@appserver/common/components/PageLayout";
import { combineUrl, createPasswordHash } from "@appserver/common/utils"; import {
combineUrl,
createPasswordHash,
getProviderTranslation,
} from "@appserver/common/utils";
import { AppServerConfig, providersData } from "@appserver/common/constants"; import { AppServerConfig, providersData } from "@appserver/common/constants";
import { isMobile } from "react-device-detect"; import { isMobile } from "react-device-detect";
import { desktop } from "@appserver/components/utils/device"; import { desktop } from "@appserver/components/utils/device";
@ -182,7 +186,7 @@ class Confirm extends React.PureComponent {
> >
<FacebookButton <FacebookButton
iconName={icon} iconName={icon}
label={t(label)} label={getProviderTranslation(label, t)}
className="socialButton" className="socialButton"
$iconOptions={iconOptions} $iconOptions={iconOptions}
data-url={faceBookData.url} data-url={faceBookData.url}
@ -213,7 +217,7 @@ class Confirm extends React.PureComponent {
<div className="buttonWrapper" key={`${item.provider}ProviderItem`}> <div className="buttonWrapper" key={`${item.provider}ProviderItem`}>
<SocialButton <SocialButton
iconName={icon} iconName={icon}
label={t(label)} label={getProviderTranslation(label, t)}
className={`socialButton ${className ? className : ""}`} className={`socialButton ${className ? className : ""}`}
$iconOptions={iconOptions} $iconOptions={iconOptions}
data-url={item.url} data-url={item.url}

View File

@ -46,10 +46,10 @@ class Body extends React.PureComponent {
setPaymentsLicense(null, fd) setPaymentsLicense(null, fd)
.then(() => { .then(() => {
toastr.success(t("LoadingLicenseSuccess"), ""); toastr.success(t("SuccessLoadingLicense"), "");
}) })
.catch((error) => { .catch((error) => {
toastr.error(t("LoadingLicenseError"), "LicenseIsNotValid", 0, true); toastr.error(t("ErrorLoadingLicense"), "LicenseIsNotValid", 0, true);
console.log(error); console.log(error);
}); });
}; };

View File

@ -45,10 +45,9 @@ const Separator = styled.div`
class ThirdPartyServices extends React.Component { class ThirdPartyServices extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
const { t } = props; const { t, setDocumentTitle } = props;
document.title = `${t("ThirdPartyAuthorization")} ${t(
"OrganizationName" setDocumentTitle(`${t("ThirdPartyAuthorization")}`);
)}`;
this.state = { this.state = {
dialogVisible: false, dialogVisible: false,
@ -203,7 +202,7 @@ ThirdPartyServices.propTypes = {
}; };
export default inject(({ setup, auth }) => { export default inject(({ setup, auth }) => {
const { settingsStore } = auth; const { settingsStore, setDocumentTitle } = auth;
const { urlAuthKeys } = settingsStore; const { urlAuthKeys } = settingsStore;
const { const {
getConsumers, getConsumers,
@ -219,5 +218,6 @@ export default inject(({ setup, auth }) => {
getConsumers, getConsumers,
updateConsumerProps, updateConsumerProps,
setSelectedConsumer, setSelectedConsumer,
setDocumentTitle,
}; };
})(withTranslation(["Settings", "Common"])(observer(ThirdPartyServices))); })(withTranslation(["Settings", "Common"])(observer(ThirdPartyServices)));

View File

@ -19,7 +19,10 @@ import ForgotPasswordModalDialog from "./sub-components/forgot-password-modal-di
import Register from "./sub-components/register-container"; import Register from "./sub-components/register-container";
import { getAuthProviders } from "@appserver/common/api/settings"; import { getAuthProviders } from "@appserver/common/api/settings";
import { checkPwd } from "@appserver/common/desktop"; import { checkPwd } from "@appserver/common/desktop";
import { createPasswordHash } from "@appserver/common/utils"; import {
createPasswordHash,
getProviderTranslation,
} from "@appserver/common/utils";
import { providersData } from "@appserver/common/constants"; import { providersData } from "@appserver/common/constants";
import { inject, observer } from "mobx-react"; import { inject, observer } from "mobx-react";
import i18n from "./i18n"; import i18n from "./i18n";
@ -350,7 +353,7 @@ const Form = (props) => {
> >
<FacebookButton <FacebookButton
iconName={icon} iconName={icon}
label={t(label)} label={getProviderTranslation(label, t)}
className="socialButton" className="socialButton"
$iconOptions={iconOptions} $iconOptions={iconOptions}
data-url={faceBookData.url} data-url={faceBookData.url}
@ -380,7 +383,7 @@ const Form = (props) => {
<div className="buttonWrapper" key={`${item.provider}ProviderItem`}> <div className="buttonWrapper" key={`${item.provider}ProviderItem`}>
<SocialButton <SocialButton
iconName={icon} iconName={icon}
label={t(label)} label={getProviderTranslation(label, t)}
className={`socialButton ${className ? className : ""}`} className={`socialButton ${className ? className : ""}`}
$iconOptions={iconOptions} $iconOptions={iconOptions}
data-url={item.url} data-url={item.url}