l10n: add type safety

Signed-off-by: Varun Patil <radialapps@gmail.com>
pull/900/head
Varun Patil 2023-10-29 13:14:28 -07:00
parent 79d8a8675c
commit 9d75451f32
44 changed files with 107 additions and 85 deletions

View File

@ -74,10 +74,10 @@ import NcAppNavigation from '@nextcloud/vue/dist/Components/NcAppNavigation';
const NcAppNavigationItem = () => import('@nextcloud/vue/dist/Components/NcAppNavigationItem');
import { generateUrl } from '@nextcloud/router';
import { translate as t } from '@nextcloud/l10n';
import * as utils from './services/utils';
import * as nativex from './native';
import { translate as t } from './services/l10n';
import staticConfig from './services/static-config';
import UserConfig from './mixins/UserConfig';
import Timeline from './components/Timeline.vue';

View File

@ -4,7 +4,6 @@ import Vue from 'vue';
import { generateFilePath } from '@nextcloud/router';
import { getRequestToken } from '@nextcloud/auth';
import { translate, translatePlural } from '@nextcloud/l10n';
// Global components
import XImg from './components/frame/XImg.vue';
@ -14,6 +13,7 @@ import VueVirtualScroller from 'vue-virtual-scroller';
// Locals
import router, { routes } from './router';
import { constants, initstate } from './services/utils';
import { translate, translatePlural } from 'services/l10n';
// CSS for components
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';

View File

@ -42,8 +42,6 @@
import { defineComponent } from 'vue';
import type { Component } from 'vue';
import { translate as t } from '@nextcloud/l10n';
import ClusterHList from './ClusterHList.vue';
import NcButton from '@nextcloud/vue/dist/Components/NcButton';
@ -56,6 +54,7 @@ import CalendarIcon from 'vue-material-design-icons/Calendar.vue';
import MapIcon from 'vue-material-design-icons/Map.vue';
import CogIcon from 'vue-material-design-icons/Cog.vue';
import { translate as t } from 'services/l10n';
import config from '../services/static-config';
import * as dav from '../services/dav';

View File

@ -87,9 +87,9 @@ import ImageIcon from 'vue-material-design-icons/Image.vue';
import LocationIcon from 'vue-material-design-icons/MapMarker.vue';
import TagIcon from 'vue-material-design-icons/Tag.vue';
import * as utils from '../services/utils';
import * as dav from '../services/dav';
import { API } from '../services/API';
import * as utils from 'services/utils';
import * as dav from 'services/dav';
import { API } from 'services/API';
import type { IAlbum, IFace, IImageInfo, IPhoto, IExif } from '../types';

View File

@ -44,8 +44,7 @@ import UserConfig from '../mixins/UserConfig';
import NcActions from '@nextcloud/vue/dist/Components/NcActions';
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import * as dav from '../services/dav';
import * as utils from '../services/utils';
import * as nativex from '../native';

View File

@ -36,7 +36,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import AdminMixin from '../AdminMixin';

View File

@ -36,7 +36,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import AdminMixin from '../AdminMixin';

View File

@ -88,7 +88,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import AdminMixin from '../AdminMixin';

View File

@ -40,7 +40,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import AdminMixin from '../AdminMixin';

View File

@ -124,7 +124,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import AdminMixin from '../AdminMixin';

View File

@ -29,7 +29,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import AdminMixin from '../AdminMixin';

View File

@ -71,7 +71,7 @@
import { defineComponent } from 'vue';
import { API } from '../../../services/API';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import * as utils from '../../../services/utils';
import AdminMixin from '../AdminMixin';

View File

@ -81,7 +81,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import AdminMixin from '../AdminMixin';

View File

@ -145,7 +145,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import AdminMixin from '../AdminMixin';

View File

@ -68,7 +68,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import AdminMixin from '../AdminMixin';

View File

@ -37,7 +37,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import AdminMixin from '../AdminMixin';

View File

@ -7,7 +7,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import AdminMixin from '../AdminMixin';

View File

