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

[DNM] feat(#9682): rtl support #9727

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions admin/src/js/controllers/edit-language.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ angular.module('controllers').controller('EditLanguageCtrl',

$scope.language = _.clone($scope.model) || {
enabled: true,
rtl: false,
generic: {},
custom: {},
type: 'translations'
Expand Down
2 changes: 1 addition & 1 deletion admin/src/templates/display_languages.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ <h4 class="panel-title">
<div class="alert alert-warning" role="alert" ng-show="{{localeModel.missing > 0}}" translate translate-values="localeModel">Missing translations</div>
<div>
<button type="button" class="btn btn-link" ng-click="editLanguage(localeModel.doc)">
<i class="fa fa-pencil"></i><span translate>edit.name</span>
<i class="fa fa-pencil"></i><span translate>edit</span>
</button>
</div>
<div ng-if="localeModel.enabled">
Expand Down
7 changes: 7 additions & 0 deletions admin/src/templates/edit_language.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,12 @@
<span class="help-block" translate>Language name help</span>
</div>

<div class="form-group" ng-class="{'has-error': errors.rtl}">
<label translate>RTL</label>
<input type="checkbox" ng-model="language.rtl" />
<span class="error" ng-show="errors.rtl">{{errors.rtl}}</span>
<span class="help-block" translate>Language RTL help</span>
</div>

</form>
</mm-modal>
5 changes: 3 additions & 2 deletions api/resources/translations/messages-en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ Language\ For\ Outgoing\ Messages = Language for outgoing messages
Language\ code = Language code
Language\ code\ help = The 2 or 3 digit code for the language following the
Language\ name\ help = The display name for the language.
Language\ RTL\ help = Whether the language should enable Right-To-Left writing UI.
Language\ to\ edit = Language to edit
Languages = Languages
Last\ Appointment = Last appointment
Expand Down Expand Up @@ -654,8 +655,8 @@ enketo.geopicker.altitude = altitude (m)
enketo.geopicker.closepolygon = close polygon
enketo.geopicker.kmlcoords = KML coordinates
enketo.geopicker.kmlpaste = paste KML coordinates here
enketo.geopicker.latitude = latitude (x.y °)
enketo.geopicker.longitude = longitude (x.y °)
enketo.geopicker.latitude = latitude (x.y �)
enketo.geopicker.longitude = longitude (x.y �)
enketo.geopicker.points = points
enketo.geopicker.searchPlaceholder = search for place or address
enketo.geopicker.removePoint = This will completely remove the current geopoint from the list of geopoints and cannot be undone. Are you sure you want to do this?
Expand Down
1 change: 1 addition & 0 deletions api/resources/translations/messages-es.properties
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ Language\ For\ Outgoing\ Messages = Idioma para mensajes salientes
Language\ code = Código de idioma
Language\ code\ help = El código de 2 o 3 dígitos para el idioma que sigue
Language\ name\ help = Nombre de visualización para el idioma
Language\ RTL\ help = Whether the language should enable Right-To-Left writing UI.
Language\ to\ edit = Idioma a editar
Languages = Idiomas
Last\ Appointment = Última cita
Expand Down
1 change: 1 addition & 0 deletions api/resources/translations/messages-fr.properties
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ Language\ For\ Outgoing\ Messages = Langue pour les messages sortants
Language\ code = Code de langue
Language\ code\ help = Le code de 2 ou 3 chiffres pour la langue.
Language\ name\ help = Le nom d'affichage de la langue.
Language\ RTL\ help = Whether the language should enable Right-To-Left writing UI.
Language\ to\ edit = Langue à modifier
Languages = Langues
Last\ Appointment = Dernier rendez-vous
Expand Down
1 change: 1 addition & 0 deletions api/resources/translations/messages-ne.properties
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ Language\ For\ Outgoing\ Messages = सन्देश पठाउने भा
Language\ code = भाषा कोड
Language\ code\ help = भाषाको लागि २ वा ३ अंक कोड ।
Language\ name\ help = भाषा नाम को लागि सहयोग
Language\ RTL\ help = Whether the language should enable Right-To-Left writing UI.
Language\ to\ edit = सच्याउने भाषा
Languages = भाषा
Last\ Appointment = पछिल्लो पटकको जाँच/ भेट
Expand Down
1 change: 1 addition & 0 deletions api/resources/translations/messages-sw.properties
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ Language\ For\ Outgoing\ Messages = Lugha inayotumika kutuma ujumbe
Language\ code = Kificho cha lugha
Language\ code\ help = Nambari 2 au 3 za kutambua lugha
Language\ name\ help = Jina la lugha linaloonekana
Language\ RTL\ help = Whether the language should enable Right-To-Left writing UI.
Language\ to\ edit = Lugha ya kuhariri
Languages = Lugha
Last\ Appointment = Tarehe ya mwisho uliotembelea kliniki
Expand Down
10 changes: 9 additions & 1 deletion api/src/translations.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const LOCAL_NAME_MAP = {
ar: 'عربي (Arabic)'
};

const RTL_LANGUAGES = ['ar'];

const extractLocaleCode = filename => {
const parts = TRANSLATION_FILE_NAME_REGEX.exec(filename);
if (parts && parts[1]) {
Expand Down Expand Up @@ -54,7 +56,8 @@ const createDoc = attachment => {
code: attachment.code,
name: LOCAL_NAME_MAP[attachment.code] || attachment.code,
enabled: true,
generic: attachment.generic
generic: attachment.generic,
rtl: RTL_LANGUAGES.includes(attachment.code),
};
};

Expand Down Expand Up @@ -88,6 +91,11 @@ const overwrite = (translationFiles, docs) => {
doc.generic = file.generic;
updatedDocs.push(doc);
}

if (RTL_LANGUAGES.includes(file.code) && !doc.rtl) {
doc.rtl = true;
updatedDocs.push(doc);
}
});
return updatedDocs;
};
Expand Down
75 changes: 73 additions & 2 deletions api/tests/mocha/translations.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,53 @@ describe('translations', () => {
hello: 'Hello UPDATED',
bye: 'Goodbye UPDATED',
added: 'ADDED'
}
},
rtl: false,
}
]);
});
});

