mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
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:
@@ -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']}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user