@ -1,7 +1,7 @@
<template>
<div class="manage-collaborators">
<div class="manage-collaborators__subtitle">
{{ t('photos', 'Add people or groups who can edit your album') }}
{{ t('memories', 'Add people or groups who can edit your album') }}
</div>
<form class="manage-collaborators__form" @submit.prevent>
@ -12,10 +12,10 @@
autocomplete="off"
type="search"
name="search"
:aria-label="t('photos', 'Search for collaborators')"
:aria-label="t('memories', 'Search for collaborators')"
aria-autocomplete="list"
:aria-controls="`manage-collaborators__form__selection-${randomId} manage-collaborators__form__list-${randomId}`"
:placeholder="t('photos', 'Search people or groups')"
:placeholder="t('memories', 'Search people or groups')"
@input="searchCollaborators"
>
<Magnify :size="16" />
@ -37,7 +37,7 @@
:user="availableCollaborators[collaboratorKey].id"
:display-name="availableCollaborators[collaboratorKey].label"
:aria-label="
t('photos', 'Add {collaboratorLabel} to the collaborators list', {
t('memories', 'Add {collaboratorLabel} to the collaborators list', {
collaboratorLabel: availableCollaborators[collaboratorKey].label,
})
"
@ -49,7 +49,7 @@
v-else
key="emptycontent"
class="manage-collaborators__form__list--empty"
:title="t('photos', 'No collaborators available')"
:title="t('memories', 'No collaborators available')"
>
<AccountGroup slot="icon" />
</NcEmptyContent>
@ -71,7 +71,7 @@
<NcButton
type="tertiary"
:aria-label="
t('photos', 'Remove {collaboratorLabel} from the collaborators list', {
t('memories', 'Remove {collaboratorLabel} from the collaborators list', {
collaboratorLabel: availableCollaborators[collaboratorKey].label,
})
"
@ -88,15 +88,15 @@
<template v-if="isPublicLinkSelected">
<NcButton
class="manage-collaborators__public-link-button"
:aria-label="t('photos', 'Copy the public link')"
:aria-label="t('memories', 'Copy the public link')"
:disabled="publicLink.id === ''"
@click="copyPublicLink"
>
<template v-if="publicLinkCopied">
{{ t('photos', 'Public link copied!') }}
{{ t('memories', 'Public link copied!') }}
</template>
<template v-else>
{{ t('photos', 'Copy public link') }}
{{ t('memories', 'Copy public link') }}
</template>
<template #icon>
<Check v-if="publicLinkCopied" />
@ -105,7 +105,7 @@
</NcButton>
<NcButton
type="tertiary"
:aria-label="t('photos', 'Delete the public link')"
:aria-label="t('memories', 'Delete the public link')"
:disabled="publicLink.id === ''"
@click="deletePublicLink"
>
@ -115,7 +115,7 @@
</template>
<NcButton v-else class="manage-collaborators__public-link-button" @click="createPublicLinkForAlbum">
<Earth slot="icon" />
{{ t('photos', 'Share via public link') }}
{{ t('memories', 'Share via public link') }}
</NcButton>
</div>
@ -296,7 +296,7 @@ export default defineComponent({
};
} catch (error) {
this.errorFetchingCollaborators = error;
showError(this.t('photos', 'Failed to fetch collaborators list.'));
showError(this.t('memories', 'Failed to fetch collaborators list.'));
} finally {
this.loadingCollaborators = false;
}
@ -311,7 +311,7 @@ export default defineComponent({
this.availableCollaborators = {
3: {
id: '',
label: this.t('photos', 'Public link'),
label: this.t('memories', 'Public link'),
type: Type.SHARE_TYPE_LINK,
},
...this.availableCollaborators,
@ -352,7 +352,7 @@ export default defineComponent({
this.errorFetchingAlbum = error;
}
showError(this.t('photos', 'Failed to fetch album.'));
showError(this.t('memories', 'Failed to fetch album.'));
} finally {
this.loadingAlbum = false;
}
@ -362,7 +362,7 @@ export default defineComponent({
this.unselectEntity(`${Type.SHARE_TYPE_LINK}`);
this.availableCollaborators[3] = {
id: '',
label: this.t('photos', 'Public link'),
label: this.t('memories', 'Public link'),
type: Type.SHARE_TYPE_LINK,
};
this.publicLinkCopied = false;
@ -380,7 +380,7 @@ export default defineComponent({
},
});
} catch (error) {
showError(this.t('photos', 'Failed to update album.'));
showError(this.t('memories', 'Failed to update album.'));
} finally {
this.loadingAlbum = false;
}

View File

@ -10,7 +10,7 @@
</template>
<div class="outer">
<AlbumForm :album="album" :display-back-button="false" :title="t('photos', 'New album')" @done="done" />
<AlbumForm :album="album" :display-back-button="false" :title="t('memories', 'New album')" @done="done" />
</div>
</Modal>
</template>
@ -51,7 +51,7 @@ export default defineComponent({
this.album = await dav.getAlbum(this.$route.params.user, this.$route.params.name);
} catch (e) {
console.error(e);
showError(this.t('photos', 'Could not load the selected album'));
showError(this.t('memories', 'Could not load the selected album'));
return;
}
} else {

View File

@ -75,7 +75,7 @@ export default defineComponent({
this.close();
} catch (error) {
console.log(error);
showError(this.t('photos', 'Failed to delete {name}.', { name: this.name }));
showError(this.t('memories', 'Failed to delete {name}.', { name: this.name }));
}
},
},

View File

@ -8,14 +8,14 @@
name="name"
:required="true"
autofocus="true"
:placeholder="t('photos', 'Name of the album')"
:placeholder="t('memories', 'Name of the album')"
/>
<label>
<NcTextField
:value.sync="albumLocation"
name="location"
type="text"
:placeholder="t('photos', 'Location of the album')"
:placeholder="t('memories', 'Location of the album')"
/>
</label>
</div>
@ -23,17 +23,17 @@
<span class="left-buttons">
<NcButton
v-if="displayBackButton"
:aria-label="t('photos', 'Go back to the previous view.')"
:aria-label="t('memories', 'Go back to the previous view.')"
type="tertiary"
@click="back"
>
{{ t('photos', 'Back') }}
{{ t('memories', 'Back') }}
</NcButton>
</span>
<span class="right-buttons">
<NcButton
v-if="sharingEnabled && !editMode"
:aria-label="t('photos', 'Go to the add collaborators view.')"
:aria-label="t('memories', 'Go to the add collaborators view.')"
type="secondary"
:disabled="albumName.trim() === '' || loading"
@click="showCollaboratorView = true"
@ -41,7 +41,7 @@
<template #icon>
<AccountMultiplePlus />
</template>
{{ t('photos', 'Add collaborators') }}
{{ t('memories', 'Add collaborators') }}
</NcButton>
<NcButton :aria-label="saveText" type="primary" :disabled="albumName === '' || loading" @click="submit()">
<template #icon>
@ -63,11 +63,11 @@
>
<span class="left-buttons">
<NcButton
:aria-label="t('photos', 'Back to the new album form.')"
:aria-label="t('memories', 'Back to the new album form.')"
type="tertiary"
@click="showCollaboratorView = false"
>
{{ t('photos', 'Back') }}
{{ t('memories', 'Back') }}
</NcButton>
</span>
<span class="right-buttons">
@ -153,7 +153,7 @@ export default defineComponent({
},
saveText(): string {
return this.editMode ? this.t('photos', 'Save') : this.t('photos', 'Create album');
return this.editMode ? this.t('memories', 'Save') : this.t('memories', 'Create album');
},
/**

View File

@ -13,7 +13,7 @@
v-slot="{ collaborators }"
>
<NcButton
:aria-label="t('photos', 'Save collaborators for this album.')"
:aria-label="t('memories', 'Save collaborators for this album.')"
type="primary"
:disabled="loadingAddCollaborators"
@click="handleSetCollaborators(collaborators)"
@ -21,7 +21,7 @@
<template #icon>
<XLoadingIcon v-if="loadingAddCollaborators" />
</template>
{{ t('photos', 'Save') }}
{{ t('memories', 'Save') }}
</NcButton>
</AlbumCollaborators>

View File

@ -27,7 +27,7 @@ import { IExif, IPhoto } from '../../types';
const NcTextField = () => import('@nextcloud/vue/dist/Components/NcTextField');
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
interface IField {
field: keyof IExif;

View File

@ -75,7 +75,7 @@ export default defineComponent({
this.close();
} catch (error) {
console.log(error);
showError(this.t('photos', 'Failed to delete {name}.', { name: this.name }));
showError(this.t('memories', 'Failed to delete {name}.', { name: this.name }));
}
},
},

View File

@ -102,7 +102,7 @@ export default defineComponent({
} catch (error) {
console.log(error);
showError(
this.t('photos', 'Failed to rename {oldName} to {name}.', {
this.t('memories', 'Failed to rename {oldName} to {name}.', {
oldName: this.name,
name: this.input,
}),

View File

@ -144,7 +144,7 @@ export default defineComponent({
}
} catch (error) {
console.error(error);
showError(this.t('photos', 'Failed to move {name}.', { name }));
showError(this.t('memories', 'Failed to move {name}.', { name }));
}
},
},

View File

@ -120,7 +120,7 @@ export default defineComponent({
}
} catch (error) {
console.error(error);
showError(this.t('photos', 'An error occurred while moving photos from {name}.', { name }));
showError(this.t('memories', 'An error occurred while moving photos from {name}.', { name }));
} finally {
this.close();
}

View File

@ -1,4 +1,4 @@
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
/**
* Translations file from library source

View File

@ -4,7 +4,7 @@ import * as nativex from '../../native';
import * as utils from '../../services/utils';
import { showError } from '@nextcloud/dialogs';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import { API } from '../../services/API';
import type { PsContent, PsEvent } from './types';

3
src/globals.d.ts vendored
View File

@ -1,13 +1,12 @@
import Router, { type Route } from 'vue-router';
import type { ComponentPublicInstance } from 'vue';
import type { translate, translatePlural } from '@nextcloud/l10n';
import type PlyrType from 'plyr';
import type videojsType from 'video.js';
import type { IPhoto, TimelineState } from './types';
import type { constants, initstate } from './services/utils';
import type { translate, translatePlural } from 'services/l10n';
import type { GlobalRouteCheckers, routes } from './router';
// Global exposed variables

View File

@ -2,7 +2,6 @@ import Router, { Route, RouteConfig } from 'vue-router';
import Vue from 'vue';
import { generateUrl } from '@nextcloud/router';
import { translate as t } from '@nextcloud/l10n';
import Timeline from './components/Timeline.vue';
import Explore from './components/Explore.vue';
@ -10,6 +9,7 @@ import SplitTimeline from './components/SplitTimeline.vue';
import ClusterView from './components/ClusterView.vue';
import NativeXSetup from './native/Setup.vue';
import { translate as t } from 'services/l10n';
import { constants as c } from './services/utils';
// Routes are defined here

View File

@ -2,9 +2,10 @@ import * as base from './base';
import axios from '@nextcloud/axios';
import { showError } from '@nextcloud/dialogs';
import { translate as t, getLanguage } from '@nextcloud/l10n';
import { getLanguage } from '@nextcloud/l10n';
import { IAlbum, IFileInfo, IPhoto } from '../../types';
import { translate as t } from 'services/l10n';
import { API } from '../API';
import client from './client';
@ -121,7 +122,7 @@ export async function createAlbum(albumName: string) {
await client.createDirectory(`/photos/${utils.uid}/albums/${albumName}`);
} catch (error) {
console.error(error);
showError(t('photos', 'Failed to create {albumName}.', { albumName }));
showError(t('memories', 'Failed to create {albumName}.', { albumName }));
}
}
@ -167,7 +168,7 @@ export async function updateAlbum(album: any, { albumName, properties }: any) {
} catch (error) {
console.error(error);
showError(
t('photos', 'Failed to update properties of {albumName} with {properties}.', {
t('memories', 'Failed to update properties of {albumName} with {properties}.', {
albumName,
properties: JSON.stringify(properties),
}),
@ -223,7 +224,7 @@ export async function renameAlbum(album: any, currentAlbumName: string, newAlbum
} catch (error) {
console.error(error);
showError(
t('photos', 'Failed to rename {currentAlbumName} to {newAlbumName}.', {
t('memories', 'Failed to rename {currentAlbumName} to {newAlbumName}.', {
currentAlbumName,
newAlbumName,
}),

View File

@ -1,6 +1,6 @@
import * as base from './base';
import { showError } from '@nextcloud/dialogs';
import { translate as t, translatePlural as n } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import axios from '@nextcloud/axios';
import { API } from '../API';

View File

@ -1,14 +1,14 @@
import { showError } from '@nextcloud/dialogs';
import { translate as t } from '@nextcloud/l10n';
import axios from '@nextcloud/axios';
import { IFileInfo, IPhoto } from '../../types';
import { API } from '../API';
import { getAlbumFileInfos } from './albums';
import client from './client';
import * as utils from '../utils';
import { constants as c } from '../utils';
import * as nativex from '../../native';
import { translate as t } from 'services/l10n';
const GET_FILE_CHUNK_SIZE = 50;
@ -234,7 +234,7 @@ export async function* deletePhotos(photos: IPhoto[], confirm: boolean = true) {
await nativex.deleteLocalPhotos(photos);
// Remove purely local files
const deleted = photos.filter((p) => p.flag & c.FLAG_IS_LOCAL);
const deleted = photos.filter((p) => p.flag & utils.constants.FLAG_IS_LOCAL);
// Yield for the fully local files
if (deleted.length > 0) {

View File

@ -1,6 +1,7 @@
import axios from '@nextcloud/axios';
import { showError } from '@nextcloud/dialogs';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import { IPhoto } from '../../types';
import { API } from '../API';
import * as nativex from '../../native';

View File

@ -1,11 +1,12 @@
import axios from '@nextcloud/axios';
import { showError } from '@nextcloud/dialogs';
import { translate as t } from '@nextcloud/l10n';
import { generateUrl } from '@nextcloud/router';
import { translate as t } from 'services/l10n';
import { IFace, IPhoto } from '../../types';
import { API } from '../API';
import client from './client';
import { constants as c } from '../utils';
import client from './client';
import * as base from './base';
/**

View File

@ -1,5 +1,6 @@
import { showError } from '@nextcloud/dialogs';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import { IFileInfo, IPhoto } from '../../types';
import client from './client';
import * as base from './base';

View File

@ -1,9 +1,10 @@
import { getLanguage } from '@nextcloud/l10n';
import axios from '@nextcloud/axios';
import { ICluster } from '../../types';
import { API } from '../API';
import client from './client';
import { translate as t, getLanguage } from '@nextcloud/l10n';
import axios from '@nextcloud/axios';
import { translate as t } from 'services/l10n';
export interface ITag {
id: number;

View File

@ -0,0 +1,14 @@
import { translate as t, translatePlural as n } from '@nextcloud/l10n';
// apps for translation
type TranslateApps = 'memories' | 'recognize';
// utility type to drop the first element of a tuple
type DropFirst<T extends unknown[]> = T extends [any, ...infer U] ? U : never;
// Prevent typos in translations
type TranslateType = (app: TranslateApps, ...args: DropFirst<Parameters<typeof t>>) => string;
type TranslatePluralType = (app: TranslateApps, ...args: DropFirst<Parameters<typeof n>>) => string;
export const translate = t as TranslateType;
export const translatePlural = n as TranslatePluralType;

View File

@ -3,7 +3,7 @@ import { showInfo, showError } from '@nextcloud/dialogs';
import { API } from './API';
import { IConfig } from '../types';
import { getBuilder } from '@nextcloud/browser-storage';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
import * as utils from './utils';
import type Storage from '@nextcloud/browser-storage/dist/storage';

View File

@ -1,5 +1,5 @@
import staticConfig from './static-config';
import { translate as t } from '@nextcloud/l10n';
import { translate as t } from 'services/l10n';
export function emptyDescription(routeName: string): string {
switch (routeName) {

View File

@ -1,6 +1,7 @@
import { translate as t, translatePlural as n } from '@nextcloud/l10n';
import { FilePickerType, getFilePickerBuilder } from '@nextcloud/dialogs';
import { showError } from '@nextcloud/dialogs';
import { translate as t, translatePlural as n } from 'services/l10n';
import { bus } from './event-bus';
import { fragment } from './fragment';

View File

@ -5,6 +5,7 @@
"ES2017",
"WebWorker"
],
"baseUrl": "./src",
"target": "ES2017",
"module": "ES2020",
"moduleResolution": "node",
@ -21,7 +22,7 @@
},
"ts-node": {
"compilerOptions": {
"module": "CommonJS",
"module": "CommonJS"
}
},
}
}

View File

@ -125,11 +125,15 @@ module.exports = {
resolve: {
extensions: ['.ts', '.js', '.vue'],
symlinks: false,
// Ensure npm does not duplicate vue dependency, and that npm link works for vue 3
// See https://github.com/vuejs/core/issues/1503
// See https://github.com/nextcloud/nextcloud-vue/issues/3281
alias: {
vue$: path.resolve('./node_modules/vue'),
// Ensure npm does not duplicate vue dependency, and that npm link works for vue 3
// See https://github.com/vuejs/core/issues/1503
// See https://github.com/nextcloud/nextcloud-vue/issues/3281
vue$: path.resolve(__dirname, 'node_modules', 'vue'),
// We can turn on preferRelative instead but this seems safer to be explicit
services: path.resolve(__dirname, 'src', 'services'),
utils$: path.resolve(__dirname, 'src', 'utils'),
},
},
};