Make a good part of the app localizable and add French translations (#325)

Co-authored-by: Yohan Boniface <yohanboniface@free.fr>
This commit is contained in:
Arnaud Peich
2022-10-28 18:11:08 +02:00
committed by GitHub
parent ec20e7fb68
commit 79deeca640
78 changed files with 2364 additions and 665 deletions

View File

@@ -1,4 +1,5 @@
import {get as getBrowserGlobals} from 'app/client/lib/browserGlobals';
import {makeT} from 'app/client/lib/localization';
import {error} from 'app/client/lib/log';
import {reportError, setErrorNotifier} from 'app/client/models/errors';
import {urlState} from 'app/client/models/gristUrlState';
@@ -22,6 +23,8 @@ import {getOrgName, Organization, OrgError, UserAPI, UserAPIImpl} from 'app/comm
import {getUserPrefObs, getUserPrefsObs} from 'app/client/models/UserPrefs';
import {bundleChanges, Computed, Disposable, Observable, subscribe} from 'grainjs';
const t = makeT('models.AppModel')
// Reexported for convenience.
export {reportError} from 'app/client/models/errors';
@@ -192,7 +195,7 @@ export class TopAppModelImpl extends Disposable implements TopAppModel {
if (org.billingAccount && org.billingAccount.product &&
org.billingAccount.product.name === 'suspended') {
this.notifier.createUserMessage(
'This team site is suspended. Documents can be read, but not modified.',
t('TeamSiteSuspended'),
{actions: ['renew', 'personal']}
);
}

View File

@@ -25,9 +25,12 @@ import {canEdit, isOwner} from 'app/common/roles';
import {Document, NEW_DOCUMENT_CODE, Organization, UserAPI, Workspace} from 'app/common/UserAPI';
import {Holder, Observable, subscribe} from 'grainjs';
import {Computed, Disposable, dom, DomArg, DomElementArg} from 'grainjs';
import {makeT} from 'app/client/lib/localization';
// tslint:disable:no-console
const t = makeT('models.DocPageModel');
export interface DocInfo extends Document {
isReadonly: boolean;
isPreFork: boolean;
@@ -233,17 +236,13 @@ export class DocPageModelImpl extends Disposable implements DocPageModel {
const isDenied = (err as any).code === 'ACL_DENY';
const isDocOwner = isOwner(this.currentDoc.get());
confirmModal(
"Error accessing document",
"Reload",
t("ErrorAccessingDocument"),
t("Reload"),
async () => window.location.reload(true),
isDocOwner ? `You can try reloading the document, or using recovery mode. ` +
`Recovery mode opens the document to be fully accessible to owners, and ` +
`inaccessible to others. It also disables formulas. ` +
`[${err.message}]` :
isDenied ? `Sorry, access to this document has been denied. [${err.message}]` :
`Document owners can attempt to recover the document. [${err.message}]`,
isDocOwner ? t('ReloadingOrRecoveryMode', {error: err.message}) :
t('AccessError', {context: isDenied ? 'denied' : 'recover', error: err.message}),
{ hideCancel: true,
extraButtons: (isDocOwner && !isDenied) ? bigBasicButton('Enter recovery mode', dom.on('click', async () => {
extraButtons: (isDocOwner && !isDenied) ? bigBasicButton(t('EnterRecoveryMode'), dom.on('click', async () => {
await this._api.getDocAPI(this.currentDocId.get()!).recover(true);
window.location.reload(true);
}), testId('modal-recovery-mode')) : null,
@@ -339,18 +338,18 @@ function addMenu(importSources: ImportSource[], gristDoc: GristDoc, isReadonly:
menuItem(
(elem) => openPageWidgetPicker(elem, gristDoc.docModel, (val) => gristDoc.addNewPage(val).catch(reportError),
{isNewPage: true, buttonLabel: 'Add Page'}),
menuIcon("Page"), "Add Page", testId('dp-add-new-page'),
menuIcon("Page"), t("AddPage"), testId('dp-add-new-page'),
dom.cls('disabled', isReadonly)
),
menuItem(
(elem) => openPageWidgetPicker(elem, gristDoc.docModel, (val) => gristDoc.addWidgetToPage(val).catch(reportError),
{isNewPage: false, selectBy}),
menuIcon("Widget"), "Add Widget to Page", testId('dp-add-widget-to-page'),
menuIcon("Widget"), t("AddWidgetToPage"), testId('dp-add-widget-to-page'),
// disable for readonly doc and all special views
dom.cls('disabled', (use) => typeof use(gristDoc.activeViewId) !== 'number' || isReadonly),
),
menuItem(() => gristDoc.addEmptyTable().catch(reportError),
menuIcon("TypeTable"), "Add Empty Table", testId('dp-empty-table'),
menuIcon("TypeTable"), t("AddEmptyTable"), testId('dp-empty-table'),
dom.cls('disabled', isReadonly)
),
menuDivider(),
@@ -362,7 +361,7 @@ function addMenu(importSources: ImportSource[], gristDoc: GristDoc, isReadonly:
dom.cls('disabled', isReadonly)
)
),
isReadonly ? menuText('You do not have edit access to this document') : null,
isReadonly ? menuText(t('NoEditAccess')) : null,
testId('dp-add-new-menu')
];
}

View File

@@ -1,3 +1,4 @@
import {makeT} from 'app/client/lib/localization';
import {GristDoc} from 'app/client/components/GristDoc';
import {AppModel} from 'app/client/models/AppModel';
import {DocPageModel} from 'app/client/models/DocPageModel';
@@ -10,6 +11,8 @@ import {ANONYMOUS_USER_EMAIL, Document, EVERYONE_EMAIL, FullUser, getRealAccess,
import {computed, Computed, Disposable, obsArray, ObsArray, observable, Observable} from 'grainjs';
import some = require('lodash/some');
const t = makeT('models.UserManagerModel');
export interface UserManagerModel {
initData: PermissionData; // PermissionData used to initialize the UserManager
resource: Resource|null; // The access resource.
@@ -97,28 +100,28 @@ interface IBuildMemberOptions {
export class UserManagerModelImpl extends Disposable implements UserManagerModel {
// Select options for each individual user's role dropdown.
public readonly userSelectOptions: IMemberSelectOption[] = [
{ value: roles.OWNER, label: 'Owner' },
{ value: roles.EDITOR, label: 'Editor' },
{ value: roles.VIEWER, label: 'Viewer' }
{ value: roles.OWNER, label: t('Owner') },
{ value: roles.EDITOR, label: t('Editor') },
{ value: roles.VIEWER, label: t('Viewer') }
];
// Select options for each individual user's role dropdown in the org.
public readonly orgUserSelectOptions: IOrgMemberSelectOption[] = [
{ value: roles.OWNER, label: 'Owner' },
{ value: roles.EDITOR, label: 'Editor' },
{ value: roles.VIEWER, label: 'Viewer' },
{ value: roles.MEMBER, label: 'No Default Access' },
{ value: roles.OWNER, label: t('Owner') },
{ value: roles.EDITOR, label: t('Editor') },
{ value: roles.VIEWER, label: t('Viewer') },
{ value: roles.MEMBER, label: t('NoDefaultAccess') },
];
// Select options for the resource's maxInheritedRole dropdown.
public readonly inheritSelectOptions: IMemberSelectOption[] = [
{ value: roles.OWNER, label: 'In Full' },
{ value: roles.EDITOR, label: 'View & Edit' },
{ value: roles.VIEWER, label: 'View Only' },
{ value: null, label: 'None' }
{ value: roles.OWNER, label: t('InFull') },
{ value: roles.EDITOR, label: t('ViewAndEdit') },
{ value: roles.VIEWER, label: t('ViewOnly') },
{ value: null, label: t('None') }
];
// Select options for the public member's role dropdown.
public readonly publicUserSelectOptions: IMemberSelectOption[] = [
{ value: roles.EDITOR, label: 'Editor' },
{ value: roles.VIEWER, label: 'Viewer' },
{ value: roles.EDITOR, label: t('Editor') },
{ value: roles.VIEWER, label: t('Viewer') },
];
public activeUser: FullUser|null = this._options.activeUser ?? null;