Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Arabic and rtl lang support #2084

Merged
merged 17 commits into from
May 1, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions Backend.Tests/Models/ProjectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ public class WritingSystemTests
private const string Name = "System 1";
private const string Bcp47 = "lang-1";
private const string Font = "calibri";
private const string Dir = "rtl";

[Test]
public void TestEquals()
Expand All @@ -185,20 +186,31 @@ public void TestNotEquals()
{
var system = new WritingSystem { Name = Name, Bcp47 = Bcp47 };
Assert.IsFalse(system.Equals(new WritingSystem { Name = Name }));
system = new WritingSystem { Name = Name, Dir = Dir };
Assert.IsFalse(system.Equals(new WritingSystem { Dir = Dir }));
}

[Test]
public void TestToString()
{
var system = new WritingSystem { Name = Name, Bcp47 = Bcp47 };
var system = new WritingSystem { Name = Name, Bcp47 = Bcp47, Font = Font, Dir = Dir };
var sysString = system.ToString();
Assert.IsTrue(sysString.Contains(Name) && sysString.Contains(Bcp47) &&
sysString.Contains(Font) && sysString.Contains(Dir));
}

[Test]
public void TestToStringWithoutDir()
{
var system = new WritingSystem { };
var sysString = system.ToString();
Assert.IsTrue(sysString.Contains(Name) && sysString.Contains(Bcp47));
Assert.IsTrue(sysString.Contains("name") && !sysString.Contains("dir"));
}

[Test]
public void TestClone()
{
var system = new WritingSystem { Name = Name, Bcp47 = Bcp47, Font = Font };
var system = new WritingSystem { Name = Name, Bcp47 = Bcp47, Font = Font, Dir = Dir };
var clonedSystem = system.Clone();
Assert.AreEqual(system, clonedSystem);
}
Expand Down
16 changes: 12 additions & 4 deletions Backend/Models/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ public class WritingSystem
[Required]
public string Font { get; set; }

public string? Dir { get; set; }

public WritingSystem()
{
Name = "";
Expand All @@ -295,7 +297,8 @@ public WritingSystem Clone()
{
Name = (string)Name.Clone(),
Bcp47 = (string)Bcp47.Clone(),
Font = (string)Font.Clone()
Font = (string)Font.Clone(),
Dir = (string?)Dir?.Clone()
};
}

Expand All @@ -306,17 +309,22 @@ public override bool Equals(object? obj)
return false;
}

return Name == ws.Name && Bcp47 == ws.Bcp47 && Font == ws.Font;
return Name == ws.Name && Bcp47 == ws.Bcp47 && Font == ws.Font && Dir == ws.Dir;
}

public override int GetHashCode()
{
return HashCode.Combine(Name, Bcp47, Font);
return HashCode.Combine(Name, Bcp47, Font, Dir);
}

public override string ToString()
{
return $"<name: {Name}, bcp47: {Bcp47}, font: {Font}>";
if (Dir is null)
{
return $"<name: {Name}, bcp47: {Bcp47}, font: {Font}>";
}
return $"<name: {Name}, bcp47: {Bcp47}, font: {Font}, dir: {Dir}>";

}
}

Expand Down
340 changes: 340 additions & 0 deletions public/locales/ar/translation.json

Large diffs are not rendered by default.

