Finalize first i18n implementation
This commit is contained in:
parent
927b4b85b5
commit
cfcff69b95
@ -26,6 +26,7 @@ import IconFavs from '@/components/icons/IconFavs';
|
|||||||
import IconEdit from '@/components/icons/IconEdit';
|
import IconEdit from '@/components/icons/IconEdit';
|
||||||
import { photoNeedsToBeSynced } from '@/photo/sync';
|
import { photoNeedsToBeSynced } from '@/photo/sync';
|
||||||
import { KEY_COMMANDS } from '@/photo/key-commands';
|
import { KEY_COMMANDS } from '@/photo/key-commands';
|
||||||
|
import { APP_TEXT } from '@/app/config';
|
||||||
|
|
||||||
export default function AdminPhotoMenu({
|
export default function AdminPhotoMenu({
|
||||||
photo,
|
photo,
|
||||||
@ -48,7 +49,7 @@ export default function AdminPhotoMenu({
|
|||||||
|
|
||||||
const sectionMain = useMemo(() => {
|
const sectionMain = useMemo(() => {
|
||||||
const items: ComponentProps<typeof MoreMenuItem>[] = [{
|
const items: ComponentProps<typeof MoreMenuItem>[] = [{
|
||||||
label: 'Edit',
|
label: APP_TEXT.admin.edit,
|
||||||
icon: <IconEdit
|
icon: <IconEdit
|
||||||
size={15}
|
size={15}
|
||||||
className="translate-x-[0.5px]"
|
className="translate-x-[0.5px]"
|
||||||
@ -58,7 +59,7 @@ export default function AdminPhotoMenu({
|
|||||||
}];
|
}];
|
||||||
if (includeFavorite) {
|
if (includeFavorite) {
|
||||||
items.push({
|
items.push({
|
||||||
label: isFav ? 'Unfavorite' : 'Favorite',
|
label: isFav ? APP_TEXT.admin.unfavorite : APP_TEXT.admin.favorite,
|
||||||
icon: <IconFavs
|
icon: <IconFavs
|
||||||
size={14}
|
size={14}
|
||||||
className="translate-x-[-1px] translate-y-[0.5px]"
|
className="translate-x-[-1px] translate-y-[0.5px]"
|
||||||
@ -76,7 +77,7 @@ export default function AdminPhotoMenu({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
items.push({
|
items.push({
|
||||||
label: 'Download',
|
label: APP_TEXT.admin.download,
|
||||||
icon: <MdOutlineFileDownload
|
icon: <MdOutlineFileDownload
|
||||||
size={17}
|
size={17}
|
||||||
className="translate-x-[-1px]"
|
className="translate-x-[-1px]"
|
||||||
@ -86,9 +87,9 @@ export default function AdminPhotoMenu({
|
|||||||
...showKeyCommands && { keyCommand: KEY_COMMANDS.download },
|
...showKeyCommands && { keyCommand: KEY_COMMANDS.download },
|
||||||
});
|
});
|
||||||
items.push({
|
items.push({
|
||||||
label: 'Sync',
|
label: APP_TEXT.admin.sync,
|
||||||
labelComplex: <span className="inline-flex items-center gap-2">
|
labelComplex: <span className="inline-flex items-center gap-2">
|
||||||
<span>Sync</span>
|
<span>{APP_TEXT.admin.sync}</span>
|
||||||
{photoNeedsToBeSynced(photo) &&
|
{photoNeedsToBeSynced(photo) &&
|
||||||
<InsightsIndicatorDot
|
<InsightsIndicatorDot
|
||||||
colorOverride="blue"
|
colorOverride="blue"
|
||||||
@ -115,7 +116,7 @@ export default function AdminPhotoMenu({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const sectionDelete: ComponentProps<typeof MoreMenuItem>[] = useMemo(() => [{
|
const sectionDelete: ComponentProps<typeof MoreMenuItem>[] = useMemo(() => [{
|
||||||
label: 'Delete',
|
label: APP_TEXT.admin.delete,
|
||||||
icon: <BiTrash
|
icon: <BiTrash
|
||||||
size={15}
|
size={15}
|
||||||
className="translate-x-[-1px]"
|
className="translate-x-[-1px]"
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import {
|
|||||||
absolutePathForCamera,
|
absolutePathForCamera,
|
||||||
absolutePathForCameraImage,
|
absolutePathForCameraImage,
|
||||||
} from '@/app/paths';
|
} from '@/app/paths';
|
||||||
|
import { APP_TEXT } from '@/app/config';
|
||||||
|
|
||||||
// Meta functions moved to separate file to avoid
|
// Meta functions moved to separate file to avoid
|
||||||
// dependencies (camelcase-keys) found in photo/index.ts
|
// dependencies (camelcase-keys) found in photo/index.ts
|
||||||
@ -19,8 +20,9 @@ export const titleForCamera = (
|
|||||||
photos: Photo[],
|
photos: Photo[],
|
||||||
explicitCount?: number,
|
explicitCount?: number,
|
||||||
) => [
|
) => [
|
||||||
'Shot on',
|
APP_TEXT.category.cameraShare(
|
||||||
formatCameraText(cameraFromPhoto(photos[0], camera)),
|
formatCameraText(cameraFromPhoto(photos[0], camera)),
|
||||||
|
),
|
||||||
photoQuantityText(explicitCount ?? photos.length),
|
photoQuantityText(explicitCount ?? photos.length),
|
||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
@ -28,10 +30,9 @@ export const shareTextForCamera = (
|
|||||||
camera: Camera,
|
camera: Camera,
|
||||||
photos: Photo[],
|
photos: Photo[],
|
||||||
) =>
|
) =>
|
||||||
[
|
APP_TEXT.category.cameraShare(
|
||||||
'Photos shot on',
|
|
||||||
formatCameraText(cameraFromPhoto(photos[0], camera)),
|
formatCameraText(cameraFromPhoto(photos[0], camera)),
|
||||||
].join(' ');
|
);
|
||||||
|
|
||||||
export const descriptionForCameraPhotos = (
|
export const descriptionForCameraPhotos = (
|
||||||
photos: Photo[],
|
photos: Photo[],
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import LoaderButton from './primitives/LoaderButton';
|
|||||||
import clsx from 'clsx/lite';
|
import clsx from 'clsx/lite';
|
||||||
import { toastSuccess } from '@/toast';
|
import { toastSuccess } from '@/toast';
|
||||||
import { ComponentProps } from 'react';
|
import { ComponentProps } from 'react';
|
||||||
|
import { APP_TEXT } from '@/app/config';
|
||||||
|
|
||||||
export default function CopyButton({
|
export default function CopyButton({
|
||||||
label,
|
label,
|
||||||
@ -29,7 +30,7 @@ export default function CopyButton({
|
|||||||
onClick={text
|
onClick={text
|
||||||
? () => {
|
? () => {
|
||||||
navigator.clipboard.writeText(text);
|
navigator.clipboard.writeText(text);
|
||||||
toastSuccess(`${label} copied to clipboard`);
|
toastSuccess(APP_TEXT.misc.copyPhrase(label));
|
||||||
}
|
}
|
||||||
: undefined}
|
: undefined}
|
||||||
styleAs="link"
|
styleAs="link"
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { FiUploadCloud } from 'react-icons/fi';
|
|||||||
import { MAX_IMAGE_SIZE } from '@/platforms/next-image';
|
import { MAX_IMAGE_SIZE } from '@/platforms/next-image';
|
||||||
import ProgressButton from './primitives/ProgressButton';
|
import ProgressButton from './primitives/ProgressButton';
|
||||||
import { useAppState } from '@/state/AppState';
|
import { useAppState } from '@/state/AppState';
|
||||||
|
import { APP_TEXT } from '@/app/config';
|
||||||
|
|
||||||
export default function ImageInput({
|
export default function ImageInput({
|
||||||
ref: inputRefExternal,
|
ref: inputRefExternal,
|
||||||
@ -84,9 +85,13 @@ export default function ImageInput({
|
|||||||
>
|
>
|
||||||
{isUploading
|
{isUploading
|
||||||
? filesLength > 1
|
? filesLength > 1
|
||||||
? `Uploading ${fileUploadIndex + 1} of ${filesLength}`
|
? APP_TEXT.utility.paginate(
|
||||||
: 'Uploading'
|
fileUploadIndex + 1,
|
||||||
: 'Upload Photos'}
|
filesLength,
|
||||||
|
APP_TEXT.admin.uploading,
|
||||||
|
)
|
||||||
|
: APP_TEXT.admin.uploading
|
||||||
|
: APP_TEXT.admin.uploadPhotos}
|
||||||
</ProgressButton>}
|
</ProgressButton>}
|
||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import {
|
|||||||
} from '@/utility/string';
|
} from '@/utility/string';
|
||||||
import { AnnotatedTag } from '@/photo/form';
|
import { AnnotatedTag } from '@/photo/form';
|
||||||
import PhotoFilmIcon from './PhotoFilmIcon';
|
import PhotoFilmIcon from './PhotoFilmIcon';
|
||||||
|
import { APP_TEXT } from '@/app/config';
|
||||||
|
|
||||||
export type FilmWithCount = {
|
export type FilmWithCount = {
|
||||||
film: string
|
film: string
|
||||||
@ -67,7 +68,7 @@ export const titleForFilm = (
|
|||||||
export const shareTextForFilm = (
|
export const shareTextForFilm = (
|
||||||
film: string,
|
film: string,
|
||||||
) =>
|
) =>
|
||||||
`Photos shot on ${labelForFilm(film).large}`;
|
APP_TEXT.category.filmShare(labelForFilm(film).large);
|
||||||
|
|
||||||
export const descriptionForFilmPhotos = (
|
export const descriptionForFilmPhotos = (
|
||||||
photos: Photo[],
|
photos: Photo[],
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
absolutePathForFocalLength,
|
absolutePathForFocalLength,
|
||||||
absolutePathForFocalLengthImage,
|
absolutePathForFocalLengthImage,
|
||||||
} from '@/app/paths';
|
} from '@/app/paths';
|
||||||
|
import { APP_TEXT } from '@/app/config';
|
||||||
|
|
||||||
export type FocalLengths = {
|
export type FocalLengths = {
|
||||||
focal: number
|
focal: number
|
||||||
@ -31,12 +32,12 @@ export const titleForFocalLength = (
|
|||||||
photos: Photo[],
|
photos: Photo[],
|
||||||
explicitCount?: number,
|
explicitCount?: number,
|
||||||
) => [
|
) => [
|
||||||
`${formatFocalLength(focal)} Focal Length`,
|
APP_TEXT.category.focalLengthTitle(formatFocalLengthSafe(focal)),
|
||||||
photoQuantityText(explicitCount ?? photos.length),
|
photoQuantityText(explicitCount ?? photos.length),
|
||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
export const shareTextFocalLength = (focal: number) =>
|
export const shareTextFocalLength = (focal: number) =>
|
||||||
`Photos shot at ${formatFocalLength(focal)}`;
|
APP_TEXT.category.focalLengthShare(formatFocalLengthSafe(focal));
|
||||||
|
|
||||||
export const descriptionForFocalLengthPhotos = (
|
export const descriptionForFocalLengthPhotos = (
|
||||||
photos: Photo[],
|
photos: Photo[],
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { I18NDeepPartial } from '..';
|
import { I18NDeepPartial } from '..';
|
||||||
|
import { ptBR } from 'date-fns/locale';
|
||||||
|
|
||||||
const TEXT: I18NDeepPartial = {
|
const TEXT: I18NDeepPartial = {
|
||||||
photo: {
|
photo: {
|
||||||
@ -7,21 +8,30 @@ const TEXT: I18NDeepPartial = {
|
|||||||
taken: 'Capturado',
|
taken: 'Capturado',
|
||||||
created: 'Criado',
|
created: 'Criado',
|
||||||
updated: 'Atualizado',
|
updated: 'Atualizado',
|
||||||
|
copied: 'Link para foto copiado',
|
||||||
},
|
},
|
||||||
category: {
|
category: {
|
||||||
camera: 'Câmera',
|
camera: 'Câmera',
|
||||||
cameraPlural: 'Câmeras',
|
cameraPlural: 'Câmeras',
|
||||||
|
cameraTitle: (camera: string) => `Tirado com ${camera}`,
|
||||||
|
cameraShare: (camera: string) => `Fotos tiradas com ${camera}`,
|
||||||
lens: 'Lente',
|
lens: 'Lente',
|
||||||
lensPlural: 'Lentes',
|
lensPlural: 'Lentes',
|
||||||
tag: 'Tag',
|
tag: 'Tag',
|
||||||
tagPlural: 'Tags',
|
tagPlural: 'Tags',
|
||||||
tagged: 'Marcado',
|
taggedPhotos: 'Fotos Marcadas',
|
||||||
|
taggedPhrase: (tag: string) => `Fotos marcadas com '${tag}'`,
|
||||||
|
taggedFavs: 'Fotos Favoritas',
|
||||||
recipe: 'Receita',
|
recipe: 'Receita',
|
||||||
recipePlural: 'Receitas',
|
recipePlural: 'Receitas',
|
||||||
|
recipeShare: (recipe: string) => `Fotos da receita ${recipe}`,
|
||||||
film: 'Filme',
|
film: 'Filme',
|
||||||
filmPlural: 'Filmes',
|
filmPlural: 'Filmes',
|
||||||
|
filmShare: (film: string) => `Fotos tiradas com ${film}`,
|
||||||
focalLength: 'Distância Focal',
|
focalLength: 'Distância Focal',
|
||||||
focalLengthPlural: 'Distâncias Focais',
|
focalLengthPlural: 'Distâncias Focais',
|
||||||
|
focalLengthTitle: (focal: string) => `Distância Focal ${focal}`,
|
||||||
|
focalLengthShare: (focal: string) => `Fotos tiradas em ${focal}`,
|
||||||
},
|
},
|
||||||
nav: {
|
nav: {
|
||||||
home: 'Início',
|
home: 'Início',
|
||||||
@ -62,6 +72,7 @@ const TEXT: I18NDeepPartial = {
|
|||||||
uploadPhotos: 'Enviar Fotos',
|
uploadPhotos: 'Enviar Fotos',
|
||||||
upload: 'Enviar',
|
upload: 'Enviar',
|
||||||
uploadPlural: 'Envios',
|
uploadPlural: 'Envios',
|
||||||
|
uploading: 'Enviando',
|
||||||
updates: 'Atualizações',
|
updates: 'Atualizações',
|
||||||
managePhotos: 'Gerenciar Fotos',
|
managePhotos: 'Gerenciar Fotos',
|
||||||
manageCameras: 'Gerenciar Câmeras',
|
manageCameras: 'Gerenciar Câmeras',
|
||||||
@ -73,10 +84,29 @@ const TEXT: I18NDeepPartial = {
|
|||||||
batchExitEdit: 'Sair da Edição em Lote',
|
batchExitEdit: 'Sair da Edição em Lote',
|
||||||
appInsights: 'Insights do App',
|
appInsights: 'Insights do App',
|
||||||
appConfig: 'Configuração do App',
|
appConfig: 'Configuração do App',
|
||||||
|
edit: 'Editar',
|
||||||
|
favorite: 'Favoritar',
|
||||||
|
unfavorite: 'Remover dos Favoritos',
|
||||||
|
download: 'Baixar',
|
||||||
|
sync: 'Sincronizar',
|
||||||
|
delete: 'Excluir',
|
||||||
|
deleteConfirm: (photoTitle: string) =>
|
||||||
|
`Tem certeza que deseja excluir "${photoTitle}"?`,
|
||||||
},
|
},
|
||||||
misc: {
|
misc: {
|
||||||
repo: 'Feito com',
|
repo: 'Feito com',
|
||||||
|
copyPhrase: (label: string) => `${label} copiado`,
|
||||||
},
|
},
|
||||||
|
utility: {
|
||||||
|
paginate: (
|
||||||
|
index: number,
|
||||||
|
count: number,
|
||||||
|
action?: string,
|
||||||
|
) => action
|
||||||
|
? `${action} ${index} de ${count}`
|
||||||
|
: `${index} de ${count}`,
|
||||||
|
},
|
||||||
|
dateLocale: ptBR,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TEXT;
|
export default TEXT;
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { enUS } from 'date-fns/locale';
|
||||||
|
|
||||||
const TEXT = {
|
const TEXT = {
|
||||||
photo: {
|
photo: {
|
||||||
photo: 'Photo',
|
photo: 'Photo',
|
||||||
@ -5,21 +7,30 @@ const TEXT = {
|
|||||||
taken: 'Taken',
|
taken: 'Taken',
|
||||||
created: 'Created',
|
created: 'Created',
|
||||||
updated: 'Updated',
|
updated: 'Updated',
|
||||||
|
copied: 'Link to photo copied',
|
||||||
},
|
},
|
||||||
category: {
|
category: {
|
||||||
camera: 'Camera',
|
camera: 'Camera',
|
||||||
cameraPlural: 'Cameras',
|
cameraPlural: 'Cameras',
|
||||||
|
cameraTitle: (camera: string) => `Shot on ${camera}`,
|
||||||
|
cameraShare: (camera: string) => `Photos shot on ${camera}`,
|
||||||
lens: 'Lens',
|
lens: 'Lens',
|
||||||
lensPlural: 'Lenses',
|
lensPlural: 'Lenses',
|
||||||
tag: 'Tag',
|
tag: 'Tag',
|
||||||
tagPlural: 'Tags',
|
tagPlural: 'Tags',
|
||||||
tagged: 'Tagged Photos',
|
taggedPhotos: 'Tagged Photos',
|
||||||
|
taggedPhrase: (tag: string) => `Photos tagged '${tag}'`,
|
||||||
|
taggedFavs: 'Favorite Photos',
|
||||||
recipe: 'Recipe',
|
recipe: 'Recipe',
|
||||||
recipePlural: 'Recipes',
|
recipePlural: 'Recipes',
|
||||||
|
recipeShare: (recipe: string) => `${recipe} recipe photos`,
|
||||||
film: 'Film',
|
film: 'Film',
|
||||||
filmPlural: 'Films',
|
filmPlural: 'Films',
|
||||||
|
filmShare: (film: string) => `Photos shot on ${film}`,
|
||||||
focalLength: 'Focal Length',
|
focalLength: 'Focal Length',
|
||||||
focalLengthPlural: 'Focal Lengths',
|
focalLengthPlural: 'Focal Lengths',
|
||||||
|
focalLengthTitle: (focal: string) => `Focal Length ${focal}`,
|
||||||
|
focalLengthShare: (focal: string) => `Photos shot at ${focal}`,
|
||||||
},
|
},
|
||||||
nav: {
|
nav: {
|
||||||
home: 'Home',
|
home: 'Home',
|
||||||
@ -60,6 +71,7 @@ const TEXT = {
|
|||||||
uploadPhotos: 'Upload Photos',
|
uploadPhotos: 'Upload Photos',
|
||||||
upload: 'Upload',
|
upload: 'Upload',
|
||||||
uploadPlural: 'Uploads',
|
uploadPlural: 'Uploads',
|
||||||
|
uploading: 'Uploading',
|
||||||
updates: 'Updates',
|
updates: 'Updates',
|
||||||
managePhotos: 'Manage Photos',
|
managePhotos: 'Manage Photos',
|
||||||
manageCameras: 'Manage Cameras',
|
manageCameras: 'Manage Cameras',
|
||||||
@ -71,17 +83,29 @@ const TEXT = {
|
|||||||
batchExitEdit: 'Exit Batch Edit',
|
batchExitEdit: 'Exit Batch Edit',
|
||||||
appInsights: 'App Insights',
|
appInsights: 'App Insights',
|
||||||
appConfig: 'App Configuration',
|
appConfig: 'App Configuration',
|
||||||
|
edit: 'Edit',
|
||||||
|
favorite: 'Favorite',
|
||||||
|
unfavorite: 'Unfavorite',
|
||||||
|
download: 'Download',
|
||||||
|
sync: 'Sync',
|
||||||
|
delete: 'Delete',
|
||||||
|
deleteConfirm: (photoTitle: string) =>
|
||||||
|
`Are you sure you want to delete "${photoTitle}?"`,
|
||||||
},
|
},
|
||||||
misc: {
|
misc: {
|
||||||
repo: 'Made with',
|
repo: 'Made with',
|
||||||
|
copyPhrase: (label: string) => `${label} copied`,
|
||||||
},
|
},
|
||||||
paginate: (
|
utility: {
|
||||||
index: number,
|
paginate: (
|
||||||
count: number,
|
index: number,
|
||||||
verb?: string,
|
count: number,
|
||||||
) => verb
|
action?: string,
|
||||||
? `${verb} ${index} of ${count}`
|
) => action
|
||||||
: `${index} of ${count}`,
|
? `${action} ${index} of ${count}`
|
||||||
|
: `${index} of ${count}`,
|
||||||
|
},
|
||||||
|
dateLocale: enUS,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TEXT;
|
export default TEXT;
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import {
|
|||||||
absolutePathForLens,
|
absolutePathForLens,
|
||||||
absolutePathForLensImage,
|
absolutePathForLensImage,
|
||||||
} from '@/app/paths';
|
} from '@/app/paths';
|
||||||
|
import { APP_TEXT } from '@/app/config';
|
||||||
|
|
||||||
// Meta functions moved to separate file to avoid
|
// Meta functions moved to separate file to avoid
|
||||||
// dependencies (camelcase-keys) found in photo/index.ts
|
// dependencies (camelcase-keys) found in photo/index.ts
|
||||||
@ -19,7 +20,7 @@ export const titleForLens = (
|
|||||||
photos: Photo[],
|
photos: Photo[],
|
||||||
explicitCount?: number,
|
explicitCount?: number,
|
||||||
) => [
|
) => [
|
||||||
'Lens:',
|
`${APP_TEXT.category.lens}:`,
|
||||||
formatLensText(lensFromPhoto(photos[0], lens)),
|
formatLensText(lensFromPhoto(photos[0], lens)),
|
||||||
photoQuantityText(explicitCount ?? photos.length),
|
photoQuantityText(explicitCount ?? photos.length),
|
||||||
].join(' ');
|
].join(' ');
|
||||||
@ -29,7 +30,7 @@ export const shareTextForLens = (
|
|||||||
photos: Photo[],
|
photos: Photo[],
|
||||||
) =>
|
) =>
|
||||||
[
|
[
|
||||||
'Lens:',
|
`${APP_TEXT.category.lens}:`,
|
||||||
formatLensText(lensFromPhoto(photos[0], lens)),
|
formatLensText(lensFromPhoto(photos[0], lens)),
|
||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
|
|||||||
@ -155,9 +155,13 @@ export default function PhotoHeader({
|
|||||||
}} />}
|
}} />}
|
||||||
</>
|
</>
|
||||||
: <ResponsiveText
|
: <ResponsiveText
|
||||||
shortText={APP_TEXT.paginate(paginationIndex, paginationCount)}
|
shortText={APP_TEXT.utility.paginate(
|
||||||
|
paginationIndex,
|
||||||
|
paginationCount,
|
||||||
|
entityVerb,
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{APP_TEXT.paginate(
|
{APP_TEXT.utility.paginate(
|
||||||
paginationIndex,
|
paginationIndex,
|
||||||
paginationCount,
|
paginationCount,
|
||||||
entityVerb)}
|
entityVerb)}
|
||||||
|
|||||||
@ -172,7 +172,7 @@ export const photoStatsAsString = (photo: Photo) => [
|
|||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
export const descriptionForPhoto = (photo: Photo) =>
|
export const descriptionForPhoto = (photo: Photo) =>
|
||||||
photo.takenAtNaiveFormatted?.toUpperCase();
|
formatDate({ date: photo.takenAt }).toLocaleUpperCase();
|
||||||
|
|
||||||
export const getPreviousPhoto = (photo: Photo, photos: Photo[]) => {
|
export const getPreviousPhoto = (photo: Photo, photos: Photo[]) => {
|
||||||
const index = photos.findIndex(p => p.id === photo.id);
|
const index = photos.findIndex(p => p.id === photo.id);
|
||||||
@ -251,7 +251,7 @@ export const photoQuantityText = (
|
|||||||
: `${count} ${photoLabelForCount(count, capitalize)}`;
|
: `${count} ${photoLabelForCount(count, capitalize)}`;
|
||||||
|
|
||||||
export const deleteConfirmationTextForPhoto = (photo: Photo) =>
|
export const deleteConfirmationTextForPhoto = (photo: Photo) =>
|
||||||
`Are you sure you want to delete "${titleForPhoto(photo)}?"`;
|
APP_TEXT.admin.deleteConfirm(titleForPhoto(photo));
|
||||||
|
|
||||||
export type PhotoDateRange = { start: string, end: string };
|
export type PhotoDateRange = { start: string, end: string };
|
||||||
|
|
||||||
@ -265,9 +265,10 @@ export const descriptionForPhotoSet = (
|
|||||||
dateBased
|
dateBased
|
||||||
? dateRangeForPhotos(photos, explicitDateRange).description.toUpperCase()
|
? dateRangeForPhotos(photos, explicitDateRange).description.toUpperCase()
|
||||||
: [
|
: [
|
||||||
explicitCount ?? photos.length,
|
explicitCount ?? photos.length, (
|
||||||
descriptor,
|
descriptor ||
|
||||||
photoLabelForCount(explicitCount ?? photos.length, false),
|
photoLabelForCount(explicitCount ?? photos.length, false)
|
||||||
|
),
|
||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
const sortPhotosByDateNonDestructively = (
|
const sortPhotosByDateNonDestructively = (
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import { TbChecklist } from 'react-icons/tb';
|
|||||||
import CopyButton from '@/components/CopyButton';
|
import CopyButton from '@/components/CopyButton';
|
||||||
import { labelForFilm } from '@/film';
|
import { labelForFilm } from '@/film';
|
||||||
import PhotoRecipe from './PhotoRecipe';
|
import PhotoRecipe from './PhotoRecipe';
|
||||||
|
import { APP_TEXT } from '@/app/config';
|
||||||
|
|
||||||
export default function PhotoRecipeOverlay({
|
export default function PhotoRecipeOverlay({
|
||||||
ref,
|
ref,
|
||||||
@ -138,7 +139,7 @@ export default function PhotoRecipeOverlay({
|
|||||||
'text-black/40 active:text-black/75',
|
'text-black/40 active:text-black/75',
|
||||||
'hover:text-black/40',
|
'hover:text-black/40',
|
||||||
)}
|
)}
|
||||||
tooltip="Copy recipe text"
|
tooltip={APP_TEXT.tooltip.recipeCopy}
|
||||||
tooltipColor="frosted"
|
tooltipColor="frosted"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
} from '@/utility/string';
|
} from '@/utility/string';
|
||||||
import { FujifilmRecipe } from '@/platforms/fujifilm/recipe';
|
import { FujifilmRecipe } from '@/platforms/fujifilm/recipe';
|
||||||
import { labelForFilm } from '@/film';
|
import { labelForFilm } from '@/film';
|
||||||
|
import { APP_TEXT } from '@/app/config';
|
||||||
|
|
||||||
export type RecipeWithCount = {
|
export type RecipeWithCount = {
|
||||||
recipe: string
|
recipe: string
|
||||||
@ -32,12 +33,12 @@ export const titleForRecipe = (
|
|||||||
photos:Photo[] = [],
|
photos:Photo[] = [],
|
||||||
explicitCount?: number,
|
explicitCount?: number,
|
||||||
) => [
|
) => [
|
||||||
`Recipe: ${formatRecipe(recipe)}`,
|
`${APP_TEXT.category.recipe}: ${formatRecipe(recipe)}`,
|
||||||
photoQuantityText(explicitCount ?? photos.length),
|
photoQuantityText(explicitCount ?? photos.length),
|
||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
export const shareTextForRecipe = (recipe: string) =>
|
export const shareTextForRecipe = (recipe: string) =>
|
||||||
`${formatRecipe(recipe)} recipe photos`;
|
APP_TEXT.category.recipeShare(formatRecipe(recipe));
|
||||||
|
|
||||||
export const descriptionForRecipePhotos = (
|
export const descriptionForRecipePhotos = (
|
||||||
photos: Photo[] = [],
|
photos: Photo[] = [],
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { ReactNode, useEffect } from 'react';
|
|||||||
import { shortenUrl } from '@/utility/url';
|
import { shortenUrl } from '@/utility/url';
|
||||||
import { toastSuccess } from '@/toast';
|
import { toastSuccess } from '@/toast';
|
||||||
import { PiXLogo } from 'react-icons/pi';
|
import { PiXLogo } from 'react-icons/pi';
|
||||||
import { SHOW_SOCIAL } from '@/app/config';
|
import { APP_TEXT, SHOW_SOCIAL } from '@/app/config';
|
||||||
import { generateXPostText } from '@/utility/social';
|
import { generateXPostText } from '@/utility/social';
|
||||||
import { useAppState } from '@/state/AppState';
|
import { useAppState } from '@/state/AppState';
|
||||||
import useOnPathChange from '@/utility/useOnPathChange';
|
import useOnPathChange from '@/utility/useOnPathChange';
|
||||||
@ -96,7 +96,7 @@ export default function ShareModal({
|
|||||||
<BiCopy size={18} />,
|
<BiCopy size={18} />,
|
||||||
() => {
|
() => {
|
||||||
navigator.clipboard.writeText(pathShare);
|
navigator.clipboard.writeText(pathShare);
|
||||||
toastSuccess('Link to photo copied');
|
toastSuccess(APP_TEXT.photo.copied);
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export default function TagHeader({
|
|||||||
entity={isTagFavs(tag)
|
entity={isTagFavs(tag)
|
||||||
? <FavsTag contrast="high" />
|
? <FavsTag contrast="high" />
|
||||||
: <PhotoTag tag={tag} contrast="high" />}
|
: <PhotoTag tag={tag} contrast="high" />}
|
||||||
entityVerb={APP_TEXT.category.tagged}
|
entityVerb={APP_TEXT.category.taggedPhotos}
|
||||||
entityDescription={descriptionForTaggedPhotos(photos, undefined, count)}
|
entityDescription={descriptionForTaggedPhotos(photos, undefined, count)}
|
||||||
photos={photos}
|
photos={photos}
|
||||||
selectedPhoto={selectedPhoto}
|
selectedPhoto={selectedPhoto}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import {
|
|||||||
formatCountDescriptive,
|
formatCountDescriptive,
|
||||||
} from '@/utility/string';
|
} from '@/utility/string';
|
||||||
import { sortCategoryByCount } from '@/category';
|
import { sortCategoryByCount } from '@/category';
|
||||||
|
import { APP_TEXT } from '@/app/config';
|
||||||
|
|
||||||
// Reserved tags
|
// Reserved tags
|
||||||
export const TAG_FAVS = 'favs';
|
export const TAG_FAVS = 'favs';
|
||||||
@ -48,7 +49,9 @@ export const titleForTag = (
|
|||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
export const shareTextForTag = (tag: string) =>
|
export const shareTextForTag = (tag: string) =>
|
||||||
isTagFavs(tag) ? 'Favorite photos' : `Photos tagged '${formatTag(tag)}'`;
|
isTagFavs(tag)
|
||||||
|
? APP_TEXT.category.taggedFavs
|
||||||
|
: APP_TEXT.category.taggedPhrase(formatTag(tag));
|
||||||
|
|
||||||
export const sortTagsArray = (
|
export const sortTagsArray = (
|
||||||
tags: string[],
|
tags: string[],
|
||||||
@ -95,7 +98,7 @@ export const descriptionForTaggedPhotos = (
|
|||||||
) =>
|
) =>
|
||||||
descriptionForPhotoSet(
|
descriptionForPhotoSet(
|
||||||
photos,
|
photos,
|
||||||
'tagged',
|
APP_TEXT.category.taggedPhotos,
|
||||||
dateBased,
|
dateBased,
|
||||||
explicitCount,
|
explicitCount,
|
||||||
explicitDateRange,
|
explicitDateRange,
|
||||||
@ -139,5 +142,6 @@ export const convertTagsForForm = (tags: Tags = []) =>
|
|||||||
.map(({ tag, count }) => ({
|
.map(({ tag, count }) => ({
|
||||||
value: tag,
|
value: tag,
|
||||||
annotation: formatCount(count),
|
annotation: formatCount(count),
|
||||||
annotationAria: formatCountDescriptive(count, 'tagged'),
|
annotationAria:
|
||||||
|
formatCountDescriptive(count, APP_TEXT.category.taggedPhotos),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { parseISO, parse, format } from 'date-fns';
|
import { parseISO, parse, format } from 'date-fns';
|
||||||
import { formatInTimeZone } from 'date-fns-tz';
|
import { formatInTimeZone } from 'date-fns-tz';
|
||||||
import { Timezone } from './timezone';
|
import { Timezone } from './timezone';
|
||||||
|
import { APP_TEXT } from '@/app/config';
|
||||||
|
|
||||||
const DATE_STRING_FORMAT_TINY = 'dd MMM yy';
|
const DATE_STRING_FORMAT_TINY = 'dd MMM yy';
|
||||||
const DATE_STRING_FORMAT_TINY_PLACEHOLDER = '00 000 00';
|
const DATE_STRING_FORMAT_TINY_PLACEHOLDER = '00 000 00';
|
||||||
@ -65,8 +66,10 @@ export const formatDate = ({
|
|||||||
return showPlaceholder
|
return showPlaceholder
|
||||||
? placeholderString
|
? placeholderString
|
||||||
: timezone
|
: timezone
|
||||||
? formatInTimeZone(date, timezone, formatString)
|
? formatInTimeZone(
|
||||||
: format(date, formatString);
|
date, timezone, formatString, { locale: APP_TEXT.dateLocale },
|
||||||
|
)
|
||||||
|
: format(date, formatString, { locale: APP_TEXT.dateLocale });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const formatDateFromPostgresString = (date: string, length?: Length) =>
|
export const formatDateFromPostgresString = (date: string, length?: Length) =>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user