From 0768ae5ea2efc86ab861e736c37818496945c4e1 Mon Sep 17 00:00:00 2001 From: 4gray <4gray@users.noreply.github.com> Date: Sat, 3 Oct 2026 16:00:11 +0200 Subject: [PATCH] ci(i18n): fail on new English-identical translations (#1793) * ci(i18n): fail on new English-identical translations The drift check only warned about locale values identical to English, so untranslated strings kept landing. It now fails on any such value that tools/i18n/identical-en-baseline.json does not record for that locale and key. The baseline captures today's 2,015 entries: legitimately identical values (brand and technical names, language autonyms, PIN) and the existing debt. An entry only covers the English text it recorded, so copying reworded English into a locale fails too. Baseline entries that are no longer identical are reported, not fatal. `pnpm run i18n:baseline:update` rewrites the baseline deliberately; CI runs `pnpm run i18n:validate` (node tests, then the check) and never rewrites it. `--fail-on-identical` remains as a strict audit. Co-Authored-By: Claude Opus 5.5 * fix(i18n): keep the baseline intact on failed updates and strict audits `--update-baseline` now writes nothing while any locale is unreadable or has missing or extra keys, so an incomplete translation cannot reshape the baseline. `--fail-on-identical` no longer reads the baseline it ignores, so a damaged file cannot block a strict audit. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 --- .github/workflows/ci.yml | 8 +- docs/architecture/validation-map.md | 26 +- package.json | 3 + tools/i18n/README.md | 22 +- tools/i18n/check-drift.mjs | 364 ++++- tools/i18n/check-drift.test.mjs | 278 ++++ tools/i18n/identical-en-baseline.json | 2053 +++++++++++++++++++++++++ 7 files changed, 2671 insertions(+), 83 deletions(-) create mode 100644 tools/i18n/check-drift.test.mjs create mode 100644 tools/i18n/identical-en-baseline.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f86e9c4be..e952c325e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -390,8 +390,12 @@ jobs: - name: Typecheck Jest spec programs run: pnpm run typecheck:spec:test && pnpm run typecheck:spec - - name: Check i18n drift - run: pnpm run i18n:check + # Fails on missing or extra keys and on values identical to + # English that tools/i18n/identical-en-baseline.json does not list. + # The baseline only changes through a reviewed + # `pnpm run i18n:baseline:update`; CI never rewrites it. + - name: Check i18n drift and untranslated values + run: pnpm run i18n:validate # Node specs for the benchmark and journey harness (about ten # seconds). They live in the E2E app, which the unit coverage scope diff --git a/docs/architecture/validation-map.md b/docs/architecture/validation-map.md index 4d4ed2bf2..c380f3ee4 100644 --- a/docs/architecture/validation-map.md +++ b/docs/architecture/validation-map.md @@ -242,14 +242,32 @@ pnpm run coverage:e2e:v8:web ## I18n ```bash -pnpm run i18n:check +pnpm run i18n:validate # checker unit tests, then the check (CI) +pnpm run i18n:check # the check only +pnpm run i18n:baseline:update # deliberate: rewrite the English-identical baseline ``` The i18n check is non-mutating. It compares every locale file in `apps/web/src/assets/i18n/` against `en.json` and fails on missing or extra keys. -Identical English fallback values are reported as warnings by default; use -`node tools/i18n/check-drift.mjs --fail-on-identical` for a stricter translation -audit. +It also fails on a locale value that is identical to English unless +`tools/i18n/identical-en-baseline.json` records that exact English text for the +same locale and key. The baseline holds values that are legitimately the same +in a language (brand and technical names, language autonyms, loanwords such as +"PIN") and the untranslated debt that existed when the guard was added, so new +keys must ship translated. An entry stops covering its key once the English +text changes, so copying reworded English into a locale fails as well. + +Baseline entries that are no longer English-identical (translated, removed, or +reworded) are reported but do not fail. `pnpm run i18n:baseline:update` +rewrites the baseline from the current locale files, dropping those entries +and printing every added one; it writes nothing while any locale is unreadable +or has missing or extra keys. Run it only after translating, or for a value +that is legitimately identical in that language; review the diff, and never +run it in CI. A new locale starts with no baseline entries, so it has to +record its legitimate identical values the same way. +`node tools/i18n/check-drift.mjs --fail-on-identical` ignores the baseline for +a full translation audit. CI runs `pnpm run i18n:validate` in the unit test +job. ## Performance diff --git a/package.json b/package.json index 7c729b9b1..fcbbbd11d 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,9 @@ "serve:website": "nx serve website", "build:website": "nx build website", "i18n:check": "node tools/i18n/check-drift.mjs", + "i18n:test": "node --test tools/i18n/check-drift.test.mjs", + "i18n:validate": "pnpm run i18n:test && pnpm run i18n:check", + "i18n:baseline:update": "node tools/i18n/check-drift.mjs --update-baseline", "perf:initial-bytes": "node tools/performance/measure-initial-bytes.mjs", "perf:initial-bytes:check": "node tools/performance/measure-initial-bytes.mjs --summary dist/performance/initial-bytes.summary.json && node tools/performance/check-journey-ratchet.mjs --summary dist/performance/initial-bytes.summary.json --only launch/renderer.initialBytes", "perf:journeys": "nx run electron-backend-e2e:journeys", diff --git a/tools/i18n/README.md b/tools/i18n/README.md index c16dcd770..6eb994d37 100644 --- a/tools/i18n/README.md +++ b/tools/i18n/README.md @@ -49,10 +49,24 @@ For a non-mutating CI/agent check, run: pnpm run i18n:check ``` -The check fails on missing or extra keys against `en.json`. It reports values -that are still identical to English as warnings so untranslated fallback strings -remain visible without blocking key-parity validation. For stricter translation -audits, run: +The check fails on missing or extra keys against `en.json`, and on values that +are identical to English unless `identical-en-baseline.json` lists that exact +English text for the locale and key. Do not copy English into a locale as a +placeholder: translate it. The contract, including when an entry stops +covering a key, is in the +[validation map](../../docs/architecture/validation-map.md#i18n). + +Baseline entries that are no longer identical are reported, not fatal. After +translating baselined values, or for a value that is legitimately the same in a +language (a brand, a technical name, a loanword), rewrite the baseline and +review every added entry in the diff: + +```bash +pnpm run i18n:baseline:update +``` + +The checker's own tests run with `pnpm run i18n:test`; CI runs both through +`pnpm run i18n:validate`. For a full audit that ignores the baseline, run: ```bash node tools/i18n/check-drift.mjs --fail-on-identical diff --git a/tools/i18n/check-drift.mjs b/tools/i18n/check-drift.mjs index 7966a58de..e86c82ae4 100644 --- a/tools/i18n/check-drift.mjs +++ b/tools/i18n/check-drift.mjs @@ -1,27 +1,47 @@ #!/usr/bin/env node -import { readdirSync, readFileSync } from 'node:fs'; +/** + * Compares every locale in apps/web/src/assets/i18n/ with en.json. + * + * Missing or extra keys fail. A value identical to English fails too, unless + * tools/i18n/identical-en-baseline.json records that exact English text for + * that locale and key. The baseline holds values that are legitimately the + * same in a language (brands, technical terms, loanwords such as "PIN") and + * the untranslated debt that existed when the guard was introduced, so new + * keys must ship translated while old debt can be paid down. + * + * Baseline entries that no longer match an English-identical value are only + * reported. `--update-baseline` rewrites the baseline from the current files; + * run it deliberately and review the diff. CI never runs it. + * `--fail-on-identical` ignores the baseline for a strict audit. + */ +import { readdirSync, readFileSync, writeFileSync } from 'node:fs'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); -const REPO_ROOT = resolve(__dirname, '..', '..'); -const I18N_DIR = resolve(REPO_ROOT, 'apps/web/src/assets/i18n'); -const EN_LOCALE = 'en.json'; +export const I18N_DIR = resolve(__dirname, '../../apps/web/src/assets/i18n'); +export const BASELINE_PATH = resolve(__dirname, 'identical-en-baseline.json'); +const EN_LOCALE = 'en'; const MAX_EXAMPLES = 5; -const FAIL_ON_IDENTICAL = process.argv.includes('--fail-on-identical'); +const UPDATE_COMMAND = 'pnpm run i18n:baseline:update'; +const FLAGS = new Set(['--fail-on-identical', '--update-baseline', '--']); function readJson(filePath) { return JSON.parse(readFileSync(filePath, 'utf8')); } -function collectLeaves(value, prefix = '', leaves = new Map()) { - if (!value || typeof value !== 'object' || Array.isArray(value)) { +function isPlainObject(value) { + return !!value && typeof value === 'object' && !Array.isArray(value); +} + +export function collectLeaves(value, prefix = '', leaves = new Map()) { + if (!isPlainObject(value)) { throw new Error(`Expected object at ${prefix || ''}`); } for (const [key, entry] of Object.entries(value)) { const path = prefix ? `${prefix}.${key}` : key; - if (entry && typeof entry === 'object' && !Array.isArray(entry)) { + if (isPlainObject(entry)) { collectLeaves(entry, path, leaves); } else { leaves.set(path, entry); @@ -31,10 +51,87 @@ function collectLeaves(value, prefix = '', leaves = new Map()) { return leaves; } -function listLocaleFiles() { - return readdirSync(I18N_DIR) - .filter((file) => file.endsWith('.json')) - .sort(); +/** Keys whose non-empty locale string equals the English string. */ +export function findIdenticalToEnglish(enLeaves, localeLeaves) { + const identical = new Map(); + for (const [key, enValue] of enLeaves) { + const localeValue = localeLeaves.get(key); + if ( + typeof enValue === 'string' && + localeValue === enValue && + enValue.trim().length > 0 + ) { + identical.set(key, enValue); + } + } + return identical; +} + +/** + * Splits a locale's English-identical keys into baselined and new ones. An + * entry covers a key only while its recorded text is still the English text, + * so rewording English and copying the new text into a locale is new debt. + * Stale entries no longer describe an English-identical value: the key was + * translated or removed, or its English text changed. + */ +export function compareWithBaseline(identical, localeBaseline = new Map()) { + const baselined = []; + const unbaselined = []; + for (const [key, value] of identical) { + (localeBaseline.get(key) === value ? baselined : unbaselined).push(key); + } + const stale = [...localeBaseline] + .filter(([key, value]) => identical.get(key) !== value) + .map(([key]) => key); + return { baselined, unbaselined, stale }; +} + +/** Validates the baseline JSON into Map>. */ +export function parseBaseline(raw) { + if (!isPlainObject(raw)) { + throw new Error('expected an object of locales'); + } + const baseline = new Map(); + for (const [locale, entries] of Object.entries(raw)) { + if (!isPlainObject(entries)) { + throw new Error(`expected an object of keys for ${locale}`); + } + for (const [key, value] of Object.entries(entries)) { + if (typeof value !== 'string') { + throw new Error(`expected English text at ${locale} ${key}`); + } + } + baseline.set(locale, new Map(Object.entries(entries))); + } + return baseline; +} + +/** Serialises Map> with locales and keys sorted. */ +export function serializeBaseline(identicalByLocale) { + const byCodeUnit = (a, b) => (a < b ? -1 : a > b ? 1 : 0); + const output = {}; + for (const locale of [...identicalByLocale.keys()].sort(byCodeUnit)) { + const entries = identicalByLocale.get(locale); + if (entries.size > 0) { + output[locale] = Object.fromEntries( + [...entries].sort(([a], [b]) => byCodeUnit(a, b)) + ); + } + } + return `${JSON.stringify(output, null, 4)}\n`; +} + +function countChanges(from, to, onChange = () => undefined) { + let changes = 0; + for (const [locale, entries] of from) { + for (const [key, value] of entries) { + if (to.get(locale)?.get(key) !== value) { + changes += 1; + onChange(locale, key, value); + } + } + } + return changes; } function formatExamples(values) { @@ -50,73 +147,194 @@ function formatError(error) { return error instanceof Error ? error.message : String(error); } -let enLeaves; -try { - enLeaves = collectLeaves(readJson(resolve(I18N_DIR, EN_LOCALE))); -} catch (error) { - console.error(`FAIL ${EN_LOCALE}: ${formatError(error)}`); - process.exit(1); -} - -const enKeys = [...enLeaves.keys()]; -let failed = false; -let identicalTotal = 0; - -for (const localeFile of listLocaleFiles()) { - if (localeFile === EN_LOCALE) { - continue; - } - +function readBaseline(baselinePath) { + let raw; try { - const localeLeaves = collectLeaves( - readJson(resolve(I18N_DIR, localeFile)) - ); - const localeKeys = [...localeLeaves.keys()]; - const missing = enKeys.filter((key) => !localeLeaves.has(key)); - const extra = localeKeys.filter((key) => !enLeaves.has(key)); - const identical = enKeys.filter((key) => { - if (!localeLeaves.has(key)) { - return false; - } - - const localeValue = localeLeaves.get(key); - const enValue = enLeaves.get(key); - - return ( - typeof localeValue === 'string' && - typeof enValue === 'string' && - localeValue.trim().length > 0 && - localeValue === enValue - ); - }); - - identicalTotal += identical.length; - const localeFailed = - missing.length > 0 || - extra.length > 0 || - (FAIL_ON_IDENTICAL && identical.length > 0); - failed = failed || localeFailed; - - const status = localeFailed ? 'FAIL' : 'ok'; - console.log( - `${status} ${localeFile}: missing=${missing.length}${formatExamples( - missing - )} extra=${extra.length}${formatExamples(extra)} identical_en=${ - identical.length - }${formatExamples(identical)}` - ); + raw = readJson(baselinePath); } catch (error) { - failed = true; - console.log(`FAIL ${localeFile}: ${formatError(error)}`); + if (error?.code === 'ENOENT') { + return new Map(); + } + throw new Error(`${baselinePath}: ${formatError(error)}`); + } + try { + return parseBaseline(raw); + } catch (error) { + throw new Error(`${baselinePath}: ${formatError(error)}`); } } -if (identicalTotal > 0 && !FAIL_ON_IDENTICAL) { - console.log( - `warn identical_en=${identicalTotal}; pass --fail-on-identical to make English fallback values fatal.` +function listLocales(i18nDir) { + return readdirSync(i18nDir) + .filter((file) => file.endsWith('.json')) + .map((file) => file.slice(0, -'.json'.length)) + .filter((locale) => locale !== EN_LOCALE) + .sort(); +} + +function checkLocale({ i18nDir, locale, enLeaves, baseline, mode }) { + const localeLeaves = collectLeaves( + readJson(resolve(i18nDir, `${locale}.json`)) + ); + const missing = [...enLeaves.keys()].filter( + (key) => !localeLeaves.has(key) + ); + const extra = [...localeLeaves.keys()].filter((key) => !enLeaves.has(key)); + const identical = findIdenticalToEnglish(enLeaves, localeLeaves); + const { baselined, unbaselined, stale } = compareWithBaseline( + identical, + baseline.get(locale) + ); + const rejected = + mode === 'strict' + ? [...identical.keys()] + : mode === 'update' + ? [] + : unbaselined; + return { missing, extra, identical, baselined, rejected, stale }; +} + +function reportLocale(log, file, result, mode) { + const { missing, extra, identical, baselined, rejected, stale } = result; + const failed = missing.length + extra.length + rejected.length > 0; + const baselineNote = + mode === 'baseline' ? ` baselined=${baselined.length}` : ''; + log( + `${failed ? 'FAIL' : 'ok'} ${file}: missing=${ + missing.length + }${formatExamples(missing)} extra=${extra.length}${formatExamples( + extra + )} identical_en=${identical.size}${baselineNote}` + ); + if (mode === 'baseline' && stale.length > 0) { + log( + `note ${file}: ${stale.length} baseline entries are no longer English-identical${formatExamples( + stale + )}` + ); + } + return failed; +} + +function reportRejected(log, rejected, enLeaves, mode) { + if (rejected.length === 0) { + return; + } + const scope = mode === 'strict' ? '' : ' and are not in the baseline'; + log(`${rejected.length} value(s) equal English${scope}:`); + for (const [file, key] of rejected) { + log(` ${file} ${key}: ${JSON.stringify(enLeaves.get(key))}`); + } + if (mode === 'baseline') { + log( + `Translate them (see the i18n-fill skill). Only if a value is legitimately the same in that language, run \`${UPDATE_COMMAND}\` and commit the reviewed baseline.` + ); + } +} + +function writeBaseline(log, baselinePath, previous, identicalByLocale) { + const added = countChanges( + identicalByLocale, + previous, + (locale, key, value) => + log(` + ${locale} ${key}: ${JSON.stringify(value)}`) + ); + const removed = countChanges(previous, identicalByLocale); + writeFileSync(baselinePath, serializeBaseline(identicalByLocale)); + log( + `Baseline written: ${added} added, ${removed} removed. Review every added entry before committing.` ); } -if (failed) { - process.exitCode = 1; +/** Runs the check and returns the process exit code. */ +export function run({ + i18nDir = I18N_DIR, + baselinePath = BASELINE_PATH, + argv = [], + log = console.log, +} = {}) { + const strict = argv.includes('--fail-on-identical'); + const update = argv.includes('--update-baseline'); + if (argv.some((arg) => !FLAGS.has(arg)) || (strict && update)) { + log( + `FAIL unsupported arguments: ${argv.join(' ')}; pass at most one of --fail-on-identical and --update-baseline.` + ); + return 1; + } + const mode = strict ? 'strict' : update ? 'update' : 'baseline'; + + let enLeaves; + let baseline; + try { + enLeaves = collectLeaves( + readJson(resolve(i18nDir, `${EN_LOCALE}.json`)) + ); + // A strict audit ignores the baseline, so a damaged file cannot block it. + baseline = strict ? new Map() : readBaseline(baselinePath); + } catch (error) { + if (!enLeaves || !update) { + log(`FAIL ${formatError(error)}`); + return 1; + } + log(`note replacing unreadable baseline: ${formatError(error)}`); + baseline = new Map(); + } + + const locales = listLocales(i18nDir); + const identicalByLocale = new Map(); + const rejected = []; + let failed = false; + let staleTotal = 0; + + for (const locale of locales) { + const file = `${locale}.json`; + let result; + try { + result = checkLocale({ i18nDir, locale, enLeaves, baseline, mode }); + } catch (error) { + failed = true; + log(`FAIL ${file}: ${formatError(error)}`); + continue; + } + identicalByLocale.set(locale, result.identical); + rejected.push(...result.rejected.map((key) => [file, key])); + staleTotal += result.stale.length; + failed = reportLocale(log, file, result, mode) || failed; + } + + if (mode === 'baseline') { + for (const [locale, entries] of baseline) { + if (!locales.includes(locale)) { + staleTotal += entries.size; + log(`note ${locale}: baseline lists a locale with no file`); + } + } + if (staleTotal > 0) { + log( + `note ${staleTotal} stale baseline entries; run \`${UPDATE_COMMAND}\` to shrink the baseline.` + ); + } + } + reportRejected(log, rejected, enLeaves, mode); + + if (update) { + // Only a complete, readable set of locales is a state worth recording. + if (failed) { + log( + 'FAIL baseline not written: fix the unreadable, missing or extra keys above first.' + ); + return 1; + } + writeBaseline(log, baselinePath, baseline, identicalByLocale); + } + + return failed ? 1 : 0; +} + +const isMain = + process.argv[1] && + resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url)); + +if (isMain) { + process.exitCode = run({ argv: process.argv.slice(2) }); } diff --git a/tools/i18n/check-drift.test.mjs b/tools/i18n/check-drift.test.mjs new file mode 100644 index 000000000..45f249666 --- /dev/null +++ b/tools/i18n/check-drift.test.mjs @@ -0,0 +1,278 @@ +import assert from 'node:assert/strict'; +import { + mkdirSync, + mkdtempSync, + readFileSync, + rmSync, + writeFileSync, +} from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { afterEach, beforeEach, test } from 'node:test'; + +import { parseBaseline, run, serializeBaseline } from './check-drift.mjs'; + +const EN = { + APP: { TITLE: 'IPTVnator', SAVE: 'Save' }, + PIN: 'PIN', +}; + +let root; +let dir; +let baselinePath; + +beforeEach(() => { + root = mkdtempSync(path.join(os.tmpdir(), 'i18n-drift-')); + dir = path.join(root, 'i18n'); + mkdirSync(dir); + baselinePath = path.join(root, 'identical-en-baseline.json'); +}); + +afterEach(() => { + rmSync(root, { recursive: true, force: true }); +}); + +function writeLocale(locale, content) { + writeFileSync(path.join(dir, `${locale}.json`), JSON.stringify(content)); +} + +function writeBaseline(content) { + writeFileSync(baselinePath, JSON.stringify(content)); +} + +function check(...argv) { + const lines = []; + const code = run({ + i18nDir: dir, + baselinePath, + argv, + log: (line) => lines.push(line), + }); + return { code, output: lines.join('\n') }; +} + +function readBaselineFile() { + return JSON.parse(readFileSync(baselinePath, 'utf8')); +} + +test('fails on a new English-identical key that the baseline does not list', () => { + writeLocale('en', { ...EN, APP: { ...EN.APP, NEW: 'New label' } }); + writeLocale('de', { + APP: { TITLE: 'IPTVnator', SAVE: 'Speichern', NEW: 'New label' }, + PIN: 'PIN', + }); + writeBaseline({ de: { 'APP.TITLE': 'IPTVnator', PIN: 'PIN' } }); + + const { code, output } = check(); + + assert.equal(code, 1); + assert.match(output, /^FAIL de\.json: .*identical_en=3 baselined=2$/m); + assert.match(output, /de\.json APP\.NEW: "New label"/); +}); + +test('passes a translated key', () => { + writeLocale('en', EN); + writeLocale('de', { + APP: { TITLE: 'IPTVnator', SAVE: 'Speichern' }, + PIN: 'PIN-Code', + }); + writeBaseline({ de: { 'APP.TITLE': 'IPTVnator' } }); + + const { code, output } = check(); + + assert.equal(code, 0); + assert.match(output, /^ok de\.json: .*identical_en=1 baselined=1$/m); +}); + +test('passes an English-identical key listed in the baseline', () => { + writeLocale('en', EN); + writeLocale('de', { + APP: { TITLE: 'IPTVnator', SAVE: 'Speichern' }, + PIN: 'PIN', + }); + writeBaseline({ de: { 'APP.TITLE': 'IPTVnator', PIN: 'PIN' } }); + + assert.equal(check().code, 0); +}); + +test('a baseline entry only covers the English text it recorded', () => { + // English was reworded and the new text copied into the locale: that is + // new untranslated text, not the value the baseline accepted. + writeLocale('en', { ...EN, APP: { ...EN.APP, SAVE: 'Save changes' } }); + writeLocale('de', { + APP: { TITLE: 'IPTVnator', SAVE: 'Save changes' }, + PIN: 'PIN-Code', + }); + writeBaseline({ de: { 'APP.TITLE': 'IPTVnator', 'APP.SAVE': 'Save' } }); + + const { code, output } = check(); + + assert.equal(code, 1); + assert.match(output, /de\.json APP\.SAVE: "Save changes"/); +}); + +test('reports baseline entries that are no longer identical without failing', () => { + writeLocale('en', EN); + writeLocale('de', { + APP: { TITLE: 'IPTVnator', SAVE: 'Speichern' }, + PIN: 'PIN-Code', + }); + writeBaseline({ + de: { 'APP.TITLE': 'IPTVnator', 'APP.SAVE': 'Save', PIN: 'PIN' }, + fr: { PIN: 'PIN' }, + }); + + const { code, output } = check(); + + assert.equal(code, 0); + assert.match( + output, + /^note de\.json: 2 baseline entries .*\(APP\.SAVE, PIN\)$/m + ); + assert.match(output, /^note fr: baseline lists a locale with no file$/m); + assert.match( + output, + /^note 3 stale baseline entries; run .*i18n:baseline:update/m + ); +}); + +test('a locale without baseline entries fails on every identical value', () => { + writeLocale('en', EN); + writeLocale('uk', { + APP: { TITLE: 'IPTVnator', SAVE: 'Зберегти' }, + PIN: 'PIN-код', + }); + + const { code, output } = check(); + + assert.equal(code, 1); + assert.match(output, /uk\.json APP\.TITLE: "IPTVnator"/); +}); + +test('still fails on missing and extra keys', () => { + writeLocale('en', EN); + writeLocale('de', { APP: { TITLE: 'IPTVnator', OLD: 'Alt' }, PIN: 'PIN' }); + writeBaseline({ de: { 'APP.TITLE': 'IPTVnator', PIN: 'PIN' } }); + + const { code, output } = check(); + + assert.equal(code, 1); + assert.match( + output, + /^FAIL de\.json: missing=1 \(APP\.SAVE\) extra=1 \(APP\.OLD\)/m + ); +}); + +test('--fail-on-identical ignores the baseline', () => { + writeLocale('en', EN); + writeLocale('de', { + APP: { TITLE: 'IPTVnator', SAVE: 'Speichern' }, + PIN: 'PIN', + }); + writeBaseline({ de: { 'APP.TITLE': 'IPTVnator', PIN: 'PIN' } }); + + const { code, output } = check('--fail-on-identical'); + + assert.equal(code, 1); + assert.match(output, /de\.json APP\.TITLE: "IPTVnator"/); + assert.match(output, /de\.json PIN: "PIN"/); +}); + +test('--update-baseline records current identical values and drops resolved ones', () => { + writeLocale('en', EN); + writeLocale('de', { + APP: { TITLE: 'IPTVnator', SAVE: 'Speichern' }, + PIN: 'PIN', + }); + writeLocale('fr', { + APP: { TITLE: 'IPTVnator', SAVE: 'Enregistrer' }, + PIN: 'Code PIN', + }); + writeBaseline({ de: { 'APP.SAVE': 'Save' }, fr: { PIN: 'PIN' } }); + + const { code, output } = check('--update-baseline'); + + assert.equal(code, 0); + assert.match(output, /^ {2}\+ de PIN: "PIN"$/m); + assert.match(output, /Baseline written: 3 added, 2 removed\./); + assert.deepEqual(readBaselineFile(), { + de: { 'APP.TITLE': 'IPTVnator', PIN: 'PIN' }, + fr: { 'APP.TITLE': 'IPTVnator' }, + }); + assert.equal(check().code, 0); +}); + +test('--update-baseline keeps the old baseline when a locale is unreadable', () => { + writeLocale('en', EN); + writeFileSync(path.join(dir, 'de.json'), '{ broken'); + writeBaseline({ de: { PIN: 'PIN' } }); + + const { code, output } = check('--update-baseline'); + + assert.equal(code, 1); + assert.match(output, /baseline not written/); + assert.deepEqual(readBaselineFile(), { de: { PIN: 'PIN' } }); +}); + +test('--update-baseline keeps the old baseline while keys are missing or extra', () => { + writeLocale('en', EN); + writeLocale('de', { APP: { TITLE: 'IPTVnator', OLD: 'Alt' }, PIN: 'PIN' }); + writeBaseline({ de: { 'APP.SAVE': 'Save', PIN: 'PIN' } }); + + const { code, output } = check('--update-baseline'); + + assert.equal(code, 1); + assert.match(output, /baseline not written/); + assert.deepEqual(readBaselineFile(), { + de: { 'APP.SAVE': 'Save', PIN: 'PIN' }, + }); +}); + +test('--fail-on-identical audits even when the baseline is damaged', () => { + writeLocale('en', EN); + writeLocale('de', { + APP: { TITLE: 'IPTVnator', SAVE: 'Speichern' }, + PIN: 'PIN-Code', + }); + writeFileSync(baselinePath, '{ broken'); + + const { code, output } = check('--fail-on-identical'); + + assert.equal(code, 1); + assert.match(output, /^FAIL de\.json: missing=0 extra=0 identical_en=1$/m); + assert.match(output, /de\.json APP\.TITLE: "IPTVnator"/); + assert.equal(check().code, 1); +}); + +test('rejects unknown and conflicting flags', () => { + writeLocale('en', EN); + + assert.equal(check('--update').code, 1); + assert.equal(check('--fail-on-identical', '--update-baseline').code, 1); +}); + +test('rejects a malformed baseline', () => { + assert.throws(() => parseBaseline({ de: ['PIN'] }), /object of keys/); + assert.throws(() => parseBaseline({ de: { PIN: 1 } }), /English text/); +}); + +test('serialises locales and keys in a stable order', () => { + const serialized = serializeBaseline( + new Map([ + [ + 'fr', + new Map([ + ['B', 'b'], + ['A', 'a'], + ]), + ], + ['de', new Map()], + ['ar', new Map([['Z', 'z']])], + ]) + ); + + assert.equal( + serialized, + '{\n "ar": {\n "Z": "z"\n },\n "fr": {\n "A": "a",\n "B": "b"\n }\n}\n' + ); +}); diff --git a/tools/i18n/identical-en-baseline.json b/tools/i18n/identical-en-baseline.json new file mode 100644 index 000000000..457d73f5c --- /dev/null +++ b/tools/i18n/identical-en-baseline.json @@ -0,0 +1,2053 @@ +{ + "ar": { + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TITLE": "IPTVnator", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.STALKER_PORTAL": "Stalker Portal", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "WORKSPACE.SHELL.STALKER_PORTAL": "Stalker Portal", + "WORKSPACE.SHELL.XTREAM_CODE": "Xtream Code", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.URL": "URL" + }, + "ary": { + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_ID": "TVG ID", + "CHANNELS.DETAILS_DIALOG.TVG_LOGO": "TVG logo", + "CHANNELS.DETAILS_DIALOG.TVG_NAME": "TVG name", + "CHANNELS.DETAILS_DIALOG.TVG_REC": "TVG rec", + "CHANNELS.DETAILS_DIALOG.TVG_URL": "TVG URL", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_STALKER": "Stalker Portal", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TITLE": "IPTVnator", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "WORKSPACE.CONTEXT.STALKER_PORTAL": "Stalker portal", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.STALKER_PORTAL": "Stalker Portal", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "WORKSPACE.SHELL.STALKER_PORTAL": "Stalker Portal", + "WORKSPACE.SHELL.XTREAM_CODE": "Xtream Code", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.URL": "URL", + "XTREAM.DETAIL_FALLBACK.STREAM_ID": "Stream ID" + }, + "by": { + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_ID": "TVG ID", + "CHANNELS.DETAILS_DIALOG.TVG_REC": "TVG rec", + "CHANNELS.DETAILS_DIALOG.TVG_URL": "TVG URL", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "HOME.ADD_PLAYLIST.METHOD_URL_LABEL": "M3U URL", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TITLE": "IPTVnator", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "PARENTAL_LOCK.PIN_DIALOG.PIN_LABEL": "PIN", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "WORKSPACE.SHELL.XTREAM_CODE": "Xtream Code", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.URL": "URL" + }, + "de": { + "ABOUT.VERSION": "Version", + "AUDIO_PLAYER.PAUSE": "Pause", + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.NAME": "Name", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "DOWNLOADS.TITLE": "Downloads", + "EMBEDDED_MPV.PLAYER.AUDIO_TRACK_FALLBACK": "Audio {{index}}", + "EMBEDDED_MPV.PLAYER.PAUSE": "Pause", + "EMBEDDED_MPV.PLAYER.STATS_AUDIO": "Audio", + "EMBEDDED_MPV.PLAYER.STATS_CONTAINER": "Container", + "EMBEDDED_MPV.PLAYER.STATS_VIDEO": "Video", + "EMBEDDED_MPV.PLAYER.SUBTITLE_COLOR_CYAN": "Cyan", + "EPG.GUIDE.ZOOM": "Zoom", + "EPG.LIVE_STREAM": "Live stream", + "EPG.PROGRAM_DIALOG.EPISODE": "Episode", + "EPG.TIMELINE.ZOOM": "Zoom", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_STALKER": "Stalker Portal", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLISTS.INFO_DIALOG.AUTO_UPDATE": "Auto-update", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.SORT_OPTIONS.NAME_ASC": "Name (A-Z)", + "HOME.SORT_OPTIONS.NAME_DESC": "Name (Z-A)", + "HOME.TITLE": "IPTVnator", + "HOME.XTREAM_PLAYLIST.SERVER_URL": "Server URL", + "INFORMATION": "Information", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "PARENTAL_LOCK.PIN_DIALOG.PIN_LABEL": "PIN", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "PORTALS.SIDEBAR.LIVE_TV": "Live TV", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.LINK_BLOG": "Blog", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.NAV_REMOTE": "Remote", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SETTINGS.PORT_RANGE_LABEL": "Port (1-65535)", + "SETTINGS.VERSION": "Version", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "STALKER.ACCOUNT_INFO.LOGIN": "Login", + "STALKER.ACCOUNT_INFO.PORTAL_INFO": "Portal", + "STALKER.ACCOUNT_INFO.STATUS": "Status", + "THEMES.SYSTEM_THEME": "System", + "WORKSPACE.DASHBOARD.HERO_DETAILS": "Details", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.TYPE_LIVE": "Live", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.RAIL_DASHBOARD": "Dashboard", + "WORKSPACE.SHELL.RAIL_DOWNLOADS": "Downloads", + "WORKSPACE.SHELL.XTREAM_CODE": "Xtream Code", + "WORKSPACE.SORT_NAME_ASC": "Name A-Z", + "WORKSPACE.SORT_NAME_DESC": "Name Z-A", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.PORTS": "Ports", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.STATUS": "Status", + "XTREAM.ACCOUNT_INFO.URL": "URL", + "XTREAM.DETAIL_FALLBACK.CONTAINER": "Format", + "XTREAM.GENRE": "Genre", + "XTREAM.SERIES_STATUS_PILOT": "Pilot" + }, + "el": { + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_ID": "TVG ID", + "CHANNELS.DETAILS_DIALOG.TVG_REC": "TVG rec", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "HOME.ADD_PLAYLIST.METHOD_URL_LABEL": "M3U URL", + "HOME.FILE_UPLOAD.OR": "or", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_STALKER": "Stalker Portal", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLISTS.INFO_DIALOG.MAC_ADDRESS": "Mac Address", + "HOME.PLAYLISTS.INFO_DIALOG.TITLE": "Title", + "HOME.PLAYLISTS.STALKER_PORTAL": "Stalker portal", + "HOME.PLAYLISTS.XTREAM_PLAYLIST": "Xtream playlist", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.STALKER_PORTAL.MAC_ADDRESS": "Mac Address", + "HOME.TITLE": "IPTVnator", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "PARENTAL_LOCK.PIN_DIALOG.PIN_LABEL": "PIN", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.LINK_BLOG": "Blog", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_MPV": "MPV player", + "SETTINGS.PLAYER_VLC": "VLC", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "WORKSPACE.CONTEXT.STALKER_PORTAL": "Stalker portal", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.STALKER_PORTAL": "Stalker Portal", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "WORKSPACE.SHELL.STALKER_PORTAL": "Stalker Portal", + "WORKSPACE.SHELL.XTREAM_CODE": "Xtream Code", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.YOUTUBE_TRAILER": "YouTube Trailer" + }, + "es": { + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_ID": "TVG ID", + "CHANNELS.DETAILS_DIALOG.TVG_REC": "TVG rec", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "EMBEDDED_MPV.PLAYER.AUDIO_TRACK_FALLBACK": "Audio {{index}}", + "EMBEDDED_MPV.PLAYER.STATS_AUDIO": "Audio", + "EPG.GUIDE.ZOOM": "Zoom", + "EPG.TIMELINE.MINUTES_SHORT": "min", + "EPG.TIMELINE.ZOOM": "Zoom", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TITLE": "IPTVnator", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "NO": "No", + "PARENTAL_LOCK.PIN_DIALOG.PIN_LABEL": "PIN", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "PORTALS.SERIES": "Series", + "PORTALS.SIDEBAR.SERIES": "Series", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.GENERAL": "General", + "SETTINGS.LINK_BLOG": "Blog", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.NAV_GENERAL": "General", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "STALKER.ACCOUNT_INFO.PORTAL_INFO": "Portal", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.STALKER_PORTAL": "Stalker Portal", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.COMMANDS.GO_TO_SERIES_LABEL": "Series", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "WORKSPACE.SHELL.RAIL_SERIES": "Series", + "WORKSPACE.SHELL.STALKER_PORTAL": "Stalker Portal", + "WORKSPACE.SHELL.XTREAM_CODE": "Xtream Code", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.NO": "No", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.URL": "URL", + "XTREAM.CREW_JOB_DIRECTOR": "Director", + "XTREAM.DIRECTOR": "Director", + "XTREAM.DISCOVER_SERIES": "Series" + }, + "fr": { + "ABOUT.VERSION": "Version", + "AUDIO_PLAYER.PAUSE": "Pause", + "AUDIO_PLAYER.VOLUME": "Volume", + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_ID": "TVG ID", + "CHANNELS.DETAILS_DIALOG.TVG_REC": "TVG rec", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "EMBEDDED_MPV.PLAYER.AUDIO_TRACK_FALLBACK": "Audio {{index}}", + "EMBEDDED_MPV.PLAYER.PAUSE": "Pause", + "EMBEDDED_MPV.PLAYER.QUALITY_AUTO": "Auto", + "EMBEDDED_MPV.PLAYER.STATS_AUDIO": "Audio", + "EMBEDDED_MPV.PLAYER.SUBTITLE_COLOR_CYAN": "Cyan", + "EMBEDDED_MPV.PLAYER.VOLUME": "Volume", + "EPG.GUIDE.KEYS.PROGRAMME": "programme", + "EPG.GUIDE.ZOOM": "Zoom", + "EPG.PROGRAM_DIALOG.DESCRIPTION": "Description", + "EPG.TIMELINE.MINUTES_SHORT": "min", + "EPG.TIMELINE.ZOOM": "Zoom", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TITLE": "IPTVnator", + "INFORMATION": "Information", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "PORTALS.MULTI_SOURCE.SOURCE": "Source", + "PORTALS.MULTI_SOURCE.SOURCES": "Sources", + "PORTALS.PAGE": "Page", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.LINK_BLOG": "Blog", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SETTINGS.PORT_RANGE_LABEL": "Port (1-65535)", + "SETTINGS.SOURCE_PLURAL": "sources", + "SETTINGS.SOURCE_SINGULAR": "source", + "SETTINGS.VERSION": "Version", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.SEASON_EPISODE_BADGE": "S{{season}}·E{{episode}}", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "WORKSPACE.SHELL.RAIL_SOURCES": "Sources", + "WORKSPACE.SHELL.XTREAM_CODE": "Xtream Code", + "WORKSPACE.SOURCES.TITLE": "Sources", + "WORKSPACE.SOURCES.TYPE": "Type", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.PORTS": "Ports", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.URL": "URL", + "XTREAM.DESCRIPTION": "Description", + "XTREAM.DETAIL_FALLBACK.CONTAINER": "Format", + "XTREAM.GENRE": "Genre" + }, + "hu": { + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_REC": "TVG rec", + "CHANNELS.MAP_EPG": "Map EPG channel", + "DOWNLOADS.ACTION_FAILED": "Action failed", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "DOWNLOADS.PAUSE": "Pause", + "DOWNLOADS.RESUME": "Resume", + "DOWNLOADS.STATUS.PAUSED": "Paused", + "EMBEDDED_MPV.PLAYER.BACK": "Back", + "EPG_MAPPING_DIALOG.CLOSE": "Close", + "EPG_MAPPING_DIALOG.CURRENT_MAPPING": "Current mapping", + "EPG_MAPPING_DIALOG.NO_MAPPING": "No EPG mapping set", + "EPG_MAPPING_DIALOG.NO_RESULTS": "No EPG channels found", + "EPG_MAPPING_DIALOG.REMOVE": "Remove mapping", + "EPG_MAPPING_DIALOG.REMOVED": "EPG mapping removed", + "EPG_MAPPING_DIALOG.SAVE": "Save mapping", + "EPG_MAPPING_DIALOG.SAVED": "EPG mapping saved", + "EPG_MAPPING_DIALOG.SAVE_FAILED": "Could not update the EPG mapping", + "EPG_MAPPING_DIALOG.SEARCH_HINT": "Type at least {{min}} characters to search", + "EPG_MAPPING_DIALOG.SEARCH_PLACEHOLDER": "Search EPG channels...", + "EPG_MAPPING_DIALOG.SUBTITLE": "Manually select an EPG channel for \"{{channelName}}\"", + "EPG_MAPPING_DIALOG.TITLE": "Map EPG Channel", + "HOME.PLAYLISTS.AUTO_REFRESH_UPDATE_FAILED": "Playlist auto-refresh failed. None of the {{total}} playlists could be updated.", + "HOME.PLAYLISTS.AUTO_REFRESH_UPDATE_PARTIAL": "Playlist auto-refresh incomplete: {{updated}} of {{total}} playlists updated, {{failed}} failed.", + "HOME.PLAYLISTS.AUTO_REFRESH_UPDATE_PARTIAL_WITH_SKIPPED": "Playlist auto-refresh incomplete: {{updated}} of {{total}} playlists updated, {{failed}} failed, {{skipped}} skipped (no URL or file path).", + "HOME.PLAYLISTS.AUTO_REFRESH_UPDATE_SKIPPED": "Playlist auto-refresh: {{updated}} of {{total}} playlists updated, {{skipped}} skipped (no URL or file path).", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TITLE": "IPTVnator", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.ITEM": "item", + "PORTALS.ITEMS": "items", + "PORTALS.LOADING_ALL_CHANNELS": "Loading full channel list…", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "PORTALS.NO_CHANNELS_IN_CATEGORY": "No channels in this category", + "PORTALS.REFRESH_CHANNEL_LIST": "Refresh channel list", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.LINK_BLOG": "Blog", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.PLAYER_AMBIENT_MODE": "Ambient background fill", + "SETTINGS.PLAYER_AMBIENT_MODE_DESCRIPTION": "Fill the space around the player with a blurred, dimmed poster instead of plain black bars.", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.CREW_JOB_CREATOR": "Creator", + "XTREAM.CREW_JOB_DIRECTOR": "Director", + "XTREAM.SERIES_STATUS_PILOT": "Pilot" + }, + "it": { + "AUDIO_PLAYER.VOLUME": "Volume", + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_ID": "TVG ID", + "CHANNELS.DETAILS_DIALOG.TVG_REC": "TVG rec", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "EMBEDDED_MPV.PLAYER.AUDIO_TRACK_FALLBACK": "Audio {{index}}", + "EMBEDDED_MPV.PLAYER.STATS_AUDIO": "Audio", + "EMBEDDED_MPV.PLAYER.STATS_BUFFER": "Buffer", + "EMBEDDED_MPV.PLAYER.STATS_VIDEO": "Video", + "EMBEDDED_MPV.PLAYER.VOLUME": "Volume", + "EMBEDDED_MPV.PLAYER.VOLUME_LABEL": "Volume {{percent}}%", + "EPG.GUIDE.ZOOM": "Zoom", + "EPG.TIMELINE.MINUTES_SHORT": "min", + "EPG.TIMELINE.ZOOM": "Zoom", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.INFO_DIALOG.PASSWORD": "Password", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TITLE": "IPTVnator", + "HOME.XTREAM_PLAYLIST.PASSWORD": "Password", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "NO": "No", + "PARENTAL_LOCK.PIN_DIALOG.PIN_LABEL": "PIN", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.ERROR_VIEW.HOME": "Home", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.EPG_VIEW_MODE_TIMELINE": "Timeline", + "SETTINGS.LINK_BLOG": "Blog", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.NAV_REMOTE": "Remote", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "STALKER.ACCOUNT_INFO.ACCOUNT": "Account", + "STALKER.ACCOUNT_INFO.LOGIN": "Login", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.SEASON_EPISODE_BADGE": "S{{season}}·E{{episode}}", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.TYPE_LIVE": "Live", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "WORKSPACE.SHELL.RAIL_DASHBOARD": "Dashboard", + "WORKSPACE.SOURCES.PLAYLIST_COUNT_ONE": "1 playlist", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.NO": "No", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.URL": "URL" + }, + "ja": { + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_ID": "TVG ID", + "CHANNELS.DETAILS_DIALOG.TVG_LOGO": "TVG logo", + "CHANNELS.DETAILS_DIALOG.TVG_NAME": "TVG name", + "CHANNELS.DETAILS_DIALOG.TVG_REC": "TVG rec", + "CHANNELS.DETAILS_DIALOG.TVG_URL": "TVG URL", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "HOME.ADD_PLAYLIST.METHOD_URL_LABEL": "M3U URL", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TITLE": "IPTVnator", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "PARENTAL_LOCK.PIN_DIALOG.PIN_LABEL": "PIN", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "WORKSPACE.SHELL.XTREAM_CODE": "Xtream Code", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.URL": "URL" + }, + "ko": { + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_ID": "TVG ID", + "CHANNELS.DETAILS_DIALOG.TVG_REC": "TVG rec", + "CHANNELS.DETAILS_DIALOG.TVG_URL": "TVG URL", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "EPG.DOWNLOAD_SUCCESS": "EPG was successfully downloaded.", + "EPG.ERROR": "Ooops, EPG could not be loaded.", + "EPG.LIVE_STREAM": "Live stream", + "HOME.ADD_PLAYLIST.METHOD_URL_LABEL": "M3U URL", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.GLOBAL_FAVORITES": "Global favorites", + "HOME.PLAYLISTS.GLOBAL_FAVORITES_DESCRIPTION": "Auto-generated playlist with aggregated favorites from all playlists", + "HOME.PLAYLISTS.MY_PLAYLISTS": "My playlists", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TEXT_IMPORT.BUTTON_LABEL": "Import", + "HOME.TEXT_IMPORT.LABEL": "Insert m3u(8) playlist as text", + "HOME.TITLE": "IPTVnator", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "MENU.ABOUT": "About", + "MENU.ABOUT_ARIA": "About the application", + "MENU.BUG_REPORT": "Report a bug", + "MENU.OPEN": "Open menu", + "MENU.SETTINGS_ARIA": "Open settings", + "MENU.SUPPORT_ARIA": "Support IPTVnator on GitHub", + "MENU.WHAT_IS_NEW": "What is new", + "NO": "No", + "PARENTAL_LOCK.PIN_DIALOG.PIN_LABEL": "PIN", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SETTINGS.SHOW_CAPTIONS": "Show subtitles", + "SETTINGS.SUPPORT_KOFI_HINT": "Buy me a coffee", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.URL": "URL", + "YES": "Yes" + }, + "nl": { + "AUDIO_PLAYER.VOLUME": "Volume", + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "DOWNLOADS.TITLE": "Downloads", + "EMBEDDED_MPV.PLAYER.AUDIO_TRACK_FALLBACK": "Audio {{index}}", + "EMBEDDED_MPV.PLAYER.LIVE_STREAM": "Live stream", + "EMBEDDED_MPV.PLAYER.STATS_AUDIO": "Audio", + "EMBEDDED_MPV.PLAYER.STATS_BUFFER": "Buffer", + "EMBEDDED_MPV.PLAYER.STATS_CONTAINER": "Container", + "EMBEDDED_MPV.PLAYER.STATS_VIDEO": "Video", + "EMBEDDED_MPV.PLAYER.VOLUME": "Volume", + "EMBEDDED_MPV.PLAYER.VOLUME_LABEL": "Volume {{percent}}%", + "EPG.GUIDE.KEYS.DETAILS": "details", + "EPG.GUIDE.ZOOM": "Zoom", + "EPG.LIVE_STREAM": "Live stream", + "EPG.TIMELINE.MINUTES_SHORT": "min", + "EPG.TIMELINE.ZOOM": "Zoom", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_STALKER": "Stalker Portal", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLISTS.STALKER_PORTAL": "Stalker portal", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.STALKER_PORTAL.SERVER_URL": "Server URL", + "HOME.TITLE": "IPTVnator", + "HOME.XTREAM_PLAYLIST.SERVER_URL": "Server URL", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "MENU.OPEN": "Open menu", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.ERROR_VIEW.HOME": "Home", + "PORTALS.ITEM": "item", + "PORTALS.ITEMS": "items", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "PORTALS.SERIES": "Series", + "PORTALS.SIDEBAR.RECENT": "Recent", + "PORTALS.SIDEBAR.SERIES": "Series", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.COMMUNITY": "Project & community", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.LINK_BLOG": "Blog", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.NAV_TMDB": "Metadata", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SETTINGS.TMDB": "Metadata (TMDB)", + "SETTINGS.TMDB_CREDITS": "Metadata", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "STALKER.ACCOUNT_INFO.ACCOUNT": "Account", + "STALKER.ACCOUNT_INFO.STATUS": "Status", + "WORKSPACE.CONTEXT.ITEM_COUNT_ONE": "1 item", + "WORKSPACE.CONTEXT.ITEM_COUNT_OTHER": "{{count}} items", + "WORKSPACE.DASHBOARD.HERO_DETAILS": "Details", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.PROVIDER": "Provider", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.STALKER_PORTAL": "Stalker Portal", + "WORKSPACE.DASHBOARD.TYPE_LIVE": "Live", + "WORKSPACE.DASHBOARD.TYPE_SERIES": "Series", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.COMMANDS.GO_TO_SERIES_LABEL": "Series", + "WORKSPACE.SHELL.COMMANDS.OPEN_DASHBOARD_LABEL": "Open dashboard", + "WORKSPACE.SHELL.COMMANDS.OPEN_DOWNLOADS_LABEL": "Open downloads", + "WORKSPACE.SHELL.CONTEXT_DRAWER_FILTERS_TOOLTIP": "Filters", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "WORKSPACE.SHELL.OPEN_DASHBOARD": "Open dashboard", + "WORKSPACE.SHELL.OPEN_DOWNLOADS": "Open downloads", + "WORKSPACE.SHELL.RAIL_DASHBOARD": "Dashboard", + "WORKSPACE.SHELL.RAIL_DOWNLOADS": "Downloads", + "WORKSPACE.SHELL.RAIL_SERIES": "Series", + "WORKSPACE.SHELL.STALKER_PORTAL": "Stalker Portal", + "WORKSPACE.SHELL.XTREAM_CODE": "Xtream Code", + "WORKSPACE.SOURCES.TYPE": "Type", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.PROTOCOL": "Protocol", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.STATUS": "Status", + "XTREAM.ACCOUNT_INFO.URL": "URL", + "XTREAM.DISCOVER_SERIES": "Series", + "XTREAM.GENRE": "Genre", + "XTREAM.SERIES_STATUS_PILOT": "Pilot", + "XTREAM.YOUTUBE_TRAILER": "YouTube Trailer" + }, + "pl": { + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_ID": "TVG ID", + "CHANNELS.DETAILS_DIALOG.TVG_REC": "TVG rec", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "EMBEDDED_MPV.PLAYER.AUDIO_TRACK_FALLBACK": "Audio {{index}}", + "EPG.TIMELINE.MINUTES_SHORT": "min", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TITLE": "IPTVnator", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.LINK_BLOG": "Blog", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SETTINGS.PORT_RANGE_LABEL": "Port (1-65535)", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "STALKER.ACCOUNT_INFO.LOGIN": "Login", + "STALKER.ACCOUNT_INFO.PORTAL_INFO": "Portal", + "STALKER.ACCOUNT_INFO.STATUS": "Status", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "WORKSPACE.SHELL.XTREAM_CODE": "Xtream Code", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.STATUS": "Status", + "XTREAM.ACCOUNT_INFO.URL": "URL", + "XTREAM.DETAIL_FALLBACK.CONTAINER": "Format", + "XTREAM.SERIES_STATUS_PILOT": "Pilot" + }, + "pt": { + "AUDIO_PLAYER.VOLUME": "Volume", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_ID": "TVG ID", + "CHANNELS.DETAILS_DIALOG.TVG_REC": "TVG rec", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "DOWNLOADS.TITLE": "Downloads", + "EMBEDDED_MPV.PLAYER.STATS_BUFFER": "Buffer", + "EMBEDDED_MPV.PLAYER.VOLUME": "Volume", + "EMBEDDED_MPV.PLAYER.VOLUME_LABEL": "Volume {{percent}}%", + "EPG.GUIDE.ZOOM": "Zoom", + "EPG.TIMELINE.MINUTES_SHORT": "min", + "EPG.TIMELINE.ZOOM": "Zoom", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TITLE": "IPTVnator", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "PARENTAL_LOCK.PIN_DIALOG.PIN_LABEL": "PIN", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PLAYLISTS": "Playlists", + "PORTALS.ITEM": "item", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.LINK_BLOG": "Blog", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "STALKER.ACCOUNT_INFO.PORTAL_INFO": "Portal", + "WORKSPACE.CONTEXT.ITEM_COUNT_ONE": "1 item", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "WORKSPACE.SHELL.RAIL_DASHBOARD": "Dashboard", + "WORKSPACE.SHELL.RAIL_DOWNLOADS": "Downloads", + "WORKSPACE.SHELL.XTREAM_CODE": "Xtream Code", + "WORKSPACE.SOURCES.PLAYLIST_COUNT_ONE": "1 playlist", + "WORKSPACE.SOURCES.PLAYLIST_COUNT_OTHER": "{{count}} playlists", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.STATUS": "Status", + "XTREAM.ACCOUNT_INFO.URL": "URL" + }, + "ru": { + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_ID": "TVG ID", + "CHANNELS.DETAILS_DIALOG.TVG_URL": "TVG URL", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "HOME.ADD_PLAYLIST.METHOD_URL_LABEL": "M3U URL", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TITLE": "IPTVnator", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "PARENTAL_LOCK.PIN_DIALOG.PIN_LABEL": "PIN", + "PORTALS.LIVE_TV": "Live TV", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.XTREAM_CODE": "Xtream Code", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.URL": "URL" + }, + "tr": { + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_ID": "TVG ID", + "CHANNELS.DETAILS_DIALOG.TVG_REC": "TVG rec", + "CHANNELS.DETAILS_DIALOG.TVG_URL": "TVG URL", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "EMBEDDED_MPV.PLAYER.STATS_VIDEO": "Video", + "HOME.ADD_PLAYLIST.METHOD_URL_LABEL": "M3U URL", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TITLE": "IPTVnator", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "PARENTAL_LOCK.PIN_DIALOG.PIN_LABEL": "PIN", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.LINK_BLOG": "Blog", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SETTINGS.PORT_RANGE_LABEL": "Port (1-65535)", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "STALKER.ACCOUNT_INFO.PORTAL_INFO": "Portal", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.URL": "URL", + "XTREAM.DETAIL_FALLBACK.CONTAINER": "Format", + "XTREAM.SERIES_STATUS_PILOT": "Pilot" + }, + "zh": { + "CHANNELS.DETAILS_DIALOG.HTTP_ORIGIN": "Origin", + "CHANNELS.DETAILS_DIALOG.HTTP_REFERRER": "Referrer", + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_ID": "TVG ID", + "CHANNELS.DETAILS_DIALOG.TVG_REC": "TVG rec", + "CHANNELS.DETAILS_DIALOG.TVG_URL": "TVG URL", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "HOME.ADD_PLAYLIST.METHOD_URL_LABEL": "M3U URL", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TITLE": "IPTVnator", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.SEASON_EPISODE_BADGE": "S{{season}}·E{{episode}}", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "WORKSPACE.SHELL.XTREAM_CODE": "Xtream Code", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.URL": "URL" + }, + "zhtw": { + "CHANNELS.DETAILS_DIALOG.HTTP_USER_AGENT": "User-Agent", + "CHANNELS.DETAILS_DIALOG.TVG_ID": "TVG ID", + "CHANNELS.DETAILS_DIALOG.TVG_REC": "TVG rec", + "DOWNLOADS.OFFLINE_DETAIL.FILE_SIZE": "{{size}}", + "HOME.ADD_PLAYLIST.METHOD_URL_LABEL": "M3U URL", + "HOME.PLAYLISTS.CONTENT_EPG": "EPG", + "HOME.PLAYLISTS.FEATURE_XTREAM": "Xtream Codes", + "HOME.PLAYLIST_TYPES.STALKER": "Stalker", + "HOME.PLAYLIST_TYPES.XTREAM": "Xtream", + "HOME.TITLE": "IPTVnator", + "LANGUAGES.ARABIC": "العربية", + "LANGUAGES.BELARUSIAN": "Беларуская", + "LANGUAGES.CHINESE": "简体中文", + "LANGUAGES.DUTCH": "Nederlands", + "LANGUAGES.ENGLISH": "English", + "LANGUAGES.FRENCH": "Français", + "LANGUAGES.GERMAN": "Deutsch", + "LANGUAGES.GREEK": "Ελληνικά", + "LANGUAGES.HUNGARIAN": "Magyar", + "LANGUAGES.ITALIAN": "Italiano", + "LANGUAGES.JAPANESE": "日本語", + "LANGUAGES.KOREAN": "한국어", + "LANGUAGES.MOROCCAN_ARABIC": "العربية (اللهجة المغربية)", + "LANGUAGES.POLISH": "Polski", + "LANGUAGES.PORTUGUESE": "Português", + "LANGUAGES.RUSSIAN": "Русский", + "LANGUAGES.SPANISH": "Español", + "LANGUAGES.TRADITIONAL_CHINESE": "繁體中文", + "LANGUAGES.TURKISH": "Türkçe", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_MPV": "Opening MPV…", + "PLAYBACK_DIAGNOSTICS.ACTION_OPENING_VLC": "Opening VLC…", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_MPV": "Open MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_REOPEN_VLC": "Open VLC again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_MPV": "Try MPV again", + "PLAYBACK_DIAGNOSTICS.ACTION_RETRY_VLC": "Try VLC again", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_FAILED": "External player error", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_OPENING": "Opening player…", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_PLAYING": "Playing", + "PLAYBACK_DIAGNOSTICS.EXTERNAL_STARTED": "Player started", + "PORTALS.MULTI_SOURCE.FILTER_HD": "HD+", + "SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY": "Nightly", + "SETTINGS.APP_UPDATE_CHANNEL_STABLE": "Stable", + "SETTINGS.BROWSE_EPG_FILE": "Choose a local XMLTV file…", + "SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES": "Examples:", + "SETTINGS.EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.", + "SETTINGS.EPG_SOURCE_LIST_TITLE": "Source list", + "SETTINGS.NAV_EPG": "EPG", + "SETTINGS.PLAYER_ARTPLAYER": "ArtPlayer", + "SETTINGS.PLAYER_VLC": "VLC", + "SOURCE_CLEANUP.CLEANUP_WARNING": "Source deleted; some follow-up cleanup failed", + "SOURCE_CLEANUP.CLOSE": "Close", + "SOURCE_CLEANUP.CONSEQUENCES": "Delete only sources you select. Their saved favourites, history and playback progress will also be removed. Downloaded files are kept.", + "SOURCE_CLEANUP.DELETE": "Delete selected · {{count}}", + "SOURCE_CLEANUP.DELETE_PROGRESS": "Processed {{done}} of {{total}} selected sources", + "SOURCE_CLEANUP.GROUP.confirmed": "Confirmed inactive accounts", + "SOURCE_CLEANUP.GROUP.results": "Deletion results", + "SOURCE_CLEANUP.GROUP.skipped": "Skipped sources", + "SOURCE_CLEANUP.GROUP.uncertain": "Review before deleting", + "SOURCE_CLEANUP.SCOPE": "All network sources in your library, regardless of page filters.", + "SOURCE_CLEANUP.SELECT_ALL": "Select all candidates", + "SOURCE_CLEANUP.SELECT_NONE": "Deselect all", + "SOURCE_CLEANUP.STATUS.checking": "Checking…", + "SOURCE_CLEANUP.STATUS.deleted": "Deleted", + "SOURCE_CLEANUP.STATUS.deleting": "Deleting…", + "SOURCE_CLEANUP.STATUS.failed": "Deletion failed — select to retry", + "SOURCE_CLEANUP.STATUS.skipped": "Playing, busy, changed or no longer present", + "SOURCE_CLEANUP.STOP": "Stop after current source", + "SOURCE_CLEANUP.SUMMARY": "Checked {{checked}}/{{total}} · Available {{available}} · Deleted {{deleted}} · Skipped {{skipped}} · Failed {{failed}}", + "SOURCE_CLEANUP.TITLE": "Clean up inactive sources…", + "SOURCE_CLEANUP.UNTITLED": "Untitled source", + "SOURCE_HEALTH.M3U_SCOPE": "Playlist URL, not individual channels", + "SOURCE_HEALTH.REASON.auth": "Access denied; account status unconfirmed", + "SOURCE_HEALTH.REASON.available": "Available", + "SOURCE_HEALTH.REASON.cancelled": "Check cancelled", + "SOURCE_HEALTH.REASON.checking": "Checking…", + "SOURCE_HEALTH.REASON.disabled": "Account disabled", + "SOURCE_HEALTH.REASON.expired": "Subscription expired", + "SOURCE_HEALTH.REASON.http": "Server returned an error", + "SOURCE_HEALTH.REASON.invalid": "Unexpected response", + "SOURCE_HEALTH.REASON.network": "Could not connect", + "SOURCE_HEALTH.REASON.paused": "Connection checks paused", + "SOURCE_HEALTH.REASON.timeout": "Check timed out", + "SOURCE_HEALTH.REASON.unknown": "Not verified", + "SOURCE_HEALTH.RECHECK": "Check again", + "WORKSPACE.DASHBOARD.M3U": "M3U", + "WORKSPACE.DASHBOARD.SEASON_EPISODE_BADGE": "S{{season}}·E{{episode}}", + "WORKSPACE.DASHBOARD.STALKER": "Stalker", + "WORKSPACE.DASHBOARD.STALKER_PORTAL": "Stalker Portal", + "WORKSPACE.DASHBOARD.XTREAM": "Xtream", + "WORKSPACE.SHELL.BRAND_ALT": "IPTVnator", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_CLOSED": "Closed", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_DISMISS": "Dismiss", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_FAILED": "External player error", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_OPENING": "Opening player…", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_PLAYING": "Playing", + "WORKSPACE.SHELL.EXTERNAL_PLAYBACK_STARTED": "Player started", + "WORKSPACE.SHELL.STALKER_PORTAL": "Stalker Portal", + "WORKSPACE.SHELL.XTREAM_CODE": "Xtream Code", + "XTREAM.ACCOUNT_INFO.HTTPS_PORT": "HTTPS", + "XTREAM.ACCOUNT_INFO.HTTP_PORT": "HTTP", + "XTREAM.ACCOUNT_INFO.RTMP_PORT": "RTMP", + "XTREAM.ACCOUNT_INFO.URL": "URL" + } +}