it('should create new RTL language', async () => {
fs.promises.readdir.resolves(['messages-ar.properties']);
fs.promises.readFile.resolves('some buffer');

const docs = [{
doc: {
_id: 'messages-en',
code: 'en',
type: 'translations',
generic: { hello: 'Hello', bye: 'Goodbye CUSTOMISED' }
}
}];

properties.parse.callsArgWith(1, null, { hello: 'Hello UPDATED', bye: 'Goodbye UPDATED', added: 'ADDED' });
sinon.stub(db.medic, 'allDocs').resolves({ rows: docs });
sinon.stub(db.medic, 'bulkDocs').resolves();
await translations.run();

chai.expect(fs.promises.readdir.callCount).to.equal(1);
chai.expect(fs.promises.readFile.callCount).to.equal(1);
chai.expect(properties.parse.callCount).to.equal(1);
chai.expect(db.medic.allDocs.callCount).to.equal(1);
chai.expect(db.medic.bulkDocs.callCount).to.equal(1);
chai.expect(db.medic.bulkDocs.args[0][0]).to.deep.equal([
{ // new
_id: 'messages-ar',
type: 'translations',
code: 'ar',
name: 'عربي (Arabic)',
enabled: true,
generic: {
hello: 'Hello UPDATED',
bye: 'Goodbye UPDATED',
added: 'ADDED'
},
rtl: true,
}
]);
});

it('does not recreate deleted language', () => {
fs.promises.readdir.resolves(['messages-fr.properties']);
fs.promises.readFile.resolves('some buffer');
Expand Down Expand Up @@ -268,7 +309,8 @@ describe('translations', () => {
added: 'ADDED'
},
name: 'Français (French)',
enabled: true
enabled: true,
rtl: false,
}
]);
});
Expand Down Expand Up @@ -452,6 +494,35 @@ describe('translations', () => {
});
});

it('should set RTL languages', async () => {
fs.promises.readdir.resolves(['messages-ar.properties']);
fs.promises.readFile.resolves('some buffer');

const docs = [{
doc: {
_id: 'messages-ar',
code: 'ar',
type: 'translations',
generic: { hello: null, bye: 0, ciao: false, adios: 23, salut: true }
}
}];

properties.parse.callsArgWith(1, null, { hello: null, bye: 0, ciao: false, adios: 23, salut: true });
sinon.stub(db.medic, 'allDocs').resolves({ rows: docs });
sinon.stub(db.medic, 'bulkDocs').resolves();
await translations.run();

chai.expect(db.medic.bulkDocs.args[0][0]).to.deep.equal([
{
_id: 'messages-ar',
code: 'ar',
type: 'translations',
rtl: true,
generic: { hello: null, bye: 0, ciao: false, adios: 23, salut: true }
}
]);
});

