Skip to content

Commit

Permalink
Localize dates and numbers (#574)
Browse files Browse the repository at this point in the history
  • Loading branch information
InfiniteTF authored May 25, 2020
1 parent ccd7573 commit 197918d
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 23 deletions.
3 changes: 3 additions & 0 deletions ui/v2.5/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"react/prop-types": "off",
"react/destructuring-assignment": "off",
"react/jsx-props-no-spreading": "off",
"react/style-prop-object": ["error", {
"allow": ["FormattedNumber"]
}],
"spaced-comment": ["error", "always", {
"markers": ["/"]
}],
Expand Down
8 changes: 7 additions & 1 deletion ui/v2.5/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ import Movies from "./components/Movies/Movies";
// Set fontawesome/free-solid-svg as default fontawesome icons
library.add(fas);

const intlFormats = {
date: {
long: { year: "numeric", month: "long", day: "numeric" },
},
};

export const App: React.FC = () => {
const config = useConfiguration();
const language = config.data?.configuration?.interface?.language ?? "en-US";
Expand All @@ -33,7 +39,7 @@ export const App: React.FC = () => {

return (
<ErrorBoundary>
<IntlProvider locale={language} messages={messages}>
<IntlProvider locale={language} messages={messages} formats={intlFormats}>
<ToastProvider>
<MainNavbar />
<div className="main container-fluid">
Expand Down
4 changes: 2 additions & 2 deletions ui/v2.5/src/components/List/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import { Button, ButtonGroup } from "react-bootstrap";
import { useIntl } from "react-intl";
import { FormattedNumber, useIntl } from "react-intl";

interface IPaginationProps {
itemsPerPage: number;
Expand Down Expand Up @@ -62,7 +62,7 @@ export const Pagination: React.FC<IPaginationProps> = ({
active={currentPage === page}
onClick={() => onChangePage(page)}
>
{page}
<FormattedNumber value={page} />
</Button>
));

Expand Down
12 changes: 11 additions & 1 deletion ui/v2.5/src/components/Movies/MovieCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Card } from "react-bootstrap";
import React, { FunctionComponent } from "react";
import { FormattedPlural } from "react-intl";
import { Link } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";

Expand All @@ -26,7 +27,16 @@ export const MovieCard: FunctionComponent<IProps> = (props: IProps) => {

function maybeRenderSceneNumber() {
if (!props.sceneIndex) {
return <span>{props.movie.scene_count} scenes.</span>;
return (
<span>
{props.movie.scene_count}&nbsp;
<FormattedPlural
value={props.movie.scene_count ?? 0}
one="scene"
other="scenes"
/>
</span>
);
}

return <span>Scene number: {props.sceneIndex}</span>;
Expand Down
9 changes: 7 additions & 2 deletions ui/v2.5/src/components/Movies/MovieDetails/Movie.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable react/no-this-in-sfc */
import React, { useEffect, useState, useCallback } from "react";
import { useIntl } from "react-intl";
import * as GQL from "src/core/generated-graphql";
import {
useFindMovie,
Expand Down Expand Up @@ -65,6 +66,8 @@ export const Movie: React.FC = () => {
getMovieInput() as GQL.MovieDestroyInput
);

const intl = useIntl();

function updateMovieEditState(state: Partial<GQL.MovieDataFragment>) {
setName(state.name ?? undefined);
setAliases(state.aliases ?? undefined);
Expand Down Expand Up @@ -238,8 +241,10 @@ export const Movie: React.FC = () => {
setDuration(value ? Number.parseInt(value, 10) : undefined),
})}
{TableUtils.renderInputGroup({
title: "Date (YYYY-MM-DD)",
value: date,
title: `Date ${isEditing ? "(YYYY-MM-DD)" : ""}`,
value: isEditing
? date
: intl.formatDate(date, { format: "long" }),
isEditing,
onChange: setDate,
})}
Expand Down
14 changes: 12 additions & 2 deletions ui/v2.5/src/components/Performers/PerformerCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from "react";
import { Card } from "react-bootstrap";
import { Link } from "react-router-dom";
import { FormattedNumber, FormattedPlural } from "react-intl";
import * as GQL from "src/core/generated-graphql";
import { NavUtils, TextUtils } from "src/utils";
import { CountryFlag } from "src/components/Shared";
Expand Down Expand Up @@ -39,8 +40,17 @@ export const PerformerCard: React.FC<IPerformerCardProps> = ({
{age !== 0 ? <div className="text-muted">{ageString}</div> : ""}
<CountryFlag country={performer.country} />
<div className="text-muted">
Stars in {performer.scene_count}{" "}
<Link to={NavUtils.makePerformerScenesUrl(performer)}>scenes</Link>.
Stars in&nbsp;
<FormattedNumber value={performer.scene_count ?? 0} />
&nbsp;
<Link to={NavUtils.makePerformerScenesUrl(performer)}>
<FormattedPlural
value={performer.scene_count ?? 0}
one="scene"
other="scenes"
/>
</Link>
.
</div>
</div>
</Card>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable react/no-this-in-sfc */

import React, { useEffect, useState } from "react";
import { useIntl } from "react-intl";
import { Button, Popover, OverlayTrigger, Table } from "react-bootstrap";
import * as GQL from "src/core/generated-graphql";
import {
Expand Down Expand Up @@ -83,6 +84,8 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
// Network state
const [isLoading, setIsLoading] = useState(false);

const intl = useIntl();

const Scrapers = useListPerformerScrapers();
const [queryableScrapers, setQueryableScrapers] = useState<GQL.Scraper[]>([]);

Expand Down Expand Up @@ -459,6 +462,20 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
});
}

const formatHeight = () => {
if (isEditing) {
return height;
}
if (!height) {
return "";
}
return intl.formatNumber(Number.parseInt(height, 10), {
style: "unit",
unit: "centimeter",
unitDisplay: "narrow",
});
};

return (
<>
{renderDeleteAlert()}
Expand All @@ -471,7 +488,9 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
{renderGender()}
{TableUtils.renderInputGroup({
title: "Birthdate",
value: birthdate,
value: isEditing
? birthdate
: intl.formatDate(birthdate, { format: "long" }),
isEditing: !!isEditing,
onChange: setBirthdate,
})}
Expand All @@ -489,8 +508,8 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
onChange: setCountry,
})}
{TableUtils.renderInputGroup({
title: "Height (cm)",
value: height,
title: `Height ${isEditing ? "(cm)" : ""}`,
value: formatHeight(),
isEditing: !!isEditing,
onChange: setHeight,
})}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import { Link } from "react-router-dom";
import { FormattedDate } from "react-intl";
import * as GQL from "src/core/generated-graphql";
import { TextUtils } from "src/utils";
import { TagLink } from "src/components/Shared";
Expand Down Expand Up @@ -38,7 +39,9 @@ export const SceneDetailPanel: React.FC<ISceneDetailProps> = (props) => {
{props.scene.title ?? TextUtils.fileNameFromPath(props.scene.path)}
</h3>
<div className="col-6 scene-details">
<h4>{props.scene.date ?? ""}</h4>
<h4>
<FormattedDate value={props.scene.date ?? ""} format="long" />
</h4>
{props.scene.rating ? <h6>Rating: {props.scene.rating}</h6> : ""}
{props.scene.file.height && (
<h6>Resolution: {TextUtils.resolution(props.scene.file.height)}</h6>
Expand Down
25 changes: 22 additions & 3 deletions ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from "react";
import { FormattedNumber } from "react-intl";
import * as GQL from "src/core/generated-graphql";
import { TextUtils } from "src/utils";

Expand Down Expand Up @@ -49,11 +50,23 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
if (props.scene.file.size === undefined) {
return;
}

const { size, unit } = TextUtils.fileSize(
Number.parseInt(props.scene.file.size ?? "0", 10)
);

return (
<div className="row">
<span className="col-4">File Size</span>
<span className="col-8 text-truncate">
{TextUtils.fileSize(parseInt(props.scene.file.size ?? "0", 10))}
<FormattedNumber
value={size}
// eslint-disable-next-line react/style-prop-object
style="unit"
unit={unit}
unitDisplay="narrow"
maximumFractionDigits={2}
/>
</span>
</div>
);
Expand Down Expand Up @@ -95,21 +108,27 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
<div className="row">
<span className="col-4">Frame Rate</span>
<span className="col-8 text-truncate">
{props.scene.file.framerate} frames per second
<FormattedNumber value={props.scene.file.framerate ?? 0} /> frames per
second
</span>
</div>
);
}

function renderbitrate() {
// TODO: An upcoming react-intl version will support compound units, megabits-per-second
if (props.scene.file.bitrate === undefined) {
return;
}
return (
<div className="row">
<span className="col-4">Bit Rate</span>
<span className="col-8 text-truncate">
{TextUtils.bitRate(props.scene.file.bitrate ?? 0)}
<FormattedNumber
value={(props.scene.file.bitrate ?? 0) / 1000000}
maximumFractionDigits={2}
/>
&nbsp;megabits per second
</span>
</div>
);
Expand Down
11 changes: 10 additions & 1 deletion ui/v2.5/src/components/Studios/StudioCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Card } from "react-bootstrap";
import React from "react";
import { Link } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";
import { FormattedPlural } from "react-intl";

interface IProps {
studio: GQL.StudioDataFragment;
Expand All @@ -19,7 +20,15 @@ export const StudioCard: React.FC<IProps> = ({ studio }) => {
</Link>
<div className="card-section">
<h5 className="text-truncate">{studio.name}</h5>
<span>{studio.scene_count} scenes.</span>
<span>
{studio.scene_count}&nbsp;
<FormattedPlural
value={studio.scene_count ?? 0}
one="scene"
other="scenes"
/>
.
</span>
</div>
</Card>
);
Expand Down
10 changes: 7 additions & 3 deletions ui/v2.5/src/components/Tags/TagList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState } from "react";
import { Button, Form } from "react-bootstrap";
import { Link } from "react-router-dom";
import { FormattedNumber } from "react-intl";
import * as GQL from "src/core/generated-graphql";
import {
mutateMetadataAutoTag,
Expand Down Expand Up @@ -115,19 +116,22 @@ export const TagList: React.FC = () => {
to={NavUtils.makeTagScenesUrl(tag)}
className="tag-list-anchor"
>
Scenes: {tag.scene_count}
Scenes: <FormattedNumber value={tag.scene_count ?? 0} />
</Link>
</Button>
<Button variant="secondary" className="tag-list-button">
<Link
to={NavUtils.makeTagSceneMarkersUrl(tag)}
className="tag-list-anchor"
>
Markers: {tag.scene_marker_count}
Markers: <FormattedNumber value={tag.scene_marker_count ?? 0} />
</Link>
</Button>
<span className="tag-list-count">
Total: {(tag.scene_count || 0) + (tag.scene_marker_count || 0)}
Total:{" "}
<FormattedNumber
value={(tag.scene_count || 0) + (tag.scene_marker_count || 0)}
/>
</span>
<Button variant="danger" onClick={() => setDeletingTag(tag)}>
<Icon icon="trash-alt" color="danger" />
Expand Down
26 changes: 22 additions & 4 deletions ui/v2.5/src/utils/text.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
const Units = ["bytes", "kB", "MB", "GB", "TB", "PB"];
// Typescript currently does not implement the intl Unit interface
type Unit =
| "byte"
| "kilobyte"
| "megabyte"
| "gigabyte"
| "terabyte"
| "petabyte";
const Units: Unit[] = [
"byte",
"kilobyte",
"megabyte",
"gigabyte",
"terabyte",
"petabyte",
];

const truncate = (
value?: string,
Expand All @@ -9,9 +24,9 @@ const truncate = (
return value.length > limit ? value.substring(0, limit) + tail : value;
};

const fileSize = (bytes: number = 0, precision: number = 2) => {
const fileSize = (bytes: number = 0) => {
if (Number.isNaN(parseFloat(String(bytes))) || !Number.isFinite(bytes))
return "?";
return { size: 0, unit: Units[0] };

let unit = 0;
let count = bytes;
Expand All @@ -20,7 +35,10 @@ const fileSize = (bytes: number = 0, precision: number = 2) => {
unit++;
}

return `${count.toFixed(+precision)} ${Units[unit]}`;
return {
size: count,
unit: Units[unit],
};
};

const secondsToTimestamp = (seconds: number) => {
Expand Down

0 comments on commit 197918d

Please sign in to comment.