Incorporate i18n into UI elements (#1471)

* Update zh-tw string table (till 975343d2)
* Prepare localization table
* Implement i18n for Performers & Tags
* Add "add" action strings
* Use Lodash merge for deep merging language JSONs

The original implementation does not properly merge language files, causing unexpected localization string fallback behavior.

* Localize pagination strings
* Use Field name value as null id fallback

...otherwise FormattedMessage is gonna throw when the ID is null

* Use localized "Path" string for all instances
* Localize the "Interface" tab under settings
* Localize scene & performer cards
* Rename locale folder for better compatibility with i18n-ally
* Localize majority of the categories and features
This commit is contained in:
Still Hsu
2021-06-14 14:48:59 +09:00
committed by GitHub
parent 46bbede9a0
commit 3ae187e6f0
105 changed files with 3441 additions and 1084 deletions

View File

@@ -20,6 +20,7 @@ import {
} from "src/core/StashService";
import { useToast } from "src/hooks";
import { DurationUtils } from "src/utils";
import { useIntl } from "react-intl";
function renderScrapedStudio(
result: ScrapeResult<string>,
@@ -44,6 +45,7 @@ function renderScrapedStudio(
}
function renderScrapedStudioRow(
title: string,
result: ScrapeResult<string>,
onChange: (value: ScrapeResult<string>) => void,
newStudio?: GQL.ScrapedSceneStudio,
@@ -51,7 +53,7 @@ function renderScrapedStudioRow(
) {
return (
<ScrapeDialogRow
title="Studio"
title={title}
result={result}
renderOriginalField={() => renderScrapedStudio(result)}
renderNewField={() =>
@@ -90,6 +92,7 @@ function renderScrapedPerformers(
}
function renderScrapedPerformersRow(
title: string,
result: ScrapeResult<string[]>,
onChange: (value: ScrapeResult<string[]>) => void,
newPerformers: GQL.ScrapedScenePerformer[],
@@ -97,7 +100,7 @@ function renderScrapedPerformersRow(
) {
return (
<ScrapeDialogRow
title="Performers"
title={title}
result={result}
renderOriginalField={() => renderScrapedPerformers(result)}
renderNewField={() =>
@@ -136,6 +139,7 @@ function renderScrapedMovies(
}
function renderScrapedMoviesRow(
title: string,
result: ScrapeResult<string[]>,
onChange: (value: ScrapeResult<string[]>) => void,
newMovies: GQL.ScrapedSceneMovie[],
@@ -143,7 +147,7 @@ function renderScrapedMoviesRow(
) {
return (
<ScrapeDialogRow
title="Movies"
title={title}
result={result}
renderOriginalField={() => renderScrapedMovies(result)}
renderNewField={() =>
@@ -182,6 +186,7 @@ function renderScrapedTags(
}
function renderScrapedTagsRow(
title: string,
result: ScrapeResult<string[]>,
onChange: (value: ScrapeResult<string[]>) => void,
newTags: GQL.ScrapedSceneTag[],
@@ -189,7 +194,7 @@ function renderScrapedTagsRow(
) {
return (
<ScrapeDialogRow
title="Tags"
title={title}
result={result}
renderOriginalField={() => renderScrapedTags(result)}
renderNewField={() =>
@@ -321,6 +326,7 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = (
const [createMovie] = useMovieCreate();
const [createTag] = useTagCreate();
const intl = useIntl();
const Toast = useToast();
// don't show the dialog if nothing was scraped
@@ -520,52 +526,56 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = (
return (
<>
<ScrapedInputGroupRow
title="Title"
title={intl.formatMessage({ id: "title" })}
result={title}
onChange={(value) => setTitle(value)}
/>
<ScrapedInputGroupRow
title="URL"
title={intl.formatMessage({ id: "url" })}
result={url}
onChange={(value) => setURL(value)}
/>
<ScrapedInputGroupRow
title="Date"
title={intl.formatMessage({ id: "date" })}
placeholder="YYYY-MM-DD"
result={date}
onChange={(value) => setDate(value)}
/>
{renderScrapedStudioRow(
intl.formatMessage({ id: "studios" }),
studio,
(value) => setStudio(value),
newStudio,
createNewStudio
)}
{renderScrapedPerformersRow(
intl.formatMessage({ id: "performers" }),
performers,
(value) => setPerformers(value),
newPerformers,
createNewPerformer
)}
{renderScrapedMoviesRow(
intl.formatMessage({ id: "movies" }),
movies,
(value) => setMovies(value),
newMovies,
createNewMovie
)}
{renderScrapedTagsRow(
intl.formatMessage({ id: "tags" }),
tags,
(value) => setTags(value),
newTags,
createNewTag
)}
<ScrapedTextAreaRow
title="Details"
title={intl.formatMessage({ id: "details" })}
result={details}
onChange={(value) => setDetails(value)}
/>
<ScrapedImageRow
title="Cover Image"
title={intl.formatMessage({ id: "cover_image" })}
className="scene-cover"
result={image}
onChange={(value) => setImage(value)}
@@ -576,7 +586,7 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = (
return (
<ScrapeDialog
title="Scene Scrape Results"
title={intl.formatMessage({ id: "dialogs.scrape_entity_title" })}
renderScrapeRows={renderScrapeRows}
onClose={(apply) => {
props.onClose(apply ? makeNewScrapedItem() : undefined);