feat: add i18n with LingUI, Crowdin integration, and typed error codes (#16)

* feat: add i18n with LingUI, Crowdin integration, and typed error codes

- Add `@sofa/i18n` shared package with LingUI v5 (v6-ready config using
  `defineConfig` + `@lingui/format-po`), eager English + lazy-loaded
  fr/de/es/it/pt catalogs, `Intl`-based date/number format utilities,
  and test helpers
- Wire `@lingui/vite-plugin` + babel macro plugin for web, and
  `@lingui/metro-transformer` + babel config for native
- Add `I18nProvider` to both app roots with locale auto-detection
  (navigator.language / expo-localization) and persistence
  (localStorage / MMKV)
- Wrap all ~512 user-facing strings across web and native with LingUI
  macros (`<Trans>`, `useLingui`/`t`, `i18n._(msg`...`)`, `plural`)
- Add language switcher to Settings in both apps
- Add `@sofa/api/errors` with 13 typed `AppErrorCode` values and
  `appErrorData()` helper for contract `.errors()` schemas
- Update all oRPC contract error definitions with typed `data` fields;
  convert import procedure `throw new Error()` to `ORPCError` with codes
- Add per-app `error-messages.ts` utilities that map error codes to
  localized strings; update global `QueryCache.onError` handlers to stop
  leaking raw `error.message` to users
- Add `crowdin.yml` config and `.github/workflows/crowdin.yml` for
  automated source upload on merge and translation pull via dispatch
- Add `lingui.config.ts` at repo root with `bun run i18n:extract` and
  `bun run i18n:compile` convenience scripts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: add Crowdin language mapping and ignore context file

* fix(i18n): address 20 localization quality issues

- Stop leaking raw error.message to users in change-password and
  account-section; use localized fallbacks instead
- Add typed error data to discover contract's .errors() schema
- Replace all manual English plural suffixes with LingUI plural() macro
  in episode counts, backup counts, star ratings, title/image counts
- Replace date-fns formatDistanceToNow and hardcoded date patterns with
  locale-aware formatDate/formatRelativeTime from @sofa/i18n/format
- Convert integration-configs from module-scope translations (frozen at
  import time) to lazy getIntegrationConfigs(i18n) function
- Combine split Trans fragments into single translatable units in
  backup-schedule retention sentence and stats-display period labels
- Localize TV media type badge and full episode accessibilityLabel
- Replace Android-incompatible Alert.alert language picker (max 3
  buttons) with zeego DropdownMenu
- Await async locale initialization before hiding splash screen so
  non-English users don't see English flash on cold start
- Add @lingui/core as direct native app dependency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(i18n): add Intl polyfills for Hermes, replace DropdownMenu language picker, and remove date-fns
- Add `@formatjs/intl-*` polyfills loaded at app startup in `intl-polyfills.ts` so Hermes on Android has full `Intl` support for locale-aware formatting
- Alias `@formatjs/icu-messageformat-parser` to its `no-parser` variant in metro config to reduce bundle size
- Replace the zeego `DropdownMenu` language picker in Settings with a new `SelectModal` component that works reliably on all platforms without the Android 3-button limit
- Replace `date-fns` `format`/`parseISO` in `person/[id].tsx` with `formatDate` from `@sofa/i18n/format`; remove `date-fns` from native and web `package.json`
- Refactor `StatusActionButton` to use a `StatusLabel` component with `<Trans>` rather than a `getStatusConfig(t)` factory so labels are always reactive to locale changes
- Fix crowdin workflow to use root `bun run i18n:compile` script instead of `cd packages/i18n && bun run compile`
- Wrap root layout `GestureHandlerRootView` in `SafeAreaProvider` and add `.catch` to locale-ready promise to prevent unhandled rejections blocking the splash screen

* fix(i18n): localize integration status helpers, fix stats-display Trans wrapping, and clean up SelectModal
- Localize `webhookStatus` and `listStatus` in `integration-card.tsx` using `i18n._(msg`...`)` so status strings are translated instead of always rendering in English
- Wrap "Movies {select}" and "Episodes {select}" in a single `<Trans>` in `stats-display.tsx` so the period selector element is embedded inside the translatable unit rather than concatenated outside it
- Fix locale activation order in Settings: close the modal first, then `activateLocale`, and only call `setPersistedLocale` on success to avoid persisting a locale that failed to load
- Remove the redundant `SafeAreaProvider`/`SafeAreaView` wrapper from `SelectModal` — the root layout already provides `SafeAreaProvider`
- Recompile all six `.po`/`.ts` catalogs to pick up new and updated message strings

* chore(i18n): crowdin sync

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
jakeandClaude Opus 4.6 authored and GitHub committed 2026-03-18 12:16:10 -04:00
1 parent e50ab44783
commit ad0497ccd6
142 files changed
+18002 -1243

No files matched your search

+28
View File
@@ -0,0 +1,28 @@
{
"name": "@sofa/i18n",
"version": "0.1.0",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts",
"./locales": "./src/locales.ts",
"./format": "./src/format.ts",
"./test-utils": "./src/test-utils.tsx"
},
"scripts": {
"lint": "biome check",
"format": "biome format --write",
"check-types": "tsc --noEmit"
},
"dependencies": {
"@lingui/core": "catalog:",
"@lingui/react": "catalog:"
},
"devDependencies": {
"@lingui/cli": "catalog:",
"@lingui/conf": "catalog:",
"@lingui/format-po": "catalog:",
"@types/bun": "catalog:",
"typescript": "catalog:"
}
}
+70
View File
@@ -0,0 +1,70 @@
import { i18n } from "./index";
/** Whether a string looks like a date-only value (no time component). */
function isDateOnly(value: string): boolean {
return /^\d{4}-\d{2}-\d{2}$/.test(value);
}
function toDate(date: Date | string): Date {
return typeof date === "string" ? new Date(date) : date;
}
export function formatDate(
date: Date | string,
options?: Intl.DateTimeFormatOptions,
): string {
// Date-only ISO strings (e.g. "1990-05-15") are parsed as UTC midnight.
// Format in UTC so users west of UTC don't see the previous day.
const useUTC =
typeof date === "string" && isDateOnly(date) && !options?.timeZone;
return new Intl.DateTimeFormat(i18n.locale, {
year: "numeric",
month: "long",
day: "numeric",
...(useUTC ? { timeZone: "UTC" } : {}),
...options,
}).format(toDate(date));
}
export function formatShortDate(date: Date | string): string {
return new Intl.DateTimeFormat(i18n.locale, {
month: "short",
day: "numeric",
year: "numeric",
}).format(toDate(date));
}
export function formatRelativeTime(date: Date | string): string {
const d = toDate(date);
const now = Date.now();
const diffMs = d.getTime() - now;
const absSec = Math.abs(Math.round(diffMs / 1000));
const absMin = Math.abs(Math.round(diffMs / 60_000));
const absHour = Math.abs(Math.round(diffMs / 3_600_000));
const absDay = Math.abs(Math.round(diffMs / 86_400_000));
const sign = diffMs < 0 ? -1 : 1;
const rtf = new Intl.RelativeTimeFormat(i18n.locale, { numeric: "auto" });
if (absSec < 60) return rtf.format(sign * absSec, "second");
if (absMin < 60) return rtf.format(sign * absMin, "minute");
if (absHour < 24) return rtf.format(sign * absHour, "hour");
if (absDay < 30) return rtf.format(sign * absDay, "day");
if (absDay < 365) return rtf.format(sign * Math.round(absDay / 30), "month");
return rtf.format(sign * Math.round(absDay / 365), "year");
}
export function formatNumber(
n: number,
options?: Intl.NumberFormatOptions,
): string {
return new Intl.NumberFormat(i18n.locale, options).format(n);
}
export function formatBytes(bytes: number): string {
if (bytes === 0) return "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
const value = bytes / k ** i;
return `${new Intl.NumberFormat(i18n.locale, { maximumFractionDigits: 1 }).format(value)} ${sizes[i]}`;
}
+34
View File
@@ -0,0 +1,34 @@
import { i18n, type Messages } from "@lingui/core";
import { messages as enMessages } from "./po/en";
export const SUPPORTED_LOCALES = ["en", "fr", "de", "es", "it", "pt"] as const;
export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
// English always bundled — zero async on default locale
i18n.load("en", enMessages);
i18n.activate("en");
const loaders: Record<string, () => Promise<{ messages: Messages }>> = {
fr: () => import("./po/fr"),
de: () => import("./po/de"),
es: () => import("./po/es"),
it: () => import("./po/it"),
pt: () => import("./po/pt"),
};
export async function activateLocale(locale: SupportedLocale): Promise<void> {
if (locale === "en") {
i18n.activate("en");
return;
}
const loader = loaders[locale];
if (!loader) {
i18n.activate("en");
return;
}
const { messages } = await loader();
i18n.load(locale, messages);
i18n.activate(locale);
}
export { i18n };
+16
View File
@@ -0,0 +1,16 @@
import type { SupportedLocale } from "./index";
export interface LocaleInfo {
code: SupportedLocale;
name: string;
nativeName: string;
}
export const LOCALE_INFO: LocaleInfo[] = [
{ code: "en", name: "English", nativeName: "English" },
{ code: "fr", name: "French", nativeName: "Fran\u00e7ais" },
{ code: "de", name: "German", nativeName: "Deutsch" },
{ code: "es", name: "Spanish", nativeName: "Espa\u00f1ol" },
{ code: "it", name: "Italian", nativeName: "Italiano" },
{ code: "pt", name: "Portuguese", nativeName: "Portugu\u00eas" },
];
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"+5kO8P\":[\"Saturday\"],\"+6i0lS\":[\"Retrieving your watch history, watchlist, and ratings...\"],\"+Cv+V9\":[\"Remove from library\"],\"+JkEpu\":[\"This page was left on the cutting room floor. It may have been moved, removed, or never made it past the screenplay.\"],\"+K0AvT\":[\"Disconnect\"],\"+N0l5/\":[\"Connected to <0>\",[\"serverHost\"],\"</0>. Tap to change.\"],\"+gLHYi\":[\"Failed to load title\"],\"+j1ex/\":[\"Failed to remove from library\"],\"+nF1ZO\":[\"Remove Photo\"],\"/+6dvC\":[\"Errors (\",[\"0\"],\")\"],\"/BBXeA\":[\"Connecting to server...\"],\"/DwR+n\":[\"Creating…\"],\"/XtbPO\":[[\"0\"],\" titles\"],\"/cg+QV\":[\"Watch all\"],\"/gQXGv\":[\"Failed to update status\"],\"/nT6AE\":[\"New password\"],\"/sHc1/\":[\"Update check\"],\"0+dyau\":[\"Failed to update retention setting\"],\"0BFJKK\":[\"Authorization was denied. Please try again.\"],\"0GHb20\":[\"Purge metadata cache?\"],\"0IBW21\":[\"Failed to purge caches\"],\"0gH/sc\":[\"Remove profile picture\"],\"1+P9RR\":[\"Switch to \",[\"0\"]],\"12cc1j\":[\"Watching\"],\"12lVOl\":[\"Self-hosted movie & TV tracker\"],\"1B4z0M\":[\"Filmography\"],\"1J4Ek0\":[\"Automatically back up your database on a schedule\"],\"1JhxXW\":[\"Episode \",[\"0\"],\", \",[\"episodeLabel\"]],\"1N6wNP\":[\"Import options\"],\"1Qz4uG\":[\"Enable the \\\"Playback\\\" event category\"],\"1hMWR6\":[\"Create account\"],\"1jHIjh\":[\"Watched all of \",[\"seasonName\"]],\"1vSYsG\":[\"Download backup\"],\"1wL1tj\":[\"TV show\"],\"2FletP\":[\"Purged \",[\"0\",\"plural\",{\"one\":[\"#\",\" title\"],\"other\":[\"#\",\" titles\"]}],\", \",[\"1\",\"plural\",{\"one\":[\"#\",\" person\"],\"other\":[\"#\",\" persons\"]}],\", \",[\"2\",\"plural\",{\"one\":[\"#\",\" file\"],\"other\":[\"#\",\" files\"]}],\" (\",[\"freed\"],\" freed)\"],\"2Fsd9r\":[\"This Month\"],\"2MPcep\":[\"Import complete\"],\"2Mu33Z\":[\"Cache management\"],\"2POOFK\":[\"Free\"],\"2Pt2NY\":[\"This will remove all items from your history.\"],\"2fCpt5\":[\"Return home\"],\"2pPBp6\":[\"Trending today\"],\"39y5bn\":[\"Friday\"],\"3Blefz\":[\"Ratings\"],\"3JTlG8\":[\"Recent Searches\"],\"3Jy8bM\":[\"Movies \",[\"select\"]],\"3L9OuQ\":[\"Episode \",[\"0\"]],\"3T8ziB\":[\"Create an account\"],\"3hELxX\":[\"Enter your Sofa server URL to get started\"],\"4fxLkp\":[\"Open Sonarr, go to <0>Settings > Import Lists</0>\"],\"4kpBqM\":[\"Last event \",[\"0\"]],\"4uUjVO\":[\"Producer\"],\"4x+A56\":[\"Anonymous usage reporting\"],\"5IShvp\":[\"Import \",[\"totalItems\"],\" items\"],\"5IYJSv\":[\"Explore titles\"],\"5Y4mym\":[\"Import from \",[\"0\"]],\"5ZzgbQ\":[\"Connect \",[\"0\"]],\"5fEnbK\":[\"Failed to update scheduled backup setting\"],\"5lWFkC\":[\"Sign in\"],\"5v9C16\":[\"Connected to \",[\"source\"]],\"623bR4\":[\"Failed to trigger job\"],\"6HN0yh\":[\"Open Plex, go to Settings > Webhooks\"],\"6TNjOJ\":[\"Failed to update rating\"],\"6TfUy6\":[\"last \",[\"value\"]],\"6V3Ea3\":[\"Copied\"],\"6YtxFj\":[\"Name\"],\"6exX+8\":[\"Regenerate\"],\"6gRgw8\":[\"Retry\"],\"6lGV3K\":[\"Show less\"],\"76++pR\":[\"Warnings\"],\"77DIAu\":[\"Go to Dashboard > Plugins > Webhook\"],\"7Bj3x9\":[\"Failed\"],\"7D50KC\":[[\"label\"],\" disconnected\"],\"7GfM5w\":[\"Recently Viewed\"],\"7L01XJ\":[\"Actions\"],\"7eMo+U\":[\"Go Home\"],\"7p5kLi\":[\"Dashboard\"],\"85eoJ1\":[\"Schedule updated\"],\"8B9E2D\":[\"Day:\"],\"8YwF1J\":[\"Go to <0>Dashboard > Plugins > Webhook</0>\"],\"8ZsakT\":[\"Password\"],\"8tjQCz\":[\"Explore\"],\"8vNtLy\":[\"Paste the Radarr URL above into the List URL field\"],\"8wYDMp\":[\"Already have an account?\"],\"9AE3vb\":[\"Open Plex, go to <0>Settings > Webhooks<1/></0>\"],\"9GW0ZB\":[\"Import is still running in the background. Check back later.\"],\"9NyAH9\":[\"Skipped\"],\"9Xhrps\":[\"Failed to connect\"],\"9ZLGbA\":[\"backups.\"],\"9eF5oV\":[\"Welcome back\"],\"9rG25a\":[\"Server URL\"],\"A+0rLe\":[\"Marked as completed\"],\"AOHgZp\":[\"Episodes\"],\"AOddWK\":[\"Connect \",[\"label\"]],\"ATfxL/\":[\"Trailer\"],\"AVlZoM\":[\"Environment\"],\"Acf6vF\":[\"Save name\"],\"AdoUfN\":[\"Failed to add to watchlist\"],\"AeXO77\":[\"Account\"],\"AjtYj0\":[\"On Watchlist\"],\"Avee+B\":[\"Failed to update name\"],\"B2R3xD\":[\"Toggle scheduled backups\"],\"BEVzjL\":[\"Import failed\"],\"BQnS5I\":[\"Open \",[\"source\"],\" to enter code\"],\"BUnoSB\":[[\"0\"],\" \",[\"1\",\"plural\",{\"one\":[\"backup\"],\"other\":[\"backups\"]}],\" stored\"],\"BpttUI\":[\"Trending Today\"],\"BrrIs8\":[\"Storage\"],\"BskWMl\":[\"Unreachable\"],\"BzEFor\":[\"or\"],\"CGDHFb\":[\"Add a <0>Generic Destination</0> and paste the URL above\"],\"CGExDN\":[\"Failed to purge image cache\"],\"CHeXFE\":[\"Status updated\"],\"CKyk7Q\":[\"Go back\"],\"CaB/+I\":[\"Welcome back, \",[\"name\"]],\"CodnUh\":[\"Removed from library\"],\"CpzGJY\":[\"This may take a few minutes for large libraries. Please don't close this tab.\"],\"CuPxpd\":[\"Requires an active <0>PlexLine truncated
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"+5kO8P\":[\"Saturday\"],\"+6i0lS\":[\"Retrieving your watch history, watchlist, and ratings...\"],\"+Cv+V9\":[\"Remove from library\"],\"+JkEpu\":[\"This page was left on the cutting room floor. It may have been moved, removed, or never made it past the screenplay.\"],\"+K0AvT\":[\"Disconnect\"],\"+N0l5/\":[\"Connected to <0>\",[\"serverHost\"],\"</0>. Tap to change.\"],\"+gLHYi\":[\"Failed to load title\"],\"+j1ex/\":[\"Failed to remove from library\"],\"+nF1ZO\":[\"Remove Photo\"],\"/+6dvC\":[\"Errors (\",[\"0\"],\")\"],\"/BBXeA\":[\"Connecting to server...\"],\"/DwR+n\":[\"Creating…\"],\"/XtbPO\":[[\"0\"],\" titles\"],\"/cg+QV\":[\"Watch all\"],\"/gQXGv\":[\"Failed to update status\"],\"/nT6AE\":[\"New password\"],\"/sHc1/\":[\"Update check\"],\"0+dyau\":[\"Failed to update retention setting\"],\"0BFJKK\":[\"Authorization was denied. Please try again.\"],\"0GHb20\":[\"Purge metadata cache?\"],\"0IBW21\":[\"Failed to purge caches\"],\"0gH/sc\":[\"Remove profile picture\"],\"1+P9RR\":[\"Switch to \",[\"0\"]],\"12cc1j\":[\"Watching\"],\"12lVOl\":[\"Self-hosted movie & TV tracker\"],\"1B4z0M\":[\"Filmography\"],\"1J4Ek0\":[\"Automatically back up your database on a schedule\"],\"1JhxXW\":[\"Episode \",[\"0\"],\", \",[\"episodeLabel\"]],\"1N6wNP\":[\"Import options\"],\"1Qz4uG\":[\"Enable the \\\"Playback\\\" event category\"],\"1hMWR6\":[\"Create account\"],\"1jHIjh\":[\"Watched all of \",[\"seasonName\"]],\"1vSYsG\":[\"Download backup\"],\"1wL1tj\":[\"TV show\"],\"2FletP\":[\"Purged \",[\"0\",\"plural\",{\"one\":[\"#\",\" title\"],\"other\":[\"#\",\" titles\"]}],\", \",[\"1\",\"plural\",{\"one\":[\"#\",\" person\"],\"other\":[\"#\",\" persons\"]}],\", \",[\"2\",\"plural\",{\"one\":[\"#\",\" file\"],\"other\":[\"#\",\" files\"]}],\" (\",[\"freed\"],\" freed)\"],\"2Fsd9r\":[\"This Month\"],\"2MPcep\":[\"Import complete\"],\"2Mu33Z\":[\"Cache management\"],\"2POOFK\":[\"Free\"],\"2Pt2NY\":[\"This will remove all items from your history.\"],\"2fCpt5\":[\"Return home\"],\"2pPBp6\":[\"Trending today\"],\"39y5bn\":[\"Friday\"],\"3Blefz\":[\"Ratings\"],\"3JTlG8\":[\"Recent Searches\"],\"3Jy8bM\":[\"Movies \",[\"select\"]],\"3L9OuQ\":[\"Episode \",[\"0\"]],\"3T8ziB\":[\"Create an account\"],\"3hELxX\":[\"Enter your Sofa server URL to get started\"],\"4fxLkp\":[\"Open Sonarr, go to <0>Settings > Import Lists</0>\"],\"4kpBqM\":[\"Last event \",[\"0\"]],\"4uUjVO\":[\"Producer\"],\"4x+A56\":[\"Anonymous usage reporting\"],\"5IShvp\":[\"Import \",[\"totalItems\"],\" items\"],\"5IYJSv\":[\"Explore titles\"],\"5Y4mym\":[\"Import from \",[\"0\"]],\"5ZzgbQ\":[\"Connect \",[\"0\"]],\"5fEnbK\":[\"Failed to update scheduled backup setting\"],\"5lWFkC\":[\"Sign in\"],\"5v9C16\":[\"Connected to \",[\"source\"]],\"623bR4\":[\"Failed to trigger job\"],\"6HN0yh\":[\"Open Plex, go to Settings > Webhooks\"],\"6TNjOJ\":[\"Failed to update rating\"],\"6TfUy6\":[\"last \",[\"value\"]],\"6V3Ea3\":[\"Copied\"],\"6YtxFj\":[\"Name\"],\"6exX+8\":[\"Regenerate\"],\"6gRgw8\":[\"Retry\"],\"6lGV3K\":[\"Show less\"],\"76++pR\":[\"Warnings\"],\"77DIAu\":[\"Go to Dashboard > Plugins > Webhook\"],\"7Bj3x9\":[\"Failed\"],\"7D50KC\":[[\"label\"],\" disconnected\"],\"7GfM5w\":[\"Recently Viewed\"],\"7L01XJ\":[\"Actions\"],\"7eMo+U\":[\"Go Home\"],\"7p5kLi\":[\"Dashboard\"],\"85eoJ1\":[\"Schedule updated\"],\"8B9E2D\":[\"Day:\"],\"8YwF1J\":[\"Go to <0>Dashboard > Plugins > Webhook</0>\"],\"8ZsakT\":[\"Password\"],\"8tjQCz\":[\"Explore\"],\"8vNtLy\":[\"Paste the Radarr URL above into the List URL field\"],\"8wYDMp\":[\"Already have an account?\"],\"9AE3vb\":[\"Open Plex, go to <0>Settings > Webhooks<1/></0>\"],\"9GW0ZB\":[\"Import is still running in the background. Check back later.\"],\"9NyAH9\":[\"Skipped\"],\"9Xhrps\":[\"Failed to connect\"],\"9ZLGbA\":[\"backups.\"],\"9eF5oV\":[\"Welcome back\"],\"9rG25a\":[\"Server URL\"],\"A+0rLe\":[\"Marked as completed\"],\"AOHgZp\":[\"Episodes\"],\"AOddWK\":[\"Connect \",[\"label\"]],\"ATfxL/\":[\"Trailer\"],\"AVlZoM\":[\"Environment\"],\"Acf6vF\":[\"Save name\"],\"AdoUfN\":[\"Failed to add to watchlist\"],\"AeXO77\":[\"Account\"],\"AjtYj0\":[\"On Watchlist\"],\"Avee+B\":[\"Failed to update name\"],\"B2R3xD\":[\"Toggle scheduled backups\"],\"BEVzjL\":[\"Import failed\"],\"BQnS5I\":[\"Open \",[\"source\"],\" to enter code\"],\"BUnoSB\":[[\"0\"],\" \",[\"1\",\"plural\",{\"one\":[\"backup\"],\"other\":[\"backups\"]}],\" stored\"],\"BpttUI\":[\"Trending Today\"],\"BrrIs8\":[\"Storage\"],\"BskWMl\":[\"Unreachable\"],\"BzEFor\":[\"or\"],\"CGDHFb\":[\"Add a <0>Generic Destination</0> and paste the URL above\"],\"CGExDN\":[\"Failed to purge image cache\"],\"CHeXFE\":[\"Status updated\"],\"CKyk7Q\":[\"Go back\"],\"CaB/+I\":[\"Welcome back, \",[\"name\"]],\"CodnUh\":[\"Removed from library\"],\"CpzGJY\":[\"This may take a few minutes for large libraries. Please don't close this tab.\"],\"CuPxpd\":[\"Requires an active <0>PlexLine truncated
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"+5kO8P\":[\"Saturday\"],\"+6i0lS\":[\"Retrieving your watch history, watchlist, and ratings...\"],\"+Cv+V9\":[\"Remove from library\"],\"+JkEpu\":[\"This page was left on the cutting room floor. It may have been moved, removed, or never made it past the screenplay.\"],\"+K0AvT\":[\"Disconnect\"],\"+N0l5/\":[\"Connected to <0>\",[\"serverHost\"],\"</0>. Tap to change.\"],\"+gLHYi\":[\"Failed to load title\"],\"+j1ex/\":[\"Failed to remove from library\"],\"+nF1ZO\":[\"Remove Photo\"],\"/+6dvC\":[\"Errors (\",[\"0\"],\")\"],\"/BBXeA\":[\"Connecting to server...\"],\"/DwR+n\":[\"Creating…\"],\"/XtbPO\":[[\"0\"],\" titles\"],\"/cg+QV\":[\"Watch all\"],\"/gQXGv\":[\"Failed to update status\"],\"/nT6AE\":[\"New password\"],\"/sHc1/\":[\"Update check\"],\"0+dyau\":[\"Failed to update retention setting\"],\"0BFJKK\":[\"Authorization was denied. Please try again.\"],\"0GHb20\":[\"Purge metadata cache?\"],\"0IBW21\":[\"Failed to purge caches\"],\"0gH/sc\":[\"Remove profile picture\"],\"1+P9RR\":[\"Switch to \",[\"0\"]],\"12cc1j\":[\"Watching\"],\"12lVOl\":[\"Self-hosted movie & TV tracker\"],\"1B4z0M\":[\"Filmography\"],\"1J4Ek0\":[\"Automatically back up your database on a schedule\"],\"1JhxXW\":[\"Episode \",[\"0\"],\", \",[\"episodeLabel\"]],\"1N6wNP\":[\"Import options\"],\"1Qz4uG\":[\"Enable the \\\"Playback\\\" event category\"],\"1hMWR6\":[\"Create account\"],\"1jHIjh\":[\"Watched all of \",[\"seasonName\"]],\"1vSYsG\":[\"Download backup\"],\"1wL1tj\":[\"TV show\"],\"2FletP\":[\"Purged \",[\"0\",\"plural\",{\"one\":[\"#\",\" title\"],\"other\":[\"#\",\" titles\"]}],\", \",[\"1\",\"plural\",{\"one\":[\"#\",\" person\"],\"other\":[\"#\",\" persons\"]}],\", \",[\"2\",\"plural\",{\"one\":[\"#\",\" file\"],\"other\":[\"#\",\" files\"]}],\" (\",[\"freed\"],\" freed)\"],\"2Fsd9r\":[\"This Month\"],\"2MPcep\":[\"Import complete\"],\"2Mu33Z\":[\"Cache management\"],\"2POOFK\":[\"Free\"],\"2Pt2NY\":[\"This will remove all items from your history.\"],\"2fCpt5\":[\"Return home\"],\"2pPBp6\":[\"Trending today\"],\"39y5bn\":[\"Friday\"],\"3Blefz\":[\"Ratings\"],\"3JTlG8\":[\"Recent Searches\"],\"3Jy8bM\":[\"Movies \",[\"select\"]],\"3L9OuQ\":[\"Episode \",[\"0\"]],\"3T8ziB\":[\"Create an account\"],\"3hELxX\":[\"Enter your Sofa server URL to get started\"],\"4fxLkp\":[\"Open Sonarr, go to <0>Settings > Import Lists</0>\"],\"4kpBqM\":[\"Last event \",[\"0\"]],\"4uUjVO\":[\"Producer\"],\"4x+A56\":[\"Anonymous usage reporting\"],\"5IShvp\":[\"Import \",[\"totalItems\"],\" items\"],\"5IYJSv\":[\"Explore titles\"],\"5Y4mym\":[\"Import from \",[\"0\"]],\"5ZzgbQ\":[\"Connect \",[\"0\"]],\"5fEnbK\":[\"Failed to update scheduled backup setting\"],\"5lWFkC\":[\"Sign in\"],\"5v9C16\":[\"Connected to \",[\"source\"]],\"623bR4\":[\"Failed to trigger job\"],\"6HN0yh\":[\"Open Plex, go to Settings > Webhooks\"],\"6TNjOJ\":[\"Failed to update rating\"],\"6TfUy6\":[\"last \",[\"value\"]],\"6V3Ea3\":[\"Copied\"],\"6YtxFj\":[\"Name\"],\"6exX+8\":[\"Regenerate\"],\"6gRgw8\":[\"Retry\"],\"6lGV3K\":[\"Show less\"],\"76++pR\":[\"Warnings\"],\"77DIAu\":[\"Go to Dashboard > Plugins > Webhook\"],\"7Bj3x9\":[\"Failed\"],\"7D50KC\":[[\"label\"],\" disconnected\"],\"7GfM5w\":[\"Recently Viewed\"],\"7L01XJ\":[\"Actions\"],\"7eMo+U\":[\"Go Home\"],\"7p5kLi\":[\"Dashboard\"],\"85eoJ1\":[\"Schedule updated\"],\"8B9E2D\":[\"Day:\"],\"8YwF1J\":[\"Go to <0>Dashboard > Plugins > Webhook</0>\"],\"8ZsakT\":[\"Password\"],\"8tjQCz\":[\"Explore\"],\"8vNtLy\":[\"Paste the Radarr URL above into the List URL field\"],\"8wYDMp\":[\"Already have an account?\"],\"9AE3vb\":[\"Open Plex, go to <0>Settings > Webhooks<1/></0>\"],\"9GW0ZB\":[\"Import is still running in the background. Check back later.\"],\"9NyAH9\":[\"Skipped\"],\"9Xhrps\":[\"Failed to connect\"],\"9ZLGbA\":[\"backups.\"],\"9eF5oV\":[\"Welcome back\"],\"9rG25a\":[\"Server URL\"],\"A+0rLe\":[\"Marked as completed\"],\"AOHgZp\":[\"Episodes\"],\"AOddWK\":[\"Connect \",[\"label\"]],\"ATfxL/\":[\"Trailer\"],\"AVlZoM\":[\"Environment\"],\"Acf6vF\":[\"Save name\"],\"AdoUfN\":[\"Failed to add to watchlist\"],\"AeXO77\":[\"Account\"],\"AjtYj0\":[\"On Watchlist\"],\"Avee+B\":[\"Failed to update name\"],\"B2R3xD\":[\"Toggle scheduled backups\"],\"BEVzjL\":[\"Import failed\"],\"BQnS5I\":[\"Open \",[\"source\"],\" to enter code\"],\"BUnoSB\":[[\"0\"],\" \",[\"1\",\"plural\",{\"one\":[\"backup\"],\"other\":[\"backups\"]}],\" stored\"],\"BpttUI\":[\"Trending Today\"],\"BrrIs8\":[\"Storage\"],\"BskWMl\":[\"Unreachable\"],\"BzEFor\":[\"or\"],\"CGDHFb\":[\"Add a <0>Generic Destination</0> and paste the URL above\"],\"CGExDN\":[\"Failed to purge image cache\"],\"CHeXFE\":[\"Status updated\"],\"CKyk7Q\":[\"Go back\"],\"CaB/+I\":[\"Welcome back, \",[\"name\"]],\"CodnUh\":[\"Removed from library\"],\"CpzGJY\":[\"This may take a few minutes for large libraries. Please don't close this tab.\"],\"CuPxpd\":[\"Requires an active <0>PlexLine truncated
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"+5kO8P\":[\"Saturday\"],\"+6i0lS\":[\"Retrieving your watch history, watchlist, and ratings...\"],\"+Cv+V9\":[\"Remove from library\"],\"+JkEpu\":[\"This page was left on the cutting room floor. It may have been moved, removed, or never made it past the screenplay.\"],\"+K0AvT\":[\"Disconnect\"],\"+N0l5/\":[\"Connected to <0>\",[\"serverHost\"],\"</0>. Tap to change.\"],\"+gLHYi\":[\"Failed to load title\"],\"+j1ex/\":[\"Failed to remove from library\"],\"+nF1ZO\":[\"Remove Photo\"],\"/+6dvC\":[\"Errors (\",[\"0\"],\")\"],\"/BBXeA\":[\"Connecting to server...\"],\"/DwR+n\":[\"Creating…\"],\"/XtbPO\":[[\"0\"],\" titles\"],\"/cg+QV\":[\"Watch all\"],\"/gQXGv\":[\"Failed to update status\"],\"/nT6AE\":[\"New password\"],\"/sHc1/\":[\"Update check\"],\"0+dyau\":[\"Failed to update retention setting\"],\"0BFJKK\":[\"Authorization was denied. Please try again.\"],\"0GHb20\":[\"Purge metadata cache?\"],\"0IBW21\":[\"Failed to purge caches\"],\"0gH/sc\":[\"Remove profile picture\"],\"1+P9RR\":[\"Switch to \",[\"0\"]],\"12cc1j\":[\"Watching\"],\"12lVOl\":[\"Self-hosted movie & TV tracker\"],\"1B4z0M\":[\"Filmography\"],\"1J4Ek0\":[\"Automatically back up your database on a schedule\"],\"1JhxXW\":[\"Episode \",[\"0\"],\", \",[\"episodeLabel\"]],\"1N6wNP\":[\"Import options\"],\"1Qz4uG\":[\"Enable the \\\"Playback\\\" event category\"],\"1hMWR6\":[\"Create account\"],\"1jHIjh\":[\"Watched all of \",[\"seasonName\"]],\"1vSYsG\":[\"Download backup\"],\"1wL1tj\":[\"TV show\"],\"2FletP\":[\"Purged \",[\"0\",\"plural\",{\"one\":[\"#\",\" title\"],\"other\":[\"#\",\" titles\"]}],\", \",[\"1\",\"plural\",{\"one\":[\"#\",\" person\"],\"other\":[\"#\",\" persons\"]}],\", \",[\"2\",\"plural\",{\"one\":[\"#\",\" file\"],\"other\":[\"#\",\" files\"]}],\" (\",[\"freed\"],\" freed)\"],\"2Fsd9r\":[\"This Month\"],\"2MPcep\":[\"Import complete\"],\"2Mu33Z\":[\"Cache management\"],\"2POOFK\":[\"Free\"],\"2Pt2NY\":[\"This will remove all items from your history.\"],\"2fCpt5\":[\"Return home\"],\"2pPBp6\":[\"Trending today\"],\"39y5bn\":[\"Friday\"],\"3Blefz\":[\"Ratings\"],\"3JTlG8\":[\"Recent Searches\"],\"3Jy8bM\":[\"Movies \",[\"select\"]],\"3L9OuQ\":[\"Episode \",[\"0\"]],\"3T8ziB\":[\"Create an account\"],\"3hELxX\":[\"Enter your Sofa server URL to get started\"],\"4fxLkp\":[\"Open Sonarr, go to <0>Settings > Import Lists</0>\"],\"4kpBqM\":[\"Last event \",[\"0\"]],\"4uUjVO\":[\"Producer\"],\"4x+A56\":[\"Anonymous usage reporting\"],\"5IShvp\":[\"Import \",[\"totalItems\"],\" items\"],\"5IYJSv\":[\"Explore titles\"],\"5Y4mym\":[\"Import from \",[\"0\"]],\"5ZzgbQ\":[\"Connect \",[\"0\"]],\"5fEnbK\":[\"Failed to update scheduled backup setting\"],\"5lWFkC\":[\"Sign in\"],\"5v9C16\":[\"Connected to \",[\"source\"]],\"623bR4\":[\"Failed to trigger job\"],\"6HN0yh\":[\"Open Plex, go to Settings > Webhooks\"],\"6TNjOJ\":[\"Failed to update rating\"],\"6TfUy6\":[\"last \",[\"value\"]],\"6V3Ea3\":[\"Copied\"],\"6YtxFj\":[\"Name\"],\"6exX+8\":[\"Regenerate\"],\"6gRgw8\":[\"Retry\"],\"6lGV3K\":[\"Show less\"],\"76++pR\":[\"Warnings\"],\"77DIAu\":[\"Go to Dashboard > Plugins > Webhook\"],\"7Bj3x9\":[\"Failed\"],\"7D50KC\":[[\"label\"],\" disconnected\"],\"7GfM5w\":[\"Recently Viewed\"],\"7L01XJ\":[\"Actions\"],\"7eMo+U\":[\"Go Home\"],\"7p5kLi\":[\"Dashboard\"],\"85eoJ1\":[\"Schedule updated\"],\"8B9E2D\":[\"Day:\"],\"8YwF1J\":[\"Go to <0>Dashboard > Plugins > Webhook</0>\"],\"8ZsakT\":[\"Password\"],\"8tjQCz\":[\"Explore\"],\"8vNtLy\":[\"Paste the Radarr URL above into the List URL field\"],\"8wYDMp\":[\"Already have an account?\"],\"9AE3vb\":[\"Open Plex, go to <0>Settings > Webhooks<1/></0>\"],\"9GW0ZB\":[\"Import is still running in the background. Check back later.\"],\"9NyAH9\":[\"Skipped\"],\"9Xhrps\":[\"Failed to connect\"],\"9ZLGbA\":[\"backups.\"],\"9eF5oV\":[\"Welcome back\"],\"9rG25a\":[\"Server URL\"],\"A+0rLe\":[\"Marked as completed\"],\"AOHgZp\":[\"Episodes\"],\"AOddWK\":[\"Connect \",[\"label\"]],\"ATfxL/\":[\"Trailer\"],\"AVlZoM\":[\"Environment\"],\"Acf6vF\":[\"Save name\"],\"AdoUfN\":[\"Failed to add to watchlist\"],\"AeXO77\":[\"Account\"],\"AjtYj0\":[\"On Watchlist\"],\"Avee+B\":[\"Failed to update name\"],\"B2R3xD\":[\"Toggle scheduled backups\"],\"BEVzjL\":[\"Import failed\"],\"BQnS5I\":[\"Open \",[\"source\"],\" to enter code\"],\"BUnoSB\":[[\"0\"],\" \",[\"1\",\"plural\",{\"one\":[\"backup\"],\"other\":[\"backups\"]}],\" stored\"],\"BpttUI\":[\"Trending Today\"],\"BrrIs8\":[\"Storage\"],\"BskWMl\":[\"Unreachable\"],\"BzEFor\":[\"or\"],\"CGDHFb\":[\"Add a <0>Generic Destination</0> and paste the URL above\"],\"CGExDN\":[\"Failed to purge image cache\"],\"CHeXFE\":[\"Status updated\"],\"CKyk7Q\":[\"Go back\"],\"CaB/+I\":[\"Welcome back, \",[\"name\"]],\"CodnUh\":[\"Removed from library\"],\"CpzGJY\":[\"This may take a few minutes for large libraries. Please don't close this tab.\"],\"CuPxpd\":[\"Requires an active <0>PlexLine truncated
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"+5kO8P\":[\"Saturday\"],\"+6i0lS\":[\"Retrieving your watch history, watchlist, and ratings...\"],\"+Cv+V9\":[\"Remove from library\"],\"+JkEpu\":[\"This page was left on the cutting room floor. It may have been moved, removed, or never made it past the screenplay.\"],\"+K0AvT\":[\"Disconnect\"],\"+N0l5/\":[\"Connected to <0>\",[\"serverHost\"],\"</0>. Tap to change.\"],\"+gLHYi\":[\"Failed to load title\"],\"+j1ex/\":[\"Failed to remove from library\"],\"+nF1ZO\":[\"Remove Photo\"],\"/+6dvC\":[\"Errors (\",[\"0\"],\")\"],\"/BBXeA\":[\"Connecting to server...\"],\"/DwR+n\":[\"Creating…\"],\"/XtbPO\":[[\"0\"],\" titles\"],\"/cg+QV\":[\"Watch all\"],\"/gQXGv\":[\"Failed to update status\"],\"/nT6AE\":[\"New password\"],\"/sHc1/\":[\"Update check\"],\"0+dyau\":[\"Failed to update retention setting\"],\"0BFJKK\":[\"Authorization was denied. Please try again.\"],\"0GHb20\":[\"Purge metadata cache?\"],\"0IBW21\":[\"Failed to purge caches\"],\"0gH/sc\":[\"Remove profile picture\"],\"1+P9RR\":[\"Switch to \",[\"0\"]],\"12cc1j\":[\"Watching\"],\"12lVOl\":[\"Self-hosted movie & TV tracker\"],\"1B4z0M\":[\"Filmography\"],\"1J4Ek0\":[\"Automatically back up your database on a schedule\"],\"1JhxXW\":[\"Episode \",[\"0\"],\", \",[\"episodeLabel\"]],\"1N6wNP\":[\"Import options\"],\"1Qz4uG\":[\"Enable the \\\"Playback\\\" event category\"],\"1hMWR6\":[\"Create account\"],\"1jHIjh\":[\"Watched all of \",[\"seasonName\"]],\"1vSYsG\":[\"Download backup\"],\"1wL1tj\":[\"TV show\"],\"2FletP\":[\"Purged \",[\"0\",\"plural\",{\"one\":[\"#\",\" title\"],\"other\":[\"#\",\" titles\"]}],\", \",[\"1\",\"plural\",{\"one\":[\"#\",\" person\"],\"other\":[\"#\",\" persons\"]}],\", \",[\"2\",\"plural\",{\"one\":[\"#\",\" file\"],\"other\":[\"#\",\" files\"]}],\" (\",[\"freed\"],\" freed)\"],\"2Fsd9r\":[\"This Month\"],\"2MPcep\":[\"Import complete\"],\"2Mu33Z\":[\"Cache management\"],\"2POOFK\":[\"Free\"],\"2Pt2NY\":[\"This will remove all items from your history.\"],\"2fCpt5\":[\"Return home\"],\"2pPBp6\":[\"Trending today\"],\"39y5bn\":[\"Friday\"],\"3Blefz\":[\"Ratings\"],\"3JTlG8\":[\"Recent Searches\"],\"3Jy8bM\":[\"Movies \",[\"select\"]],\"3L9OuQ\":[\"Episode \",[\"0\"]],\"3T8ziB\":[\"Create an account\"],\"3hELxX\":[\"Enter your Sofa server URL to get started\"],\"4fxLkp\":[\"Open Sonarr, go to <0>Settings > Import Lists</0>\"],\"4kpBqM\":[\"Last event \",[\"0\"]],\"4uUjVO\":[\"Producer\"],\"4x+A56\":[\"Anonymous usage reporting\"],\"5IShvp\":[\"Import \",[\"totalItems\"],\" items\"],\"5IYJSv\":[\"Explore titles\"],\"5Y4mym\":[\"Import from \",[\"0\"]],\"5ZzgbQ\":[\"Connect \",[\"0\"]],\"5fEnbK\":[\"Failed to update scheduled backup setting\"],\"5lWFkC\":[\"Sign in\"],\"5v9C16\":[\"Connected to \",[\"source\"]],\"623bR4\":[\"Failed to trigger job\"],\"6HN0yh\":[\"Open Plex, go to Settings > Webhooks\"],\"6TNjOJ\":[\"Failed to update rating\"],\"6TfUy6\":[\"last \",[\"value\"]],\"6V3Ea3\":[\"Copied\"],\"6YtxFj\":[\"Name\"],\"6exX+8\":[\"Regenerate\"],\"6gRgw8\":[\"Retry\"],\"6lGV3K\":[\"Show less\"],\"76++pR\":[\"Warnings\"],\"77DIAu\":[\"Go to Dashboard > Plugins > Webhook\"],\"7Bj3x9\":[\"Failed\"],\"7D50KC\":[[\"label\"],\" disconnected\"],\"7GfM5w\":[\"Recently Viewed\"],\"7L01XJ\":[\"Actions\"],\"7eMo+U\":[\"Go Home\"],\"7p5kLi\":[\"Dashboard\"],\"85eoJ1\":[\"Schedule updated\"],\"8B9E2D\":[\"Day:\"],\"8YwF1J\":[\"Go to <0>Dashboard > Plugins > Webhook</0>\"],\"8ZsakT\":[\"Password\"],\"8tjQCz\":[\"Explore\"],\"8vNtLy\":[\"Paste the Radarr URL above into the List URL field\"],\"8wYDMp\":[\"Already have an account?\"],\"9AE3vb\":[\"Open Plex, go to <0>Settings > Webhooks<1/></0>\"],\"9GW0ZB\":[\"Import is still running in the background. Check back later.\"],\"9NyAH9\":[\"Skipped\"],\"9Xhrps\":[\"Failed to connect\"],\"9ZLGbA\":[\"backups.\"],\"9eF5oV\":[\"Welcome back\"],\"9rG25a\":[\"Server URL\"],\"A+0rLe\":[\"Marked as completed\"],\"AOHgZp\":[\"Episodes\"],\"AOddWK\":[\"Connect \",[\"label\"]],\"ATfxL/\":[\"Trailer\"],\"AVlZoM\":[\"Environment\"],\"Acf6vF\":[\"Save name\"],\"AdoUfN\":[\"Failed to add to watchlist\"],\"AeXO77\":[\"Account\"],\"AjtYj0\":[\"On Watchlist\"],\"Avee+B\":[\"Failed to update name\"],\"B2R3xD\":[\"Toggle scheduled backups\"],\"BEVzjL\":[\"Import failed\"],\"BQnS5I\":[\"Open \",[\"source\"],\" to enter code\"],\"BUnoSB\":[[\"0\"],\" \",[\"1\",\"plural\",{\"one\":[\"backup\"],\"other\":[\"backups\"]}],\" stored\"],\"BpttUI\":[\"Trending Today\"],\"BrrIs8\":[\"Storage\"],\"BskWMl\":[\"Unreachable\"],\"BzEFor\":[\"or\"],\"CGDHFb\":[\"Add a <0>Generic Destination</0> and paste the URL above\"],\"CGExDN\":[\"Failed to purge image cache\"],\"CHeXFE\":[\"Status updated\"],\"CKyk7Q\":[\"Go back\"],\"CaB/+I\":[\"Welcome back, \",[\"name\"]],\"CodnUh\":[\"Removed from library\"],\"CpzGJY\":[\"This may take a few minutes for large libraries. Please don't close this tab.\"],\"CuPxpd\":[\"Requires an active <0>PlexLine truncated
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"+5kO8P\":[\"Saturday\"],\"+6i0lS\":[\"Retrieving your watch history, watchlist, and ratings...\"],\"+Cv+V9\":[\"Remove from library\"],\"+JkEpu\":[\"This page was left on the cutting room floor. It may have been moved, removed, or never made it past the screenplay.\"],\"+K0AvT\":[\"Disconnect\"],\"+N0l5/\":[\"Connected to <0>\",[\"serverHost\"],\"</0>. Tap to change.\"],\"+gLHYi\":[\"Failed to load title\"],\"+j1ex/\":[\"Failed to remove from library\"],\"+nF1ZO\":[\"Remove Photo\"],\"/+6dvC\":[\"Errors (\",[\"0\"],\")\"],\"/BBXeA\":[\"Connecting to server...\"],\"/DwR+n\":[\"Creating…\"],\"/XtbPO\":[[\"0\"],\" titles\"],\"/cg+QV\":[\"Watch all\"],\"/gQXGv\":[\"Failed to update status\"],\"/nT6AE\":[\"New password\"],\"/sHc1/\":[\"Update check\"],\"0+dyau\":[\"Failed to update retention setting\"],\"0BFJKK\":[\"Authorization was denied. Please try again.\"],\"0GHb20\":[\"Purge metadata cache?\"],\"0IBW21\":[\"Failed to purge caches\"],\"0gH/sc\":[\"Remove profile picture\"],\"1+P9RR\":[\"Switch to \",[\"0\"]],\"12cc1j\":[\"Watching\"],\"12lVOl\":[\"Self-hosted movie & TV tracker\"],\"1B4z0M\":[\"Filmography\"],\"1J4Ek0\":[\"Automatically back up your database on a schedule\"],\"1JhxXW\":[\"Episode \",[\"0\"],\", \",[\"episodeLabel\"]],\"1N6wNP\":[\"Import options\"],\"1Qz4uG\":[\"Enable the \\\"Playback\\\" event category\"],\"1hMWR6\":[\"Create account\"],\"1jHIjh\":[\"Watched all of \",[\"seasonName\"]],\"1vSYsG\":[\"Download backup\"],\"1wL1tj\":[\"TV show\"],\"2FletP\":[\"Purged \",[\"0\",\"plural\",{\"one\":[\"#\",\" title\"],\"other\":[\"#\",\" titles\"]}],\", \",[\"1\",\"plural\",{\"one\":[\"#\",\" person\"],\"other\":[\"#\",\" persons\"]}],\", \",[\"2\",\"plural\",{\"one\":[\"#\",\" file\"],\"other\":[\"#\",\" files\"]}],\" (\",[\"freed\"],\" freed)\"],\"2Fsd9r\":[\"This Month\"],\"2MPcep\":[\"Import complete\"],\"2Mu33Z\":[\"Cache management\"],\"2POOFK\":[\"Free\"],\"2Pt2NY\":[\"This will remove all items from your history.\"],\"2fCpt5\":[\"Return home\"],\"2pPBp6\":[\"Trending today\"],\"39y5bn\":[\"Friday\"],\"3Blefz\":[\"Ratings\"],\"3JTlG8\":[\"Recent Searches\"],\"3Jy8bM\":[\"Movies \",[\"select\"]],\"3L9OuQ\":[\"Episode \",[\"0\"]],\"3T8ziB\":[\"Create an account\"],\"3hELxX\":[\"Enter your Sofa server URL to get started\"],\"4fxLkp\":[\"Open Sonarr, go to <0>Settings > Import Lists</0>\"],\"4kpBqM\":[\"Last event \",[\"0\"]],\"4uUjVO\":[\"Producer\"],\"4x+A56\":[\"Anonymous usage reporting\"],\"5IShvp\":[\"Import \",[\"totalItems\"],\" items\"],\"5IYJSv\":[\"Explore titles\"],\"5Y4mym\":[\"Import from \",[\"0\"]],\"5ZzgbQ\":[\"Connect \",[\"0\"]],\"5fEnbK\":[\"Failed to update scheduled backup setting\"],\"5lWFkC\":[\"Sign in\"],\"5v9C16\":[\"Connected to \",[\"source\"]],\"623bR4\":[\"Failed to trigger job\"],\"6HN0yh\":[\"Open Plex, go to Settings > Webhooks\"],\"6TNjOJ\":[\"Failed to update rating\"],\"6TfUy6\":[\"last \",[\"value\"]],\"6V3Ea3\":[\"Copied\"],\"6YtxFj\":[\"Name\"],\"6exX+8\":[\"Regenerate\"],\"6gRgw8\":[\"Retry\"],\"6lGV3K\":[\"Show less\"],\"76++pR\":[\"Warnings\"],\"77DIAu\":[\"Go to Dashboard > Plugins > Webhook\"],\"7Bj3x9\":[\"Failed\"],\"7D50KC\":[[\"label\"],\" disconnected\"],\"7GfM5w\":[\"Recently Viewed\"],\"7L01XJ\":[\"Actions\"],\"7eMo+U\":[\"Go Home\"],\"7p5kLi\":[\"Dashboard\"],\"85eoJ1\":[\"Schedule updated\"],\"8B9E2D\":[\"Day:\"],\"8YwF1J\":[\"Go to <0>Dashboard > Plugins > Webhook</0>\"],\"8ZsakT\":[\"Password\"],\"8tjQCz\":[\"Explore\"],\"8vNtLy\":[\"Paste the Radarr URL above into the List URL field\"],\"8wYDMp\":[\"Already have an account?\"],\"9AE3vb\":[\"Open Plex, go to <0>Settings > Webhooks<1/></0>\"],\"9GW0ZB\":[\"Import is still running in the background. Check back later.\"],\"9NyAH9\":[\"Skipped\"],\"9Xhrps\":[\"Failed to connect\"],\"9ZLGbA\":[\"backups.\"],\"9eF5oV\":[\"Welcome back\"],\"9rG25a\":[\"Server URL\"],\"A+0rLe\":[\"Marked as completed\"],\"AOHgZp\":[\"Episodes\"],\"AOddWK\":[\"Connect \",[\"label\"]],\"ATfxL/\":[\"Trailer\"],\"AVlZoM\":[\"Environment\"],\"Acf6vF\":[\"Save name\"],\"AdoUfN\":[\"Failed to add to watchlist\"],\"AeXO77\":[\"Account\"],\"AjtYj0\":[\"On Watchlist\"],\"Avee+B\":[\"Failed to update name\"],\"B2R3xD\":[\"Toggle scheduled backups\"],\"BEVzjL\":[\"Import failed\"],\"BQnS5I\":[\"Open \",[\"source\"],\" to enter code\"],\"BUnoSB\":[[\"0\"],\" \",[\"1\",\"plural\",{\"one\":[\"backup\"],\"other\":[\"backups\"]}],\" stored\"],\"BpttUI\":[\"Trending Today\"],\"BrrIs8\":[\"Storage\"],\"BskWMl\":[\"Unreachable\"],\"BzEFor\":[\"or\"],\"CGDHFb\":[\"Add a <0>Generic Destination</0> and paste the URL above\"],\"CGExDN\":[\"Failed to purge image cache\"],\"CHeXFE\":[\"Status updated\"],\"CKyk7Q\":[\"Go back\"],\"CaB/+I\":[\"Welcome back, \",[\"name\"]],\"CodnUh\":[\"Removed from library\"],\"CpzGJY\":[\"This may take a few minutes for large libraries. Please don't close this tab.\"],\"CuPxpd\":[\"Requires an active <0>PlexLine truncated
+13
View File
@@ -0,0 +1,13 @@
import { i18n } from "@lingui/core";
import { I18nProvider } from "@lingui/react";
import type { ReactNode } from "react";
import { messages } from "./po/en";
i18n.load("en", messages);
i18n.activate("en");
export function TestI18nProvider({ children }: { children: ReactNode }) {
return <I18nProvider i18n={i18n}>{children}</I18nProvider>;
}
export { i18n as testI18n };
+15
View File
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"skipLibCheck": true,
"noEmit": true,
"isolatedModules": true,
"esModuleInterop": true,
"types": ["bun"]
},
"include": ["src"]
}