describe('getTranslationDocs', () => {
it('should return all translation docs', () => {
sinon.stub(db.medic, 'allDocs').resolves({
Expand Down
5 changes: 4 additions & 1 deletion tests/integration/cht-form/wdio.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ const ALLOWED_LOG_MSGS = [
'tag was parsed inside of a <select> which was not inserted into the document. This is not valid ' +
'HTML and the behavior may be changed in future versions of chrome.',
'invalid phone number',
'Error submitting form data:'
'Error submitting form data:',
`Error initialising watching for db changes" TypeError: Cannot read properties of undefined (reading 'update_seq')`,
'Attempting changes initialisation in 5 seconds'
];
const notAllowedLog = (msg) => {
return !ALLOWED_LOG_MSGS.some(allowedMsg => msg.includes(allowedMsg));
Expand Down Expand Up @@ -64,6 +66,7 @@ const defaultConfig = {
.map(({ message }) => message)
.filter(notAllowedLog);
if (logs.length) {
console.warn(logs.join('\n'));
test.callback(new Error(`Browser console logs are not empty: ${logs.join('\n')}`));
}
},
Expand Down
3 changes: 2 additions & 1 deletion webapp/src/css/inbox.less
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
@import 'error-log';
@import 'tooltip';
@import 'old-nav';
@import 'rtl';

.container-fluid {
overflow: hidden;
Expand Down Expand Up @@ -1918,4 +1919,4 @@ mm-sidebar-menu .mat-sidenav-container {
.desktop-only {
display: none;
}
}
}
151 changes: 151 additions & 0 deletions webapp/src/css/rtl.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
@import "variables";

.app-root:not(.old-nav).rtl {
direction: rtl;
* {
direction: rtl;
}

mm-header {
right: 0;
left: auto;
}

.content .page {
padding-right: 80px;
padding-left: 0;
}

.left-pane {
float: right;
}

.fast-action-trigger:not(.embed-fast-action) {
top: 60px;
left: 5px;
right: auto;
}

.content-row {
border-left: 0;
border-right: 5px solid transparent;

a {
padding: 10px 8px 10px 20px;
.icon {
margin: 5px 0 5px 7px;
}
}

input[type="checkbox"] {
float: right;
margin: 20px 10px 20px 7px;
}
}

&.reports .content-row:hover {
border-right-color: @reports-color;
}

.select-mode-available .select-all .select-all-label {
margin-right: 15px;
}

.item-summary .icon {
float: right;
margin-left: 4px;
margin-right: 0;
}

.sidebar-filter .sidebar-main {
.sidebar-header .sidebar-reset {
margin-right: 0;
margin-left: 20px;
}

.sidebar-body {
.filter-options li a {
padding-right: 31px;
padding-left: 0;
}

mat-expansion-panel mat-expansion-panel-header .chip {
margin-right: 10px;
}

.filter-options li.accordion-facility-list .accordion-facility-check {
right: 0;
left: auto;
}

.filter-options li.accordion-facility-list.accordion-facility-caret > .accordion-facility-chevron {
left: 0;
right: auto;
}

.filter-options li a:before {
float: right;
margin: 5px -30px 5px 7px;
}
}
}

mm-search-bar .mm-search-bar-container .btn.open-filter .open-filter-label {
margin-left: 6px;
}

.item-content .deselect {
float: left;
clear: left;
margin-right: -10px;
}

&.contacts .action-header {
h3 {
float: right;
}
.table-filter {
float: left;
}
}

.targets .target .heading .icon {
margin-left: 10px;
margin-right: 0;
}

.enketo .or h2, .enketo .or h3, .enketo .or h4 {
text-align: right;
}

.enketo .required {
right: 5px;
}

.progress-bar {
float: right;
}

&.reports .right-pane .item-content {
float: right;
}

@media (max-width: 767px) {
mm-sidebar-menu .mat-sidenav-container .nav-item mat-icon {
margin-right: -6px;
margin-left: 15px;
}

.content .page {
padding-right: 15px;
padding-left: 0;
}
}
}

[dir="rtl"] {
.fast-action-body .fast-action-item .fast-action-item-icon {
margin-left: 20px;
margin-right: 0;
}
}
Loading
Loading