48 changes: 38 additions & 10 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,8 @@
"noHistory": "Nothing yet",
"done": "All done"
},
"progress": {
"step": "Step",
"stepMerge": "Merge Set",
"of": "of"
}
"progress": "Step {{ val1 }} of {{ val2 }}",
"progressMerge": "Merge Set {{ val1 }} of {{ val2 }}"
},
"createStrWordInv": {
"title": "Create Structural Word Inventory"
Expand Down Expand Up @@ -261,13 +258,43 @@
"domain": "Domains cannot be left blank",
"senses": "Cannot save an entry with no senses",
"vernacular": "Vernacular cannot be left blank"
},
"columns": {
"definitions": "Definitions",
"delete": "Delete",
"domains": "Domains",
"flag": "Flag",
"glosses": "Glosses",
"note": "Note",
"pronunciations": "Pronunciations",
"senses": "Senses",
"vernacular": "Vernacular"
},
"materialTable": {
"body": {
"edit": "Edit",
"emptyDataSourceMessage": "No entries to display",
"filter": "Filter"
},
"pagination": {
"labelDisplayedRows": "{from}-{to} of {count}",
"labelRows": "rows",
"labelRowsPerPage": "Rows per page:",
"first": "First Page",
"last": "Last Page",
"next": "Next Page",
"previous": "Previous Page"
},
"toolbar": {
"search": "Search"
}
}
},
"charInventory": {
"title": "Create Character Inventory",
"characters": "characters",
"examples": "Examples",
"occurrences": "occurrences",
"occurrences": "{{ val }} occurrences",
"status": "status",
"sortBy": "Sort by",
"characterSet": {
Expand Down Expand Up @@ -382,7 +409,7 @@
"deleteRecording": "Delete Recording"
},
"statistics": {
"dataStatistics": "Data Statistics: ",
"title": "Data Statistics: {{ val }}",
"domainNumber": "Domain Number:",
"domainName": "Domain Name:",
"countSenses": "Words:",
Expand All @@ -394,9 +421,10 @@
"dayView": "Words per Day",
"estimate": "Workshop Estimate",
"domainProgress": "Domain Progress",
"domainsCollected": "Domains Collected:",
"wordsCollected": "Words Collected:",
"domainsCollected": "Domains Collected: {{ val }}",
"wordsCollected": "Words Collected: {{ val }}",
"wordsPerDay": "Words Collected Per User Per Day",
"workshopSchedule": "Workshop Schedule"
"workshopSchedule": "Workshop Schedule",
"percent": "{{ val }}%"
}
}
8 changes: 8 additions & 0 deletions public/locales/es/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,14 @@
"domain": "Los dominios no pueden dejarse en blanco",
"senses": "No guardar entrada sin sentidos",
"vernacular": "El vernáculo no puede dejarse en blanco"
},
"columns": {
"definitions": "Definiciones",
"domains": "Dominios",
"glosses": "Glosas",
"note": "Nota",
"senses": "Sentidos",
"vernacular": "Vernáculo"
}
},
"charInventory": {
Expand Down
6 changes: 6 additions & 0 deletions src/api/models/writing-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,10 @@ export interface WritingSystem {
* @memberof WritingSystem
*/
font: string;
/**
*
* @type {string}
* @memberof WritingSystem
*/
dir?: string | null;
}
51 changes: 27 additions & 24 deletions src/components/App/component.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import loadable from "@loadable/component";
import React, { ReactElement, Suspense } from "react";
import { ReactElement, Suspense, useEffect } from "react";
import { Route, Switch } from "react-router-dom";

import { Path } from "browserHistory";
Expand All @@ -12,33 +12,36 @@ import ResetRequest from "components/PasswordReset/RequestPage";
import PasswordReset from "components/PasswordReset/ResetPage";
import PrivateRoute from "components/PrivateRoute";
import ProjectInvite from "components/ProjectInvite";
import i18n from "i18n";

const AppWithBar = loadable(() => import("components/App/AppLoggedIn"));

/**
* The top-level component
*/
export default class App extends React.Component {
render(): ReactElement {
return (
<div className="App">
<Suspense fallback={<div />}>
<AnnouncementBanner />
<Switch>
<Route exact path={Path.Root} component={LandingPage} />
<PrivateRoute path={Path.ProjScreen} component={AppWithBar} />
<Route path={Path.Login} component={Login} />
<Route path={Path.SignUp} component={SignUp} />
<Route path={`${Path.PwReset}/:token`} component={PasswordReset} />
<Route path={Path.PwRequest} component={ResetRequest} />
<Route
path={`${Path.ProjInvite}/:project/:token`}
component={ProjectInvite}
/>
<Route component={PageNotFound} />
</Switch>
</Suspense>
</div>
);
}
export default function App(): ReactElement {
useEffect(() => {
document.body.dir = i18n.dir();
}, []);

return (
<div className="App">
<Suspense fallback={<div />}>
<AnnouncementBanner />
<Switch>
<Route exact path={Path.Root} component={LandingPage} />
<PrivateRoute path={Path.ProjScreen} component={AppWithBar} />
<Route path={Path.Login} component={Login} />
<Route path={Path.SignUp} component={SignUp} />
<Route path={`${Path.PwReset}/:token`} component={PasswordReset} />
<Route path={Path.PwRequest} component={ResetRequest} />
<Route
path={`${Path.ProjInvite}/:project/:token`}
component={ProjectInvite}
/>
<Route component={PageNotFound} />
</Switch>
</Suspense>
</div>
);
}
4 changes: 2 additions & 2 deletions src/components/AppBar/AppBarComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ export default function AppBarComponent(): ReactElement {
spacing={2}
alignItems="center"
>
<Grid item xs={7} md={5} lg={4}>
<Grid item xs={8} sm={7} md={6} lg={4}>
<Logo />
{!!getProjectId() && (
<NavigationButtons currentTab={currentTab} />
)}
</Grid>
<Grid item xs={2} sm={3} md={4}>
<Grid item xs={1} sm={2} md={3} lg={4}>
{!!getProjectId() && (
<ProjectNameButton currentTab={currentTab} />
)}
Expand Down
14 changes: 6 additions & 8 deletions src/components/AppBar/Logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,15 @@ import smallLogo from "resources/CombineSmallLogoV1.png";
/** A button that redirects to the home page */
export default function Logo(): ReactElement {
return (
<Button
onClick={() => {
history.push(Path.ProjScreen);
}}
id="logo-button"
>
<Hidden smDown>
<Button onClick={() => history.push(Path.ProjScreen)} id="logo-button">
<Hidden mdDown>
<img src={logo} height="50" alt="Logo" />
</Hidden>
<Hidden mdUp smDown>
<img src={smallLogo} height="40" alt="Logo" />
</Hidden>
<Hidden smUp>
<img src={smallLogo} height="50" alt="Logo" />
<img src={smallLogo} height="30" alt="Logo" />
</Hidden>
</Button>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ jest.mock("backend", () => ({
updateWord: (word: Word) => mockUpdateWord(word),
getFrontierWords: () => mockGetFrontierWords(),
}));
jest.mock("components/DataEntry/DataEntryTable/NewEntry/SenseDialog");
jest.mock(
"components/DataEntry/DataEntryTable/NewEntry/VernDialog",
() => "div"
);
jest.mock("components/DataEntry/DataEntryTable/RecentEntry/RecentEntry");
jest.mock("components/Pronunciations/PronunciationsComponent", () => "div");
jest.mock("components/Pronunciations/Recorder");
Expand Down
9 changes: 3 additions & 6 deletions src/components/Login/LoginPage/tests/LoginComponent.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,11 @@ jest.mock("backend", () => ({
}));

const LOGOUT = jest.fn();
var loginMaster: ReactTestRenderer;
var loginHandle: ReactTestInstance;
let loginMaster: ReactTestRenderer;
let loginHandle: ReactTestInstance;

const DATA = "stuff";
const MOCK_EVENT = {
preventDefault: jest.fn(),
target: { value: DATA },
};
const MOCK_EVENT = { preventDefault: jest.fn(), target: { value: DATA } };

describe("Testing login component", () => {
beforeEach(() => {
Expand Down
11 changes: 3 additions & 8 deletions src/components/Login/SignUpPage/tests/SignUpComponent.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,11 @@ jest.mock("backend", () => ({
}));

const mockReset = jest.fn();
var signUpMaster: ReactTestRenderer;
var signUpHandle: ReactTestInstance;
let signUpMaster: ReactTestRenderer;
let signUpHandle: ReactTestInstance;

const DATA = "stuff";
const MOCK_EVENT = {
preventDefault: jest.fn(),
target: {
value: DATA,
},
};
const MOCK_EVENT = { preventDefault: jest.fn(), target: { value: DATA } };

describe("Testing sign up component", () => {
beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { ProjectSwitch } from "components/ProjectSettings/ProjectSwitch/ProjectS
import { newProject, randomProject } from "types/project";

const projects = [randomProject(), randomProject(), randomProject()];
var switchMaster: ReactTestRenderer;
var switchHandle: ProjectSwitch;
let switchMaster: ReactTestRenderer;
let switchHandle: ProjectSwitch;

const createMockStore = configureMockStore();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import theme from "types/theme";
jest.mock("components/Pronunciations/Recorder");

// Variables
var testRenderer: ReactTestRenderer;
let testRenderer: ReactTestRenderer;

const createMockStore = configureMockStore();
const mockStore = createMockStore({ pronunciationsState });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const mockProjects = [randomProject(), randomProject(), randomProject()];

jest.mock("components/ProjectExport/ExportButton", () => "div");

var testRenderer: ReactTestRenderer;
let testRenderer: ReactTestRenderer;

beforeAll(() => {
renderer.act(() => {
Expand Down
Loading