From 50c457918317418a592de6a03c8ec52fae17309d Mon Sep 17 00:00:00 2001 From: Ghalib Ahmed Date: Thu, 10 Sep 2026 15:24:43 -0400 Subject: Translated using Weblate (Urdu (Pakistan)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ur_PK/ --- .../Localization/Core/ur_PK.json | 25 +++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/ur_PK.json b/Emby.Server.Implementations/Localization/Core/ur_PK.json index b3f24a31e3..a8ec31aed4 100644 --- a/Emby.Server.Implementations/Localization/Core/ur_PK.json +++ b/Emby.Server.Implementations/Localization/Core/ur_PK.json @@ -98,5 +98,28 @@ "TaskDownloadMissingLyrics": "غائب بول ڈاؤن لوڈ کریں", "TaskDownloadMissingLyricsDescription": "گانے کے غائب بول ڈاؤن لوڈ کریں", "TaskAudioNormalization": "آڈیو نارملائزیشن", - "TaskAudioNormalizationDescription": "آڈیو نارملائزیشن ڈیٹا کے لیے فائلوں کو سکین کرتا ہے۔" + "TaskAudioNormalizationDescription": "آڈیو نارملائزیشن ڈیٹا کے لیے فائلوں کو سکین کرتا ہے۔", + "LyricDownloadFailureFromForItem": "{1} کے لیے {0} سے دھن ڈاؤن لوڈ کرنے میں ناکامی", + "Original": "اصل", + "TaskRefreshTrickplayImages": "Trickplay تصاویر بنائیں", + "TaskRefreshTrickplayImagesDescription": "فعال لائبریریوں میں ویڈیوز کے لیے Trickplay پیش منظر بناتا ہے۔", + "TaskExtractMediaSegments": "میڈیا سیگمنٹ اسکین", + "TaskExtractMediaSegmentsDescription": "MediaSegment فعال پلگ انز سے میڈیا سیگمنٹس اخذ یا حاصل کرتا ہے۔", + "TaskMoveTrickplayImages": "Trickplay تصاویر کا مقام منتقل کریں", + "TaskMoveTrickplayImagesDescription": "لائبریری کی ترتیبات کے مطابق موجودہ Trickplay فائلیں منتقل کرتا ہے۔", + "CleanupUserDataTask": "صارف ڈیٹا صفائی کا ٹاسک", + "CleanupUserDataTaskDescription": "کم از کم 90 دن سے موجود نہ ہونے والے میڈیا سے تمام صارف ڈیٹا (دیکھنے کی حالت، پسندیدہ حیثیت وغیرہ) صاف کرتا ہے۔", + "NameExtraBehindTheScenes": "پسِ پردہ", + "NameExtraClip": "کلپ", + "NameExtraDeletedScene": "حذف شدہ منظر", + "NameExtraFeaturette": "فیچریٹ", + "NameExtraInterview": "انٹرویو", + "NameExtraNumbered": "{0} {1}", + "NameExtraSample": "نمونہ", + "NameExtraScene": "منظر", + "NameExtraShort": "مختصر", + "NameExtraThemeSong": "تھیم سونگ", + "NameExtraThemeVideo": "تھیم ویڈیو", + "NameExtraTrailer": "ٹریلر", + "NameExtraUnknown": "اضافی" } -- cgit v1.2.3 From ca3a1d9087f78e6f9db5723d31a4132ec0d71d43 Mon Sep 17 00:00:00 2001 From: krvi <27792771+krvi@users.noreply.github.com> Date: Fri, 11 Sep 2026 23:36:50 -0400 Subject: Translated using Weblate (Faroese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fo/ --- Emby.Server.Implementations/Localization/Core/fo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/fo.json b/Emby.Server.Implementations/Localization/Core/fo.json index 023ed6ec5e..d44f0cc68d 100644 --- a/Emby.Server.Implementations/Localization/Core/fo.json +++ b/Emby.Server.Implementations/Localization/Core/fo.json @@ -7,7 +7,7 @@ "AppDeviceValues": "App: {0}, Eind: {1}", "Books": "Bøkur", "ChapterNameValue": "Kapittul {0}", - "Favorites": "Yndislista", + "Favorites": "Yndislisti", "Folders": "Skjáttur", "Forced": "Kravt", "FailedLoginAttemptWithUserName": "Miseydnað innritanarroynd frá {0}", -- cgit v1.2.3 From f4a35963ef4534acbc4b6daa3463bdcf6cb56de5 Mon Sep 17 00:00:00 2001 From: David Wagener Date: Sun, 13 Sep 2026 20:17:48 -0400 Subject: Translated using Weblate (Luxembourgish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lb/ --- Emby.Server.Implementations/Localization/Core/lb.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/lb.json b/Emby.Server.Implementations/Localization/Core/lb.json index 21d31c5fbf..1e64a130eb 100644 --- a/Emby.Server.Implementations/Localization/Core/lb.json +++ b/Emby.Server.Implementations/Localization/Core/lb.json @@ -83,7 +83,7 @@ "NewVersionIsAvailable": "Nei Versioun fir Jellyfin Server ass verfügbar.", "PluginInstalledWithName": "{0} installéiert", "TaskMoveTrickplayImagesDescription": "Verschëfft existent Trickplay-Dateien no de Bibliothéik-Astellungen.", - "AppDeviceValues": "App: {0}, Geräter: {1}", + "AppDeviceValues": "App: {0}, Apparater: {1}", "FailedLoginAttemptWithUserName": "Net Gelongen Umeldung {0}", "HeaderLiveTV": "LiveTV", "NotificationOptionServerRestartRequired": "Server Restart Erfuerderlech", -- cgit v1.2.3 From 0ed74b7376853e8299adc869bd8ee1459494dee1 Mon Sep 17 00:00:00 2001 From: fmarcac Date: Mon, 14 Sep 2026 10:20:44 -0400 Subject: Translated using Weblate (Croatian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hr/ --- Emby.Server.Implementations/Localization/Core/hr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json index 2d38c173f0..c7e2b5f5e9 100644 --- a/Emby.Server.Implementations/Localization/Core/hr.json +++ b/Emby.Server.Implementations/Localization/Core/hr.json @@ -120,5 +120,6 @@ "NameExtraShort": "Kratki film", "NameExtraThemeSong": "Glavna Pjesma", "NameExtraThemeVideo": "Tema videa", - "NameExtraTrailer": "Trailer" + "NameExtraTrailer": "Trailer", + "NameExtraUnknown": "Dodatno" } -- cgit v1.2.3 From 9f2fdd15144dab7190960ac61618d3d88ce6d5c2 Mon Sep 17 00:00:00 2001 From: Tal Sarid Date: Mon, 14 Sep 2026 14:48:43 -0400 Subject: Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/he/ --- Emby.Server.Implementations/Localization/Core/he.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index 0ca7c6aa08..04877b18a7 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -112,8 +112,14 @@ "NameExtraBehindTheScenes": "מאחורי הקלעים", "NameExtraClip": "קליפ", "NameExtraDeletedScene": "סצנה שנמחקה", - "NameExtraFeaturette": "סרט קצר", + "NameExtraFeaturette": "סרט עלילה קצר", "NameExtraInterview": "ריאיון", "NameExtraSample": "דגימה", - "NameExtraScene": "סצנה" + "NameExtraScene": "סצנה", + "NameExtraThemeSong": "שיר נושא", + "NameExtraThemeVideo": "סרטון נושא", + "NameExtraTrailer": "קדימון", + "NameExtraUnknown": "נוסף", + "NameExtraNumbered": "{0} {1}", + "NameExtraShort": "סרט קצר" } -- cgit v1.2.3 From 89af37462ebbe1da078a6f8b4e104475519eab97 Mon Sep 17 00:00:00 2001 From: Jellyfrog Date: Tue, 15 Sep 2026 11:13:49 -0400 Subject: Backport pull request #17844 from jellyfin/release-12.z Match parental ratings case-insensitively Original-merge: 7d0a33b76252d5d3fcb5d3b10cbedd7658623949 Merged-by: crobibero Backported-by: Cody Robibero --- .../Localization/LocalizationManager.cs | 29 +++++++++++---- .../Localization/LocalizationManagerTests.cs | 43 ++++++++++++++++++++++ 2 files changed, 65 insertions(+), 7 deletions(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 0331ec39e5..65cb5379ea 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -139,7 +139,7 @@ namespace Emby.Server.Implementations.Localization var ratingSystem = await JsonSerializer.DeserializeAsync(stream, _jsonOptions).ConfigureAwait(false) ?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'"); - var dict = new Dictionary(); + var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); if (ratingSystem.Ratings is not null) { foreach (var ratingEntry in ratingSystem.Ratings) @@ -374,12 +374,25 @@ namespace Emby.Server.Implementations.Localization { ArgumentException.ThrowIfNullOrEmpty(rating); + // Handle unrated content. This has to happen before the split below, + // because some of the unrated values contain a '/' themselves (e.g. "n/a"). + if (IsUnrated(rating)) + { + return null; + } + // Some providers may list multiple ratings separated by '/' (e.g. "SE:15 / SE:15+ / SE:Från 15 år"). // Try each one in order and use the first that resolves. var ratingValues = rating.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); foreach (var ratingValue in ratingValues) { + // A single entry of such a list may be unrated while a later one still resolves + if (IsUnrated(ratingValue)) + { + continue; + } + var score = GetSingleRatingScore(ratingValue, countryCode); if (score is not null) { @@ -390,17 +403,19 @@ namespace Emby.Server.Implementations.Localization return null; } + /// + /// Checks whether a rating value marks the content as unrated. + /// + /// Rating value to check. + /// Returns true if the value is an unrated marker. + private static bool IsUnrated(ReadOnlySpan rating) + => _unratedValues.Contains(rating.Trim(), StringComparison.OrdinalIgnoreCase); + /// /// Resolves a single rating value to a score. /// private ParentalRatingScore? GetSingleRatingScore(string rating, string? countryCode) { - // Handle unrated content - if (_unratedValues.Contains(rating.AsSpan(), StringComparison.OrdinalIgnoreCase)) - { - return null; - } - // Convert ints directly // This may override some of the locale specific age ratings (but those always map to the same age) if (TryParseRatingAsScore(rating, out var ratingAge)) diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index d973076ed3..ccec4e0037 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -212,6 +212,30 @@ namespace Jellyfin.Server.Implementations.Tests.Localization Assert.Equal(expectedSubScore, score.SubScore); } + [Theory] + // Rating strings are stored mixed-case in the *.json rating systems and must match regardless of casing + [InlineData("btl", "se", 0, null)] // Direct lookup, lowercase of "Btl" + [InlineData("BARNTILLÅTEN", "se", 0, null)] // Direct lookup, uppercase incl. diacritics + [InlineData("SE-BTL", "se", 0, null)] // Country prefix stripped against the configured country + [InlineData("SE-BTL", "us", 0, null)] // Country prefix resolved via the separator fallback + [InlineData("Från 7 År", "se", 7, null)] // Diacritic casing (json has "Från 7 år") + [InlineData("SE-Från 7 År", "us", 7, null)] // Same, via the separator fallback + [InlineData("fsk-16", "de", 16, null)] // Not Sweden specific: lowercase of "FSK-16" + public async Task GetRatingScore_IsCaseInsensitive_Success(string value, string countryCode, int? expectedScore, int? expectedSubScore) + { + var localizationManager = Setup(new ServerConfiguration + { + MetadataCountryCode = countryCode + }); + await localizationManager.LoadAll(); + + var score = localizationManager.GetRatingScore(value); + + Assert.NotNull(score); + Assert.Equal(expectedScore, score.Score); + Assert.Equal(expectedSubScore, score.SubScore); + } + [Theory] [InlineData("0", 0, null)] [InlineData("1", 1, null)] @@ -241,6 +265,25 @@ namespace Jellyfin.Server.Implementations.Tests.Localization Assert.Null(localizationManager.GetRatingScore("unrated")); Assert.Null(localizationManager.GetRatingScore("Not Rated")); Assert.Null(localizationManager.GetRatingScore("n/a")); + Assert.Null(localizationManager.GetRatingScore("N/A")); + Assert.Null(localizationManager.GetRatingScore(" n/a ")); + } + + [Theory] + // "NR" and "UR" are rating strings of some systems, so they must stay unrated when listed alongside others + [InlineData("NR / R", 17, 0)] + [InlineData("unrated / R", 17, 0)] + [InlineData("R / NR", 17, 0)] + public async Task GetRatingLevel_SkipsUnratedListEntries_Success(string value, int? expectedScore, int? expectedSubScore) + { + var localizationManager = Setup(new ServerConfiguration { MetadataCountryCode = "us" }); + await localizationManager.LoadAll(); + + var score = localizationManager.GetRatingScore(value); + + Assert.NotNull(score); + Assert.Equal(expectedScore, score.Score); + Assert.Equal(expectedSubScore, score.SubScore); } [Theory] -- cgit v1.2.3 From 394facc327a2aeff139279ff35432bcf4755ff36 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Tue, 15 Sep 2026 11:16:01 -0400 Subject: Backport pull request #17933 from jellyfin/release-12.z Fix Italian ratings Original-merge: 003c7b5b9104a1eef6d00c1caf8c9836b277d0e0 Merged-by: crobibero Backported-by: Cody Robibero --- .../Localization/LocalizationManager.cs | 7 +++ .../Localization/Ratings/it.json | 22 +++++-- .../Routines/20260302090000_MigrateRatingLevels.cs | 68 ---------------------- .../Routines/20260910120000_MigrateRatingLevels.cs | 68 ++++++++++++++++++++++ .../Localization/LocalizationManagerTests.cs | 10 ++++ 5 files changed, 103 insertions(+), 72 deletions(-) delete mode 100644 Jellyfin.Server/Migrations/Routines/20260302090000_MigrateRatingLevels.cs create mode 100644 Jellyfin.Server/Migrations/Routines/20260910120000_MigrateRatingLevels.cs (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 65cb5379ea..c77717c76e 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -139,6 +139,7 @@ namespace Emby.Server.Implementations.Localization var ratingSystem = await JsonSerializer.DeserializeAsync(stream, _jsonOptions).ConfigureAwait(false) ?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'"); + // Rating strings are compared case insensitively, providers are not consistent about casing (e.g. "VM18" vs "vm18") var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); if (ratingSystem.Ratings is not null) { @@ -527,6 +528,12 @@ namespace Emby.Server.Implementations.Localization return true; } + // Explicitly unrated content (e.g. "IT-NR") is unrated by definition, not a lookup failure + if (IsUnrated(ratingPart)) + { + return true; + } + _logger.LogWarning( "Rating '{Rating}' not found in the '{CountryCode}' rating system, treating as unrated", rating, diff --git a/Emby.Server.Implementations/Localization/Ratings/it.json b/Emby.Server.Implementations/Localization/Ratings/it.json index f2889bf82c..c1d716a46c 100644 --- a/Emby.Server.Implementations/Localization/Ratings/it.json +++ b/Emby.Server.Implementations/Localization/Ratings/it.json @@ -3,28 +3,42 @@ "supportsSubScores": false, "ratings": [ { - "ratingStrings": ["T"], + "ratingStrings": ["T", "PT", "Per tutti"], "ratingScore": { "score": 0, "subScore": null } }, { - "ratingStrings": ["6+"], + "ratingStrings": ["6+", "VM6", "VM 6", "VM-6", "Vietato ai minori di 6 anni"], "ratingScore": { "score": 6, "subScore": null } }, { - "ratingStrings": ["14+"], + "ratingStrings": ["10+", "VM10", "VM 10", "VM-10", "Vietato ai minori di 10 anni"], + "ratingScore": { + "score": 10, + "subScore": null + } + }, + { + "ratingStrings": ["12+", "VM12", "VM 12", "VM-12", "Vietato ai minori di 12 anni"], + "ratingScore": { + "score": 12, + "subScore": null + } + }, + { + "ratingStrings": ["14+", "VM14", "VM 14", "VM-14", "Vietato ai minori di 14 anni"], "ratingScore": { "score": 14, "subScore": null } }, { - "ratingStrings": ["18+"], + "ratingStrings": ["18+", "VM18", "VM 18", "VM-18", "Vietato ai minori di 18 anni"], "ratingScore": { "score": 18, "subScore": null diff --git a/Jellyfin.Server/Migrations/Routines/20260302090000_MigrateRatingLevels.cs b/Jellyfin.Server/Migrations/Routines/20260302090000_MigrateRatingLevels.cs deleted file mode 100644 index ed92c34aa3..0000000000 --- a/Jellyfin.Server/Migrations/Routines/20260302090000_MigrateRatingLevels.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Linq; -using Jellyfin.Database.Implementations; -using Jellyfin.Server.ServerSetupApp; -using MediaBrowser.Model.Globalization; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; - -namespace Jellyfin.Server.Migrations.Routines; - -/// -/// Migrate rating levels. -/// -#pragma warning disable CS0618 // Type or member is obsolete -[JellyfinMigration("2026-03-02T09:00:00", nameof(MigrateRatingLevels))] -[JellyfinMigrationBackup(JellyfinDb = true)] -#pragma warning restore CS0618 // Type or member is obsolete -internal class MigrateRatingLevels : IDatabaseMigrationRoutine -{ - private readonly IStartupLogger _logger; - private readonly IDbContextFactory _provider; - private readonly ILocalizationManager _localizationManager; - - public MigrateRatingLevels( - IDbContextFactory provider, - IStartupLogger logger, - ILocalizationManager localizationManager) - { - _provider = provider; - _localizationManager = localizationManager; - _logger = logger; - } - - /// - public void Perform() - { - _logger.LogInformation("Recalculating parental rating levels based on rating string."); - using var context = _provider.CreateDbContext(); - using var transaction = context.Database.BeginTransaction(); - var ratings = context.BaseItems.AsNoTracking().Select(e => e.OfficialRating).Distinct(); - foreach (var rating in ratings) - { - if (string.IsNullOrEmpty(rating)) - { - int? value = null; - context.BaseItems - .Where(e => e.OfficialRating == null || e.OfficialRating == string.Empty) - .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingValue, value)); - context.BaseItems - .Where(e => e.OfficialRating == null || e.OfficialRating == string.Empty) - .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingSubValue, value)); - } - else - { - var ratingValue = _localizationManager.GetRatingScore(rating); - var score = ratingValue?.Score; - var subScore = ratingValue?.SubScore; - context.BaseItems - .Where(e => e.OfficialRating == rating) - .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingValue, score)); - context.BaseItems - .Where(e => e.OfficialRating == rating) - .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingSubValue, subScore)); - } - } - - transaction.Commit(); - } -} diff --git a/Jellyfin.Server/Migrations/Routines/20260910120000_MigrateRatingLevels.cs b/Jellyfin.Server/Migrations/Routines/20260910120000_MigrateRatingLevels.cs new file mode 100644 index 0000000000..a456acf47d --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/20260910120000_MigrateRatingLevels.cs @@ -0,0 +1,68 @@ +using System.Linq; +using Jellyfin.Database.Implementations; +using Jellyfin.Server.ServerSetupApp; +using MediaBrowser.Model.Globalization; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Migrations.Routines; + +/// +/// Migrate rating levels. +/// +#pragma warning disable CS0618 // Type or member is obsolete +[JellyfinMigration("2026-09-10T12:00:00", nameof(MigrateRatingLevels))] +[JellyfinMigrationBackup(JellyfinDb = true)] +#pragma warning restore CS0618 // Type or member is obsolete +internal class MigrateRatingLevels : IDatabaseMigrationRoutine +{ + private readonly IStartupLogger _logger; + private readonly IDbContextFactory _provider; + private readonly ILocalizationManager _localizationManager; + + public MigrateRatingLevels( + IDbContextFactory provider, + IStartupLogger logger, + ILocalizationManager localizationManager) + { + _provider = provider; + _localizationManager = localizationManager; + _logger = logger; + } + + /// + public void Perform() + { + _logger.LogInformation("Recalculating parental rating levels based on rating string."); + using var context = _provider.CreateDbContext(); + using var transaction = context.Database.BeginTransaction(); + var ratings = context.BaseItems.AsNoTracking().Select(e => e.OfficialRating).Distinct(); + foreach (var rating in ratings) + { + if (string.IsNullOrEmpty(rating)) + { + int? value = null; + context.BaseItems + .Where(e => e.OfficialRating == null || e.OfficialRating == string.Empty) + .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingValue, value)); + context.BaseItems + .Where(e => e.OfficialRating == null || e.OfficialRating == string.Empty) + .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingSubValue, value)); + } + else + { + var ratingValue = _localizationManager.GetRatingScore(rating); + var score = ratingValue?.Score; + var subScore = ratingValue?.SubScore; + context.BaseItems + .Where(e => e.OfficialRating == rating) + .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingValue, score)); + context.BaseItems + .Where(e => e.OfficialRating == rating) + .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingSubValue, subScore)); + } + } + + transaction.Commit(); + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index ccec4e0037..221f1d8ec8 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -199,6 +199,16 @@ namespace Jellyfin.Server.Implementations.Tests.Localization [InlineData("Rated: R", "US", 17, 0)] [InlineData("Rated R", "US", 17, 0)] [InlineData(" PG-13 ", "US", 13, 0)] + [InlineData("T", "IT", 0, null)] + [InlineData("VM6", "IT", 6, null)] + [InlineData("VM12", "IT", 12, null)] + [InlineData("VM14", "IT", 14, null)] + [InlineData("VM18", "IT", 18, null)] + [InlineData("IT-VM14", "IT", 14, null)] // TMDB style country prefix + [InlineData("IT-VM18", "IT", 18, null)] + [InlineData("it-vm18", "IT", 18, null)] // Rating strings are case insensitive + [InlineData("VM 18", "IT", 18, null)] + [InlineData("Vietato ai minori di 18 anni", "IT", 18, null)] public async Task GetRatingLevel_GivenValidString_Success(string value, string countryCode, int? expectedScore, int? expectedSubScore) { var localizationManager = Setup(new ServerConfiguration() -- cgit v1.2.3 From da3031628fda013be20ae4f89906882cc5d72e14 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Tue, 15 Sep 2026 11:16:22 -0400 Subject: Backport pull request #18002 from jellyfin/release-12.z Fix slashed rating handling Original-merge: 4405fccda92426b01903e3fe4c5985f1d7c30957 Merged-by: crobibero Backported-by: Cody Robibero --- .../Localization/LocalizationManager.cs | 12 ++++++++++ .../Entities/AggregateFolderTests.cs | 1 + .../Entities/BaseItemTests.cs | 1 + .../Localization/LocalizationManagerTests.cs | 26 ++++++++++++++++++++++ 4 files changed, 40 insertions(+) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index c77717c76e..3f89237ab2 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -382,9 +382,21 @@ namespace Emby.Server.Implementations.Localization return null; } + // Several rating systems contain a '/' inside a single rating (e.g. "M/12" in PT, + // "U/A 13+" in IN, "7/i/fig" in ES), so the value as a whole always wins over the split below. + var wholeValueScore = GetSingleRatingScore(rating, countryCode); + if (wholeValueScore is not null) + { + return wholeValueScore; + } + // Some providers may list multiple ratings separated by '/' (e.g. "SE:15 / SE:15+ / SE:Från 15 år"). // Try each one in order and use the first that resolves. var ratingValues = rating.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + if (ratingValues.Length == 1) + { + return null; + } foreach (var ratingValue in ratingValues) { diff --git a/tests/Jellyfin.Controller.Tests/Entities/AggregateFolderTests.cs b/tests/Jellyfin.Controller.Tests/Entities/AggregateFolderTests.cs index 6c297673d4..272c434fe9 100644 --- a/tests/Jellyfin.Controller.Tests/Entities/AggregateFolderTests.cs +++ b/tests/Jellyfin.Controller.Tests/Entities/AggregateFolderTests.cs @@ -9,6 +9,7 @@ using Xunit; namespace Jellyfin.Controller.Tests.Entities; +[Collection("LibraryManagerTests")] public class AggregateFolderTests { [Fact] diff --git a/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs b/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs index f363968909..588f9d5530 100644 --- a/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs +++ b/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs @@ -27,6 +27,7 @@ using Xunit; namespace Jellyfin.Controller.Tests.Entities; +[Collection("LibraryManagerTests")] public class BaseItemTests { [Fact] diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index 221f1d8ec8..93014e7244 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -296,6 +296,32 @@ namespace Jellyfin.Server.Implementations.Tests.Localization Assert.Equal(expectedSubScore, score.SubScore); } + [Theory] + // Ratings that contain a '/' themselves must not be split into a list of ratings + [InlineData("M/3", "pt", 3, null)] + [InlineData("M/12", "pt", 12, null)] + [InlineData("M/18", "pt", 18, null)] + [InlineData("PT-M/12", "pt", 12, null)] // TMDB style country prefix + [InlineData("M/12", "us", 12, null)] // Resolved through the all-systems fallback + [InlineData("U/A 13+", "in", 13, null)] + [InlineData("7/i", "es", 11, null)] + [InlineData("7/i/fig", "es", 11, null)] + [InlineData("18/fig", "es", 18, null)] + public async Task GetRatingScore_RatingContainingSlash_IsNotSplit(string value, string countryCode, int expectedScore, int? expectedSubScore) + { + var localizationManager = Setup(new ServerConfiguration + { + MetadataCountryCode = countryCode + }); + await localizationManager.LoadAll(); + + var score = localizationManager.GetRatingScore(value); + + Assert.NotNull(score); + Assert.Equal(expectedScore, score.Score); + Assert.Equal(expectedSubScore, score.SubScore); + } + [Theory] [InlineData("-NO RATING SHOWN-")] [InlineData(":NO RATING SHOWN:")] -- cgit v1.2.3 From 010ac806ee8bcb672ac51f0b829483ce84e26e55 Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Tue, 15 Sep 2026 11:17:01 -0400 Subject: Backport pull request #18017 from jellyfin/release-12.z Fix Norwegian ratings Original-merge: e0cf29ba69610ae2494de83f21aab20aa3578e73 Merged-by: crobibero Backported-by: Cody Robibero --- Emby.Server.Implementations/Localization/Ratings/no.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Ratings/no.json b/Emby.Server.Implementations/Localization/Ratings/no.json index a5e9523163..40c66e3956 100644 --- a/Emby.Server.Implementations/Localization/Ratings/no.json +++ b/Emby.Server.Implementations/Localization/Ratings/no.json @@ -10,49 +10,49 @@ } }, { - "ratingStrings": ["6"], + "ratingStrings": ["6", "6 år"], "ratingScore": { "score": 6, "subScore": null } }, { - "ratingStrings": ["7"], + "ratingStrings": ["7", "7 år"], "ratingScore": { "score": 7, "subScore": null } }, { - "ratingStrings": ["9"], + "ratingStrings": ["9", "9 år"], "ratingScore": { "score": 9, "subScore": null } }, { - "ratingStrings": ["11"], + "ratingStrings": ["11", "11 år"], "ratingScore": { "score": 11, "subScore": null } }, { - "ratingStrings": ["12"], + "ratingStrings": ["12", "12 år"], "ratingScore": { "score": 12, "subScore": null } }, { - "ratingStrings": ["15"], + "ratingStrings": ["15", "15 år"], "ratingScore": { "score": 15, "subScore": null } }, { - "ratingStrings": ["18"], + "ratingStrings": ["18", "18 år"], "ratingScore": { "score": 18, "subScore": null -- cgit v1.2.3 From 6d75be24a44d9133a6fed4dce63776ee0df7bbe6 Mon Sep 17 00:00:00 2001 From: Fjuro Date: Tue, 15 Sep 2026 15:38:21 -0400 Subject: Translated using Weblate (Czech) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/cs/ --- Emby.Server.Implementations/Localization/Core/cs.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json index 033002d2b2..cfc16d553c 100644 --- a/Emby.Server.Implementations/Localization/Core/cs.json +++ b/Emby.Server.Implementations/Localization/Core/cs.json @@ -1,6 +1,6 @@ { "AppDeviceValues": "Aplikace: {0}, Zařízení: {1}", - "Artists": "Umělci", + "Artists": "Interpreti", "AuthenticationSucceededWithUserName": "{0} úspěšně ověřen", "Books": "Knihy", "ChapterNameValue": "Kapitola {0}", -- cgit v1.2.3 From 1b6a6b0dd4e9d5c39cb9717f435ca339565695c0 Mon Sep 17 00:00:00 2001 From: brtc Date: Tue, 15 Sep 2026 09:58:35 -0400 Subject: Translated using Weblate (Turkish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/tr/ --- Emby.Server.Implementations/Localization/Core/tr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json index 67f4ecc4d5..87f49ae80d 100644 --- a/Emby.Server.Implementations/Localization/Core/tr.json +++ b/Emby.Server.Implementations/Localization/Core/tr.json @@ -81,8 +81,8 @@ "TaskCleanLogs": "Günlük Dizinini Temizle", "TaskRefreshLibraryDescription": "Medya kütüphanenize eklenen yeni dosyaları arar ve ortam bilgilerini yeniler.", "TaskRefreshLibrary": "Medya Kütüphanesini Tara", - "TaskRefreshChapterImagesDescription": "Bölümlere ayrılmış videolar için küçük resimler oluştur.", - "TaskRefreshChapterImages": "Bölüm Resimlerini Çıkar", + "TaskRefreshChapterImagesDescription": "Video bölümleri için küçük görseller oluştur.", + "TaskRefreshChapterImages": "Bölüm görsellerini çıkar", "TaskCleanCacheDescription": "Sistem tarafından artık ihtiyaç duyulmayan önbellek dosyalarını siler.", "TaskCleanActivityLog": "Etkinlik Günlüğünü Temizle", "TaskCleanActivityLogDescription": "Yapılandırılan tarihten daha eski olan etkinlik günlüğü girişlerini siler.", -- cgit v1.2.3 From aaaef07856f2b19303a1b1ae08c94928711a297b Mon Sep 17 00:00:00 2001 From: 無情天 Date: Tue, 15 Sep 2026 22:30:19 -0400 Subject: Translated using Weblate (Chinese (Simplified Han script)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/ --- Emby.Server.Implementations/Localization/Core/zh-CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index 9590d9c1c9..7c61ce6f17 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -22,7 +22,7 @@ "MixedContent": "混合内容", "Movies": "电影", "Music": "音乐", - "MusicVideos": "MV", + "MusicVideos": "音乐视频", "NameInstallFailed": "{0} 安装失败", "NameSeasonNumber": "第 {0} 季", "NameSeasonUnknown": "未知季", -- cgit v1.2.3 From 50866380c95758244f79bd4fbb01b9474e80bd81 Mon Sep 17 00:00:00 2001 From: krvi <27792771+krvi@users.noreply.github.com> Date: Tue, 15 Sep 2026 18:22:06 -0400 Subject: Translated using Weblate (Faroese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fo/ --- Emby.Server.Implementations/Localization/Core/fo.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/fo.json b/Emby.Server.Implementations/Localization/Core/fo.json index d44f0cc68d..acb78290f3 100644 --- a/Emby.Server.Implementations/Localization/Core/fo.json +++ b/Emby.Server.Implementations/Localization/Core/fo.json @@ -25,7 +25,7 @@ "NewVersionIsAvailable": "Ein nýggj útgáva av Jellyfin ambætaranum er tøk.", "NotificationOptionNewLibraryContent": "Nýtt tilfar innlagt", "NotificationOptionPluginInstalled": "Ískoytisforrit innlagt", - "NotificationOptionPluginUninstalled": "Ískoytisforrit strikað", + "NotificationOptionPluginUninstalled": "Ískoytisforrit er strikað", "NotificationOptionPluginUpdateInstalled": "Ískoytisforrit dagført", "NotificationOptionUserLockedOut": "Brúkari útihýstur", "Photos": "Ljósmyndir", @@ -96,9 +96,9 @@ "TaskRefreshChapterImagesDescription": "Ger smámyndir fyri kykmyndir ið hava kapitlar.", "TaskRefreshChapterImages": "Kapitlamyndaúttøkur", "NotificationOptionVideoPlayback": "Kykmyndaspæl byrjað", - "NotificationOptionVideoPlaybackStopped": "Kykmyndaspæl steðgað", + "NotificationOptionVideoPlaybackStopped": "Kykmyndaspæl er steðgað", "NotificationOptionAudioPlayback": "Ljóðspæl byrjað", - "NotificationOptionAudioPlaybackStopped": "Ljóðspæl steðgað", + "NotificationOptionAudioPlaybackStopped": "Ljóðspæl er steðgað", "TaskExtractMediaSegments": "Leita eftir margmiðlabrotum", "TaskExtractMediaSegmentsDescription": "Framleiður upplýsingar um brot í margmiðlum, við hjálp frá MediaSegment-virktum ískoytisforritum.", "NotificationOptionCameraImageUploaded": "Ljósmynd uppsend", -- cgit v1.2.3 From fd75964da853765d63101b4889f23a4161e2758b Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Tue, 22 Sep 2026 02:38:03 -0400 Subject: Translated using Weblate (Japanese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ja/ --- Emby.Server.Implementations/Localization/Core/ja.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json index 78b7ec744b..49757d66e7 100644 --- a/Emby.Server.Implementations/Localization/Core/ja.json +++ b/Emby.Server.Implementations/Localization/Core/ja.json @@ -2,7 +2,7 @@ "AppDeviceValues": "アプリ: {0}, デバイス: {1}", "Artists": "アーティスト", "AuthenticationSucceededWithUserName": "{0} 認証に成功しました", - "Books": "ブック", + "Books": "書籍", "ChapterNameValue": "チャプター {0}", "Collections": "コレクション", "FailedLoginAttemptWithUserName": "{0} からのログインに失敗しました", -- cgit v1.2.3 From 208c278b75abd897aefa1e1175126eac5e4dbfaa Mon Sep 17 00:00:00 2001 From: Ulrik Date: Tue, 22 Sep 2026 11:07:01 -0400 Subject: Translated using Weblate (Danish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/da/ --- Emby.Server.Implementations/Localization/Core/da.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index 6e5532ead9..4c7d16a9a7 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -10,7 +10,7 @@ "Folders": "Mapper", "Genres": "Genrer", "HeaderContinueWatching": "Fortsæt med at se", - "HeaderFavoriteEpisodes": "Yndlingsafsnit", + "HeaderFavoriteEpisodes": "Favoritepisoder", "HeaderFavoriteShows": "Yndlingsserier", "HeaderLiveTV": "Live-TV", "HeaderNextUp": "Næste", -- cgit v1.2.3 From 390296c9c8160bb6ad6f01b41226398776d21a83 Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Sat, 26 Sep 2026 09:08:08 -0400 Subject: Translated using Weblate (Japanese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ja/ --- Emby.Server.Implementations/Localization/Core/ja.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json index 49757d66e7..a93ee802a1 100644 --- a/Emby.Server.Implementations/Localization/Core/ja.json +++ b/Emby.Server.Implementations/Localization/Core/ja.json @@ -9,11 +9,11 @@ "Favorites": "お気に入り", "Folders": "フォルダー", "Genres": "ジャンル", - "HeaderContinueWatching": "再生を続ける", + "HeaderContinueWatching": "視聴をつづける", "HeaderFavoriteEpisodes": "お気に入りのエピソード", "HeaderFavoriteShows": "お気に入りの番組", "HeaderLiveTV": "ライブTV", - "HeaderNextUp": "次", + "HeaderNextUp": "次に再生", "HomeVideos": "ホームビデオ", "Inherit": "継承", "LabelIpAddressValue": "IPアドレス: {0}", @@ -41,7 +41,7 @@ "NotificationOptionServerRestartRequired": "サーバーを再起動してください", "NotificationOptionTaskFailed": "スケジュールされていたタスクの失敗", "NotificationOptionUserLockedOut": "ユーザーはロックされています", - "NotificationOptionVideoPlayback": "ビデオの再生を開始", + "NotificationOptionVideoPlayback": "動画の再生を開始", "NotificationOptionVideoPlaybackStopped": "ビデオの再生を停止", "Photos": "フォト", "PluginInstalledWithName": "{0} をインストールしました", @@ -81,7 +81,7 @@ "TaskRefreshPeopleDescription": "メディアライブラリー内の俳優や監督のメタデータを更新します。", "TaskRefreshPeople": "俳優や監督のデータの更新", "TaskDownloadMissingSubtitlesDescription": "メタデータ構成に基づいて、欠落している字幕をインターネットで検索する。", - "TaskRefreshChapterImagesDescription": "チャプターのあるビデオのサムネイルを作成します。", + "TaskRefreshChapterImagesDescription": "チャプターのあるビデオのサムネールを作成します。", "TaskRefreshChapterImages": "チャプター画像を抽出する", "TaskDownloadMissingSubtitles": "不足している字幕をダウンロードする", "TaskCleanActivityLogDescription": "設定された期間よりも古いアクティビティの履歴を削除します。", @@ -91,7 +91,7 @@ "Default": "デフォルト", "TaskOptimizeDatabaseDescription": "データベースをコンパクトにして、空き領域を切り詰めます。メディアライブラリーのスキャンやその他のデータベースの更新を伴う変更の後でこのタスクを実行すると、パフォーマンスが向上します。", "TaskOptimizeDatabase": "データベースの最適化", - "TaskKeyframeExtractorDescription": "より正確なHLSプレイリストを作成するため、動画ファイルからキーフレームを抽出する。この処理には時間がかかる場合があります。", + "TaskKeyframeExtractorDescription": "動画ファイルからキーフレームを抽出し、より正確な HLS プレーリストを作成します。この処理には時間がかかる場合があります。", "TaskKeyframeExtractor": "キーフレーム抽出", "External": "外部", "HearingImpaired": "聴覚障害の方", @@ -108,5 +108,6 @@ "CleanupUserDataTask": "ユーザーデータのクリーンアップタスク", "CleanupUserDataTaskDescription": "90日以上存在しないメディアに対して、視聴状態やお気に入り状態などのユーザーデータをすべて削除します。", "LyricDownloadFailureFromForItem": "歌詞", - "Original": "オリジナル" + "Original": "オリジナル", + "NameExtraThemeVideo": "テーマビデオ" } -- cgit v1.2.3 From af2d33355a6dd24e4eea3fe01858df5f55902ef3 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sun, 27 Sep 2026 16:30:33 -0400 Subject: Backport pull request #18057 from jellyfin/release-12.z Fix more rating systems Original-merge: e9f7544fa181658d18dede0dc38276e7d21ba9b0 Merged-by: crobibero Backported-by: Cody Robibero --- .../Localization/LocalizationManager.cs | 42 ++++++--- .../Localization/Ratings/0-prefer.json | 2 +- .../Localization/Ratings/ar.json | 24 +++-- .../Localization/Ratings/au.json | 2 +- .../Localization/Ratings/de.json | 10 +-- .../Localization/Ratings/fr.json | 8 +- .../Localization/Ratings/id.json | 25 +++++- .../Routines/20260910120000_MigrateRatingLevels.cs | 68 -------------- .../Routines/20260915120000_MigrateRatingLevels.cs | 68 ++++++++++++++ .../Localization/LocalizationManagerTests.cs | 100 ++++++++++++++++++++- 10 files changed, 248 insertions(+), 101 deletions(-) delete mode 100644 Jellyfin.Server/Migrations/Routines/20260910120000_MigrateRatingLevels.cs create mode 100644 Jellyfin.Server/Migrations/Routines/20260915120000_MigrateRatingLevels.cs (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 3f89237ab2..7af545c550 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -398,6 +398,7 @@ namespace Emby.Server.Implementations.Localization return null; } + var hasUnknownRating = false; foreach (var ratingValue in ratingValues) { // A single entry of such a list may be unrated while a later one still resolves @@ -406,11 +407,21 @@ namespace Emby.Server.Implementations.Localization continue; } - var score = GetSingleRatingScore(ratingValue, countryCode); + // An unknown entry is not worth a warning of its own as long as another entry still resolves + var score = GetSingleRatingScore(ratingValue, countryCode, logUnknownRating: false); if (score is not null) { return score; } + + hasUnknownRating = true; + } + + if (hasUnknownRating) + { + _logger.LogWarning( + "None of the ratings in '{Rating}' were found in a known rating system, treating as unrated", + rating); } return null; @@ -427,7 +438,7 @@ namespace Emby.Server.Implementations.Localization /// /// Resolves a single rating value to a score. /// - private ParentalRatingScore? GetSingleRatingScore(string rating, string? countryCode) + private ParentalRatingScore? GetSingleRatingScore(string rating, string? countryCode, bool logUnknownRating = true) { // Convert ints directly // This may override some of the locale specific age ratings (but those always map to the same age) @@ -483,9 +494,11 @@ namespace Emby.Server.Implementations.Localization } } - // Try splitting by country prefix separator to handle "US:PG-13", "Germany: FSK-18", "DE-FSK-18" - if (TryGetRatingScoreBySeparator(rating, ':', out var result) - || TryGetRatingScoreBySeparator(rating, '-', out result)) + // Try splitting by country prefix separator to handle "US:PG-13", "Germany: FSK-18", "DE-FSK-18". + // A '/' marks a list of ratings, which this would split into the first entry's country and the last entry's rating + if (!rating.Contains('/', StringComparison.Ordinal) + && (TryGetRatingScoreBySeparator(rating, ':', logUnknownRating, out var result) + || TryGetRatingScoreBySeparator(rating, '-', logUnknownRating, out result))) { return result; } @@ -493,7 +506,7 @@ namespace Emby.Server.Implementations.Localization return null; } - private bool TryGetRatingScoreBySeparator(string rating, char separator, out ParentalRatingScore? result) + private bool TryGetRatingScoreBySeparator(string rating, char separator, bool logUnknownRating, out ParentalRatingScore? result) { result = null; @@ -546,10 +559,13 @@ namespace Emby.Server.Implementations.Localization return true; } - _logger.LogWarning( - "Rating '{Rating}' not found in the '{CountryCode}' rating system, treating as unrated", - rating, - resolvedCountryCode); + if (logUnknownRating) + { + _logger.LogWarning( + "Rating '{Rating}' not found in the '{CountryCode}' rating system, treating as unrated", + rating, + resolvedCountryCode); + } return true; } @@ -561,14 +577,16 @@ namespace Emby.Server.Implementations.Localization } /// - /// Tries to parse a rating as a number, allowing an optional trailing '+' (e.g. "16" or "18+"). + /// Tries to parse a rating as a number, allowing an optional trailing '+' (e.g. "16" or "18+") + /// or a leading '-' (e.g. the French "-12"). /// /// Rating value to parse. /// Parsed score. /// Returns true if parsing was successful. private static bool TryParseRatingAsScore(ReadOnlySpan ratingValue, out int score) { - var trimmed = ratingValue.TrimEnd('+'); + // A leading '-' marks a minimum age ("-12" is French for "not for under 12s"), never a negative score + var trimmed = ratingValue.TrimStart('-').TrimEnd('+'); return int.TryParse(trimmed, out score); } diff --git a/Emby.Server.Implementations/Localization/Ratings/0-prefer.json b/Emby.Server.Implementations/Localization/Ratings/0-prefer.json index b390151611..dd88b35363 100644 --- a/Emby.Server.Implementations/Localization/Ratings/0-prefer.json +++ b/Emby.Server.Implementations/Localization/Ratings/0-prefer.json @@ -24,7 +24,7 @@ } }, { - "ratingStrings": ["X", "XX", "XXX", "XXXX"], + "ratingStrings": ["X", "XX", "XXX", "XXXX", "P"], "ratingScore": { "score": 1000, "subScore": null diff --git a/Emby.Server.Implementations/Localization/Ratings/ar.json b/Emby.Server.Implementations/Localization/Ratings/ar.json index 73dfd2c7c9..20053fe575 100644 --- a/Emby.Server.Implementations/Localization/Ratings/ar.json +++ b/Emby.Server.Implementations/Localization/Ratings/ar.json @@ -3,35 +3,49 @@ "supportsSubScores": false, "ratings": [ { - "ratingStrings": ["ATP"], + "ratingStrings": ["ATP", "Apta para todo público", "G"], "ratingScore": { "score": 0, "subScore": null } }, { - "ratingStrings": ["+13"], + "ratingStrings": ["SP"], + "ratingScore": { + "score": 10, + "subScore": null + } + }, + { + "ratingStrings": ["+13", "SAM 13", "SAM13", "R-13"], "ratingScore": { "score": 13, "subScore": null } }, { - "ratingStrings": ["+16"], + "ratingStrings": ["+16", "SAM 16", "SAM16"], "ratingScore": { "score": 16, "subScore": null } }, { - "ratingStrings": ["+18"], + "ratingStrings": ["R-17"], + "ratingScore": { + "score": 17, + "subScore": null + } + }, + { + "ratingStrings": ["+18", "SAM 18", "SAM18"], "ratingScore": { "score": 18, "subScore": null } }, { - "ratingStrings": ["C"], + "ratingStrings": ["C", "+18 C", "SAM 18 C", "SAM 18C", "SAM18C"], "ratingScore": { "score": 1001, "subScore": null diff --git a/Emby.Server.Implementations/Localization/Ratings/au.json b/Emby.Server.Implementations/Localization/Ratings/au.json index a563df899d..c5d4c97443 100644 --- a/Emby.Server.Implementations/Localization/Ratings/au.json +++ b/Emby.Server.Implementations/Localization/Ratings/au.json @@ -52,7 +52,7 @@ } }, { - "ratingStrings": ["X", "X18", "X 18"], + "ratingStrings": ["X", "X18", "X 18", "X18+", "X 18+"], "ratingScore": { "score": 1000, "subScore": 0 diff --git a/Emby.Server.Implementations/Localization/Ratings/de.json b/Emby.Server.Implementations/Localization/Ratings/de.json index 30c34b230c..2a925f999f 100644 --- a/Emby.Server.Implementations/Localization/Ratings/de.json +++ b/Emby.Server.Implementations/Localization/Ratings/de.json @@ -3,35 +3,35 @@ "supportsSubScores": false, "ratings": [ { - "ratingStrings": ["0", "FSK 0", "FSK-0", "Educational", "Infoprogramm"], + "ratingStrings": ["0", "FSK 0", "FSK-0", "FSK0", "ab 0", "Educational", "Infoprogramm"], "ratingScore": { "score": 0, "subScore": null } }, { - "ratingStrings": ["6", "FSK 6", "FSK-6"], + "ratingStrings": ["6", "FSK 6", "FSK-6", "FSK6", "ab 6"], "ratingScore": { "score": 6, "subScore": null } }, { - "ratingStrings": ["12", "FSK 12", "FSK-12"], + "ratingStrings": ["12", "FSK 12", "FSK-12", "FSK12", "ab 12"], "ratingScore": { "score": 12, "subScore": null } }, { - "ratingStrings": ["16", "FSK 16", "FSK-16"], + "ratingStrings": ["16", "FSK 16", "FSK-16", "FSK16", "ab 16"], "ratingScore": { "score": 16, "subScore": null } }, { - "ratingStrings": ["18", "FSK 18", "FSK-18"], + "ratingStrings": ["18", "FSK 18", "FSK-18", "FSK18", "ab 18"], "ratingScore": { "score": 18, "subScore": null diff --git a/Emby.Server.Implementations/Localization/Ratings/fr.json b/Emby.Server.Implementations/Localization/Ratings/fr.json index e8bafd6b87..333e467a08 100644 --- a/Emby.Server.Implementations/Localization/Ratings/fr.json +++ b/Emby.Server.Implementations/Localization/Ratings/fr.json @@ -24,14 +24,14 @@ } }, { - "ratingStrings": ["10"], + "ratingStrings": ["10", "Interdit aux moins de 10 ans"], "ratingScore": { "score": 10, "subScore": null } }, { - "ratingStrings": ["12"], + "ratingStrings": ["12", "–12", "Interdit aux moins de 12 ans"], "ratingScore": { "score": 12, "subScore": null @@ -45,14 +45,14 @@ } }, { - "ratingStrings": ["16"], + "ratingStrings": ["16", "–16", "Interdit aux moins de 16 ans"], "ratingScore": { "score": 16, "subScore": null } }, { - "ratingStrings": ["18"], + "ratingStrings": ["18", "–18", "Interdit aux moins de 18 ans"], "ratingScore": { "score": 18, "subScore": null diff --git a/Emby.Server.Implementations/Localization/Ratings/id.json b/Emby.Server.Implementations/Localization/Ratings/id.json index 8c687c2320..f55e4e4333 100644 --- a/Emby.Server.Implementations/Localization/Ratings/id.json +++ b/Emby.Server.Implementations/Localization/Ratings/id.json @@ -3,14 +3,28 @@ "supportsSubScores": false, "ratings": [ { - "ratingStrings": ["SU"], + "ratingStrings": ["SU", "SU-BO", "Semua Umur"], "ratingScore": { "score": 0, "subScore": null } }, { - "ratingStrings": ["13+"], + "ratingStrings": ["P", "P2+", "P-BO", "Prasekolah"], + "ratingScore": { + "score": 2, + "subScore": null + } + }, + { + "ratingStrings": ["A", "A7+", "A-BO", "Anak"], + "ratingScore": { + "score": 7, + "subScore": null + } + }, + { + "ratingStrings": ["13+", "R", "R13+", "R-BO", "Remaja"], "ratingScore": { "score": 13, "subScore": null @@ -23,6 +37,13 @@ "subScore": null } }, + { + "ratingStrings": ["D", "D18+", "Dewasa"], + "ratingScore": { + "score": 18, + "subScore": null + } + }, { "ratingStrings": ["21+"], "ratingScore": { diff --git a/Jellyfin.Server/Migrations/Routines/20260910120000_MigrateRatingLevels.cs b/Jellyfin.Server/Migrations/Routines/20260910120000_MigrateRatingLevels.cs deleted file mode 100644 index a456acf47d..0000000000 --- a/Jellyfin.Server/Migrations/Routines/20260910120000_MigrateRatingLevels.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Linq; -using Jellyfin.Database.Implementations; -using Jellyfin.Server.ServerSetupApp; -using MediaBrowser.Model.Globalization; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; - -namespace Jellyfin.Server.Migrations.Routines; - -/// -/// Migrate rating levels. -/// -#pragma warning disable CS0618 // Type or member is obsolete -[JellyfinMigration("2026-09-10T12:00:00", nameof(MigrateRatingLevels))] -[JellyfinMigrationBackup(JellyfinDb = true)] -#pragma warning restore CS0618 // Type or member is obsolete -internal class MigrateRatingLevels : IDatabaseMigrationRoutine -{ - private readonly IStartupLogger _logger; - private readonly IDbContextFactory _provider; - private readonly ILocalizationManager _localizationManager; - - public MigrateRatingLevels( - IDbContextFactory provider, - IStartupLogger logger, - ILocalizationManager localizationManager) - { - _provider = provider; - _localizationManager = localizationManager; - _logger = logger; - } - - /// - public void Perform() - { - _logger.LogInformation("Recalculating parental rating levels based on rating string."); - using var context = _provider.CreateDbContext(); - using var transaction = context.Database.BeginTransaction(); - var ratings = context.BaseItems.AsNoTracking().Select(e => e.OfficialRating).Distinct(); - foreach (var rating in ratings) - { - if (string.IsNullOrEmpty(rating)) - { - int? value = null; - context.BaseItems - .Where(e => e.OfficialRating == null || e.OfficialRating == string.Empty) - .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingValue, value)); - context.BaseItems - .Where(e => e.OfficialRating == null || e.OfficialRating == string.Empty) - .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingSubValue, value)); - } - else - { - var ratingValue = _localizationManager.GetRatingScore(rating); - var score = ratingValue?.Score; - var subScore = ratingValue?.SubScore; - context.BaseItems - .Where(e => e.OfficialRating == rating) - .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingValue, score)); - context.BaseItems - .Where(e => e.OfficialRating == rating) - .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingSubValue, subScore)); - } - } - - transaction.Commit(); - } -} diff --git a/Jellyfin.Server/Migrations/Routines/20260915120000_MigrateRatingLevels.cs b/Jellyfin.Server/Migrations/Routines/20260915120000_MigrateRatingLevels.cs new file mode 100644 index 0000000000..903d91e046 --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/20260915120000_MigrateRatingLevels.cs @@ -0,0 +1,68 @@ +using System.Linq; +using Jellyfin.Database.Implementations; +using Jellyfin.Server.ServerSetupApp; +using MediaBrowser.Model.Globalization; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Migrations.Routines; + +/// +/// Migrate rating levels. +/// +#pragma warning disable CS0618 // Type or member is obsolete +[JellyfinMigration("2026-09-15T12:00:00", nameof(MigrateRatingLevels))] +[JellyfinMigrationBackup(JellyfinDb = true)] +#pragma warning restore CS0618 // Type or member is obsolete +internal class MigrateRatingLevels : IDatabaseMigrationRoutine +{ + private readonly IStartupLogger _logger; + private readonly IDbContextFactory _provider; + private readonly ILocalizationManager _localizationManager; + + public MigrateRatingLevels( + IDbContextFactory provider, + IStartupLogger logger, + ILocalizationManager localizationManager) + { + _provider = provider; + _localizationManager = localizationManager; + _logger = logger; + } + + /// + public void Perform() + { + _logger.LogInformation("Recalculating parental rating levels based on rating string."); + using var context = _provider.CreateDbContext(); + using var transaction = context.Database.BeginTransaction(); + var ratings = context.BaseItems.AsNoTracking().Select(e => e.OfficialRating).Distinct(); + foreach (var rating in ratings) + { + if (string.IsNullOrEmpty(rating)) + { + int? value = null; + context.BaseItems + .Where(e => e.OfficialRating == null || e.OfficialRating == string.Empty) + .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingValue, value)); + context.BaseItems + .Where(e => e.OfficialRating == null || e.OfficialRating == string.Empty) + .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingSubValue, value)); + } + else + { + var ratingValue = _localizationManager.GetRatingScore(rating); + var score = ratingValue?.Score; + var subScore = ratingValue?.SubScore; + context.BaseItems + .Where(e => e.OfficialRating == rating) + .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingValue, score)); + context.BaseItems + .Where(e => e.OfficialRating == rating) + .ExecuteUpdate(f => f.SetProperty(e => e.InheritedParentalRatingSubValue, subScore)); + } + } + + transaction.Commit(); + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index 93014e7244..4e57f32b57 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -6,6 +6,7 @@ using BitFaster.Caching; using Emby.Server.Implementations.Localization; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Configuration; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Moq; using Xunit; @@ -180,7 +181,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(24, ratings.Count); + Assert.Equal(34, ratings.Count); var fsk = ratings.FirstOrDefault(x => x.Name.Equals("FSK-12", StringComparison.Ordinal)); Assert.NotNull(fsk); @@ -209,6 +210,37 @@ namespace Jellyfin.Server.Implementations.Tests.Localization [InlineData("it-vm18", "IT", 18, null)] // Rating strings are case insensitive [InlineData("VM 18", "IT", 18, null)] [InlineData("Vietato ai minori di 18 anni", "IT", 18, null)] + [InlineData("ATP", "AR", 0, null)] + [InlineData("SAM 13", "AR", 13, null)] + [InlineData("SAM 16", "AR", 16, null)] + [InlineData("SAM 18", "AR", 18, null)] + [InlineData("SAM13", "AR", 13, null)] // Written without a space + [InlineData("AR-SAM 16", "AR", 16, null)] // Country prefix stripped against the configured country + [InlineData("AR-SAM 13", "US", 13, null)] // Country prefix resolved via the separator fallback + [InlineData("AR-SAM 18", "US", 18, null)] + [InlineData("AR-SAM13", "US", 13, null)] + [InlineData("SAM 18 C", "AR", 1001, null)] // Condicionada, same as "C" + [InlineData("Interdit aux moins de 12 ans", "FR", 12, null)] + [InlineData("Interdit aux moins de 18 ans", "FR", 18, null)] + [InlineData("X 18+", "AU", 1000, 0)] // Official spelling of the Australian X rating + [InlineData("X18+", "AU", 1000, 0)] + [InlineData("FSK18", "DE", 18, null)] // Written without a space + [InlineData("ab 18", "DE", 18, null)] // Written as the minimum age + [InlineData("DE:ab 6", "DE", 6, null)] + [InlineData("–12", "FR", 12, null)] // The CNC writes its minimum ages with an en dash + [InlineData("–16", "FR", 16, null)] + [InlineData("–18", "FR", 18, null)] + [InlineData("SU", "ID", 0, null)] // Indonesian broadcast classifications (KPI) + [InlineData("P", "ID", 2, null)] + [InlineData("A", "ID", 7, null)] + [InlineData("R", "ID", 13, null)] + [InlineData("D", "ID", 18, null)] + [InlineData("D18+", "ID", 18, null)] // Written with the minimum age, as broadcast since 2016 + [InlineData("R-BO", "ID", 13, null)] // Parental guidance does not change the age group + [InlineData("Dewasa", "ID", 18, null)] + [InlineData("ID-D", "ID", 18, null)] // TMDB style country prefix + [InlineData("ID-D", "US", 18, null)] // Country prefix resolved via the separator fallback + [InlineData("P", "US", 1000, null)] // Ambiguous outside Indonesia, preferred as the Portuguese "Pornográfico" public async Task GetRatingLevel_GivenValidString_Success(string value, string countryCode, int? expectedScore, int? expectedSubScore) { var localizationManager = Setup(new ServerConfiguration() @@ -253,6 +285,12 @@ namespace Jellyfin.Server.Implementations.Tests.Localization [InlineData("12", 12, null)] [InlineData("42", 42, null)] [InlineData("9999", 9999, null)] + // The French CNC writes minimum ages as "-12" ("not for under 12s"). Parsing that as -12 would + // put the item below every MaxParentalRatingScore and bypass parental control entirely. + [InlineData("-10", 10, null)] + [InlineData("-12", 12, null)] + [InlineData("-16", 16, null)] + [InlineData("-18", 18, null)] public async Task GetRatingLevel_GivenValidAge_Success(string value, int? expectedScore, int? expectedSubScore) { var localizationManager = Setup(new ServerConfiguration { MetadataCountryCode = "nl" }); @@ -322,6 +360,62 @@ namespace Jellyfin.Server.Implementations.Tests.Localization Assert.Equal(expectedSubScore, score.SubScore); } + [Theory] + // Providers list every spelling of a rating in a single field. Splitting such a list by its country + // prefix pairs the first entry's country with the last entry's rating, so it has to be split by '/' first. + [InlineData("DE:FSK 18 / DE:FSK-18 / DE:FSK18 / DE:18 / DE:ab 18", "de", 18, null)] + [InlineData("SE:15 / SE:15+ / SE:Från 15 år", "de", 15, null)] + [InlineData("FR:16 / US:12", "de", 16, null)] // The first entry that resolves wins + public async Task GetRatingScore_CountryPrefixedList_UsesFirstResolvingEntry(string value, string countryCode, int expectedScore, int? expectedSubScore) + { + var localizationManager = Setup(new ServerConfiguration { MetadataCountryCode = countryCode }); + await localizationManager.LoadAll(); + + var score = localizationManager.GetRatingScore(value); + + Assert.NotNull(score); + Assert.Equal(expectedScore, score.Score); + Assert.Equal(expectedSubScore, score.SubScore); + } + + [Fact] + public async Task GetRatingScore_ResolvedCountryPrefixedList_DoesNotWarn() + { + var logger = new Mock>(); + var localizationManager = Setup(new ServerConfiguration { MetadataCountryCode = "de" }, logger.Object); + await localizationManager.LoadAll(); + + Assert.NotNull(localizationManager.GetRatingScore("DE:FSK 18 / DE:FSK-18 / DE:FSK18 / DE:18 / DE:ab 18")); + + logger.Verify( + x => x.Log( + LogLevel.Warning, + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>()), + Times.Never); + } + + [Fact] + public async Task GetRatingScore_ListWithoutKnownRating_WarnsOnce() + { + var logger = new Mock>(); + var localizationManager = Setup(new ServerConfiguration { MetadataCountryCode = "de" }, logger.Object); + await localizationManager.LoadAll(); + + Assert.Null(localizationManager.GetRatingScore("DE:Unbekannt / DE:Unsinn")); + + logger.Verify( + x => x.Log( + LogLevel.Warning, + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>()), + Times.Once); + } + [Theory] [InlineData("-NO RATING SHOWN-")] [InlineData(":NO RATING SHOWN:")] @@ -525,12 +619,12 @@ namespace Jellyfin.Server.Implementations.Tests.Localization Assert.Contains(supported, c => c.Name.Equals("es-419", StringComparison.OrdinalIgnoreCase)); } - private LocalizationManager Setup(ServerConfiguration config) + private LocalizationManager Setup(ServerConfiguration config, ILogger? logger = null) { var mockConfiguration = new Mock(); mockConfiguration.SetupGet(x => x.Configuration).Returns(config); - return new LocalizationManager(mockConfiguration.Object, new NullLogger()); + return new LocalizationManager(mockConfiguration.Object, logger ?? new NullLogger()); } } } -- cgit v1.2.3 From 91ac66ce0b02faac5a646bcbcdc4d505872fea43 Mon Sep 17 00:00:00 2001 From: allexjuang Date: Sun, 27 Sep 2026 16:30:48 -0400 Subject: Backport pull request #18134 from jellyfin/release-12.z update tw.json (rating) Original-merge: 440d975875b79afbf752d0af7dd62c7ff4821a7e Merged-by: crobibero Backported-by: Cody Robibero --- Emby.Server.Implementations/Localization/Ratings/tw.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Ratings/tw.json b/Emby.Server.Implementations/Localization/Ratings/tw.json index a7869c1221..9af7dd3128 100644 --- a/Emby.Server.Implementations/Localization/Ratings/tw.json +++ b/Emby.Server.Implementations/Localization/Ratings/tw.json @@ -3,35 +3,35 @@ "supportsSubScores": false, "ratings": [ { - "ratingStrings": ["0+"], + "ratingStrings": ["0+", "普遍級", "普"], "ratingScore": { "score": 0, "subScore": null } }, { - "ratingStrings": ["6+"], + "ratingStrings": ["6+", "保護級", "護"], "ratingScore": { "score": 6, "subScore": null } }, { - "ratingStrings": ["12+"], + "ratingStrings": ["12+", "輔12級", "輔導12歲級", "輔導級"], "ratingScore": { "score": 12, "subScore": null } }, { - "ratingStrings": ["15+"], + "ratingStrings": ["15+", "輔15級", "輔導15歲級"], "ratingScore": { "score": 15, "subScore": null } }, { - "ratingStrings": ["18+"], + "ratingStrings": ["18+", "限制級", "限"], "ratingScore": { "score": 18, "subScore": null -- cgit v1.2.3 From 3655c6861df2bab1707143e11be9bfd6ac382ba3 Mon Sep 17 00:00:00 2001 From: Acrotos Date: Sun, 27 Sep 2026 15:10:07 -0400 Subject: Translated using Weblate (Romanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ro/ --- Emby.Server.Implementations/Localization/Core/ro.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json index dccec8067d..6baaeca524 100644 --- a/Emby.Server.Implementations/Localization/Core/ro.json +++ b/Emby.Server.Implementations/Localization/Core/ro.json @@ -120,5 +120,6 @@ "NameExtraShort": "Scurt", "NameExtraThemeSong": "Audio de Fundal", "NameExtraThemeVideo": "Video de Fundal", - "NameExtraTrailer": "Trailer" + "NameExtraTrailer": "Trailer", + "NameExtraUnknown": "Extra" } -- cgit v1.2.3 From 96bca6f0bdde6e4cf282a4bdef6867c741ae3581 Mon Sep 17 00:00:00 2001 From: krvi <27792771+krvi@users.noreply.github.com> Date: Mon, 28 Sep 2026 16:59:46 -0400 Subject: Translated using Weblate (Faroese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fo/ --- Emby.Server.Implementations/Localization/Core/fo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/fo.json b/Emby.Server.Implementations/Localization/Core/fo.json index acb78290f3..080fa69524 100644 --- a/Emby.Server.Implementations/Localization/Core/fo.json +++ b/Emby.Server.Implementations/Localization/Core/fo.json @@ -69,7 +69,7 @@ "TasksApplicationCategory": "Nýtsluskipan", "NotificationOptionApplicationUpdateAvailable": "Skipanardagføring er tøk", "NotificationOptionApplicationUpdateInstalled": "Skipanardagføring var innløgd", - "UserStoppedPlayingItemWithValues": "{0} er liðugur at spæla {1} á {2}", + "UserStoppedPlayingItemWithValues": "{0} gavst at spæla {1} á {2}", "HomeVideos": "Heimaupptøkur", "StartupEmbyServerIsLoading": "Jellyfin-ambætarin er undir byrjanarinnlesing. Vinaliga royn aftur um eitt bil.", "UserOfflineFromDevice": "{0} breyt av frá {1}", -- cgit v1.2.3 From 52a680c578f1af888ebb74cefcb89b736f9c5738 Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Tue, 29 Sep 2026 07:48:45 -0400 Subject: Translated using Weblate (Japanese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ja/ --- Emby.Server.Implementations/Localization/Core/ja.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json index a93ee802a1..12cdb4b80f 100644 --- a/Emby.Server.Implementations/Localization/Core/ja.json +++ b/Emby.Server.Implementations/Localization/Core/ja.json @@ -109,5 +109,17 @@ "CleanupUserDataTaskDescription": "90日以上存在しないメディアに対して、視聴状態やお気に入り状態などのユーザーデータをすべて削除します。", "LyricDownloadFailureFromForItem": "歌詞", "Original": "オリジナル", - "NameExtraThemeVideo": "テーマビデオ" + "NameExtraThemeVideo": "テーマビデオ", + "NameExtraBehindTheScenes": "舞台裏", + "NameExtraClip": "クリップ", + "NameExtraDeletedScene": "未公開シーン", + "NameExtraFeaturette": "短編", + "NameExtraInterview": "インタビュー", + "NameExtraNumbered": "{0} {1}", + "NameExtraSample": "サンプル", + "NameExtraScene": "シーン", + "NameExtraShort": "ショート", + "NameExtraThemeSong": "テーマ曲", + "NameExtraTrailer": "予告編", + "NameExtraUnknown": "特典" } -- cgit v1.2.3 From 4e0a2b5dfb5d68a32b2b00514844e949f0b0fb3a Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Thu, 1 Oct 2026 08:49:09 -0400 Subject: Translated using Weblate (Japanese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ja/ --- Emby.Server.Implementations/Localization/Core/ja.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json index 12cdb4b80f..24008575d4 100644 --- a/Emby.Server.Implementations/Localization/Core/ja.json +++ b/Emby.Server.Implementations/Localization/Core/ja.json @@ -80,10 +80,10 @@ "TaskUpdatePlugins": "プラグインの更新", "TaskRefreshPeopleDescription": "メディアライブラリー内の俳優や監督のメタデータを更新します。", "TaskRefreshPeople": "俳優や監督のデータの更新", - "TaskDownloadMissingSubtitlesDescription": "メタデータ構成に基づいて、欠落している字幕をインターネットで検索する。", + "TaskDownloadMissingSubtitlesDescription": "メタデータ設定をもとに、欠落している字幕をインターネットで検索します。", "TaskRefreshChapterImagesDescription": "チャプターのあるビデオのサムネールを作成します。", "TaskRefreshChapterImages": "チャプター画像を抽出する", - "TaskDownloadMissingSubtitles": "不足している字幕をダウンロードする", + "TaskDownloadMissingSubtitles": "欠けている字幕をダウンロード", "TaskCleanActivityLogDescription": "設定された期間よりも古いアクティビティの履歴を削除します。", "TaskCleanActivityLog": "アクティビティの履歴を消去", "Undefined": "未定義", @@ -103,7 +103,7 @@ "TaskExtractMediaSegments": "メディアセグメントを読み取る", "TaskMoveTrickplayImages": "Trickplayの画像を移動", "TaskMoveTrickplayImagesDescription": "ライブラリ設定によりTrickplayのファイルを移動。", - "TaskDownloadMissingLyrics": "失われた歌詞をダウンロード", + "TaskDownloadMissingLyrics": "欠けている歌詞をダウンロード", "TaskExtractMediaSegmentsDescription": "MediaSegment 対応プラグインからメディア セグメントを抽出または取得します。", "CleanupUserDataTask": "ユーザーデータのクリーンアップタスク", "CleanupUserDataTaskDescription": "90日以上存在しないメディアに対して、視聴状態やお気に入り状態などのユーザーデータをすべて削除します。", -- cgit v1.2.3 From 305a96471509f11de4d3a93e1ea268d4fd2af76c Mon Sep 17 00:00:00 2001 From: SecularSteve Date: Thu, 1 Oct 2026 07:51:21 -0400 Subject: Translated using Weblate (Bosnian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/bs/ --- Emby.Server.Implementations/Localization/Core/bs.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/bs.json b/Emby.Server.Implementations/Localization/Core/bs.json index 5686807d9a..98dcc68ed1 100644 --- a/Emby.Server.Implementations/Localization/Core/bs.json +++ b/Emby.Server.Implementations/Localization/Core/bs.json @@ -118,5 +118,8 @@ "NameExtraShort": "Kratko", "NameExtraThemeSong": "Tema", "NameExtraThemeVideo": "Tematski video", - "NameExtraTrailer": "Najava" + "NameExtraTrailer": "Najava", + "LyricDownloadFailureFromForItem": "Tekstovi nisu uspješno preuzeti sa {0} za {1}", + "NameExtraUnknown": "Ekstra", + "Original": "Original" } -- cgit v1.2.3 From 00bb05e2c0a9ec62dafa8fc0ccc14c6ff16eab35 Mon Sep 17 00:00:00 2001 From: krvi <27792771+krvi@users.noreply.github.com> Date: Sat, 3 Oct 2026 23:10:33 -0400 Subject: Translated using Weblate (Faroese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fo/ --- Emby.Server.Implementations/Localization/Core/fo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/fo.json b/Emby.Server.Implementations/Localization/Core/fo.json index 080fa69524..b9964b02b8 100644 --- a/Emby.Server.Implementations/Localization/Core/fo.json +++ b/Emby.Server.Implementations/Localization/Core/fo.json @@ -49,7 +49,7 @@ "Movies": "Filmar", "MixedContent": "Blandað innihald", "Music": "Tónleikur", - "UserStartedPlayingItemWithValues": "{0} spælur {1} á {2}", + "UserStartedPlayingItemWithValues": "{0} spælir {1} á {2}", "HeaderContinueWatching": "Hald áfram at hyggja", "MusicVideos": "Sjónbandaløg", "TaskUpdatePluginsDescription": "Niðurtekur og innleggur dagføringar til ískoytisforrit ið eru stillaði til at dagførast sjálvvirkandi.", -- cgit v1.2.3 From 21034b5768db7f0536e54fc899e0adc0e58ba091 Mon Sep 17 00:00:00 2001 From: spurdl <60575077+Spurdl@users.noreply.github.com> Date: Mon, 5 Oct 2026 02:31:47 -0400 Subject: Translated using Weblate (Finnish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fi/ --- Emby.Server.Implementations/Localization/Core/fi.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json index 9a453120dd..12d50cf89a 100644 --- a/Emby.Server.Implementations/Localization/Core/fi.json +++ b/Emby.Server.Implementations/Localization/Core/fi.json @@ -120,5 +120,6 @@ "NameExtraShort": "Lyhytfilmi", "NameExtraThemeSong": "Tunnusmusiikki", "NameExtraThemeVideo": "Tunnusvideo", - "NameExtraTrailer": "Traileri" + "NameExtraTrailer": "Traileri", + "NameExtraUnknown": "Lisät" } -- cgit v1.2.3 From e0c4a0c43ff576b6c2b5c08f100f08ab5f7fa971 Mon Sep 17 00:00:00 2001 From: Natsuki Date: Thu, 8 Oct 2026 06:12:36 -0400 Subject: Translated using Weblate (Armenian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hy/ --- Emby.Server.Implementations/Localization/Core/hy.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/hy.json b/Emby.Server.Implementations/Localization/Core/hy.json index 80be71ed70..09f1bfe79c 100644 --- a/Emby.Server.Implementations/Localization/Core/hy.json +++ b/Emby.Server.Implementations/Localization/Core/hy.json @@ -29,5 +29,15 @@ "Default": "Լռելյայն", "Favorites": "Ընտրյալ", "Forced": "Ստիպուած", - "Genres": "Ոճ" + "Genres": "Ոճ", + "HeaderFavoriteEpisodes": "Սիրելի դրվագներ", + "HeaderFavoriteShows": "Սիրելի հաղորդումներ", + "HeaderLiveTV": "Ուղիղ եթեր", + "HeaderNextUp": "Հաջորդը", + "HomeVideos": "Տնային տեսանյութեր", + "LabelRunningTimeValue": "Տևողությունը: {0}", + "Latest": "Վերջինը", + "LyricDownloadFailureFromForItem": "Չհաջողվեց ներբեռնել {1}-ի երգի բառերը {0}-ից", + "MixedContent": "Խառը բովանդակություն", + "CleanupUserDataTaskDescription": "Մաքրում է օգտատիրոջ բոլոր տվյալները (դիտման կարգավիճակ, «սիրելի» նշում և այլն) այն մեդիաֆայլերից, որոնք առնվազն 90 օր է՝ առկա չեն։" } -- cgit v1.2.3 From 5a2eb9fb0936e53d97e9477e7fd700a8e12cb401 Mon Sep 17 00:00:00 2001 From: slam0121 Date: Mon, 12 Oct 2026 07:32:17 -0400 Subject: Translated using Weblate (Korean) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ko/ --- Emby.Server.Implementations/Localization/Core/ko.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index 1f16a90842..d43b7c15f4 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -111,5 +111,10 @@ "Original": "원본", "NameExtraClip": "클립", "NameExtraDeletedScene": "삭제된 장면", - "NameExtraInterview": "인터뷰" + "NameExtraInterview": "인터뷰", + "NameExtraNumbered": "{0} {1}", + "NameExtraSample": "샘플", + "NameExtraScene": "장면", + "NameExtraTrailer": "예고편", + "NameExtraUnknown": "부가영상" } -- cgit v1.2.3 From 61f9a9df23a46175de2b8bac0a6da47f807cafd0 Mon Sep 17 00:00:00 2001 From: slam0121 Date: Mon, 12 Oct 2026 07:32:40 -0400 Subject: Translated using Weblate (Korean) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ko/ --- Emby.Server.Implementations/Localization/Core/ko.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index d43b7c15f4..280d96effe 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -116,5 +116,6 @@ "NameExtraSample": "샘플", "NameExtraScene": "장면", "NameExtraTrailer": "예고편", - "NameExtraUnknown": "부가영상" + "NameExtraUnknown": "부가영상", + "NameExtraThemeSong": "주제가" } -- cgit v1.2.3 From aec42b345e1a7da25ee5492183f12209338df688 Mon Sep 17 00:00:00 2001 From: Riccardo Date: Wed, 14 Oct 2026 08:32:04 -0400 Subject: Translated using Weblate (Norwegian Bokmål) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nb_NO/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Emby.Server.Implementations/Localization/Core/nb.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index 735bc2c793..6da32f56a0 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -116,5 +116,8 @@ "NameExtraScene": "Scene", "NameExtraThemeSong": "Tema-låt", "NameExtraThemeVideo": "Tema-video", - "NameExtraTrailer": "Trailer" + "NameExtraTrailer": "Trailer", + "LyricDownloadFailureFromForItem": "Kunne ikke laste ned sangtekster fra {0} for {1}", + "NameExtraClip": "Klipp", + "NameExtraShort": "Kortfilm" } -- cgit v1.2.3 From ff34a0445d476b817d3080aacd1c8a12e3e01592 Mon Sep 17 00:00:00 2001 From: Naina Prakash Date: Fri, 16 Oct 2026 20:35:32 -0400 Subject: Translated using Weblate (Hindi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hi/ --- Emby.Server.Implementations/Localization/Core/hi.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json index f4b1f86d1d..3f1986a07b 100644 --- a/Emby.Server.Implementations/Localization/Core/hi.json +++ b/Emby.Server.Implementations/Localization/Core/hi.json @@ -50,7 +50,7 @@ "Photos": "तस्वीरें", "External": "बाहरी", "PluginUpdatedWithName": "{0} अपडेट हुए", - "UserStartedPlayingItemWithValues": "{0} {2} पर {1} खेल रहे हैं", + "UserStartedPlayingItemWithValues": "{0} {2} पर {1} चला रहे हैं", "UserStoppedPlayingItemWithValues": "{0} ने {2} पर {1} खेलना खत्म किया", "StartupEmbyServerIsLoading": "जेलीफ़िन सर्वर लोड हो रहा है। कृपया शीघ्र ही पुन: प्रयास करें।", "UserCreatedWithName": "उपयोगकर्ता {0} बनाया गया", @@ -68,8 +68,8 @@ "HearingImpaired": "मूक बधिर", "TasksMaintenanceCategory": "रखरखाव", "VersionNumber": "{0} पाठान्तर", - "TasksLibraryCategory": "संग्रहालय", - "TaskOptimizeDatabase": "जानकारी प्रवृद्धि", + "TasksLibraryCategory": "लाइब्रेरी", + "TaskOptimizeDatabase": "डेटाबेस ट्यून करें", "TaskDownloadMissingSubtitles": "लापता अनुलेख डाउनलोड करें", "TaskRefreshLibrary": "माध्यम संग्राहत को छाने", "TaskCleanActivityLog": "क्रियाकलाप लॉग साफ करें", -- cgit v1.2.3 From dbb1f5dd5d0c73825a511a91aef051b254c637cb Mon Sep 17 00:00:00 2001 From: Naina Prakash Date: Fri, 16 Oct 2026 21:09:40 -0400 Subject: Translated using Weblate (Hindi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hi/ --- .../Localization/Core/hi.json | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json index 3f1986a07b..491345b380 100644 --- a/Emby.Server.Implementations/Localization/Core/hi.json +++ b/Emby.Server.Implementations/Localization/Core/hi.json @@ -1,5 +1,5 @@ { - "HeaderNextUp": "इसके बाद", + "HeaderNextUp": "इसके आगे", "HeaderLiveTV": "लाइव टीवी", "HeaderFavoriteShows": "पसंदीदा शो", "HeaderFavoriteEpisodes": "पसंदीदा प्रकरण", @@ -12,7 +12,7 @@ "Default": "प्राथमिक", "Collections": "संग्रह", "ChapterNameValue": "अध्याय {0}", - "Books": "पुस्तकें", + "Books": "किताबें", "AuthenticationSucceededWithUserName": "{0} सफलतापूर्वक प्रमाणित किया गया", "Artists": "कलाकार", "AppDeviceValues": "एप: {0}, उपकरण: {1}", @@ -24,7 +24,7 @@ "NotificationOptionAudioPlayback": "संगीत शुरू कर दिया गया", "NotificationOptionCameraImageUploaded": "कैमरा फोटो अपलोड किया गया", "NotificationOptionApplicationUpdateInstalled": "एप्लीकेशन अपडेट इनस्टॉल कर दिया है", - "NotificationOptionApplicationUpdateAvailable": "एप्लीकेशन अपडेट उपलभ्द है", + "NotificationOptionApplicationUpdateAvailable": "एप्लीकेशन अपडेट उपलब्ध है", "NewVersionIsAvailable": "जेलीफिन सर्वर का एक नया वर्जन डाउनलोड के लिए उपलब्ध है।", "NameSeasonUnknown": "अनजान भाग", "NameSeasonNumber": "भाग {0}", @@ -40,7 +40,7 @@ "NotificationOptionUserLockedOut": "उपयोगकर्ता लॉक हो गया", "NotificationOptionTaskFailed": "निर्धारित कार्य विफलता", "NotificationOptionServerRestartRequired": "सर्वर पुनरारंभ आवश्यक है", - "NotificationOptionPluginUpdateInstalled": "प्लगइन अद्यतन स्थापित", + "NotificationOptionPluginUpdateInstalled": "प्लगिन अपडेट इंस्टॉल हो गया है", "NotificationOptionNewLibraryContent": "नई सामग्री जोड़ी गई", "LabelRunningTimeValue": "चलने का समय: {0}", "Inherit": "इनहेरिट", @@ -52,7 +52,7 @@ "PluginUpdatedWithName": "{0} अपडेट हुए", "UserStartedPlayingItemWithValues": "{0} {2} पर {1} चला रहे हैं", "UserStoppedPlayingItemWithValues": "{0} ने {2} पर {1} खेलना खत्म किया", - "StartupEmbyServerIsLoading": "जेलीफ़िन सर्वर लोड हो रहा है। कृपया शीघ्र ही पुन: प्रयास करें।", + "StartupEmbyServerIsLoading": "जेलीफ़िन सर्वर लोड हो रहा है। कृपया थोड़ी देर में दोबारा कोशिश करें।", "UserCreatedWithName": "उपयोगकर्ता {0} बनाया गया", "UserDownloadingItemWithValues": "{0} डाउनलोड हो रहा है", "UserOfflineFromDevice": "{0} {1} से डिस्कनेक्ट हो गया है", @@ -82,11 +82,11 @@ "TaskRefreshLibraryDescription": "नई फ़ाइलों के लिए आपकी मीडिया लाइब्रेरी को स्कैन करता है और मेटाडेटा को ताज़ा करता है।", "TaskCleanLogs": "स्वच्छ लॉग निर्देशिका", "TaskUpdatePluginsDescription": "प्लगइन्स के लिए अपडेट डाउनलोड और इंस्टॉल करें जो स्वचालित रूप से अपडेट करने के लिए कॉन्फ़िगर किए गए हैं।", - "TaskCleanTranscode": "स्वच्छ ट्रांसकोड निर्देशिका", - "TaskCleanTranscodeDescription": "एक दिन से अधिक पुरानी ट्रांसकोड फ़ाइलें हटाता है.", + "TaskCleanTranscode": "ट्रांसकोड डाइरेक्टरी साफ़ करें", + "TaskCleanTranscodeDescription": "एक दिन से ज़्यादा पुरानी ट्रांसकोड फ़ाइलें हटाता है।", "TaskRefreshChannelsDescription": "इंटरनेट चैनल की जानकारी को ताज़ा करता है।", - "TaskOptimizeDatabaseDescription": "डेटाबेस को कॉम्पैक्ट करता है और मुक्त स्थान को छोटा करता है। लाइब्रेरी को स्कैन करने के बाद इस कार्य को चलाने या अन्य परिवर्तन करने से जो डेटाबेस संशोधनों को लागू करते हैं, प्रदर्शन में सुधार कर सकते हैं।", - "TaskRefreshChannels": "इंटरनेट चैनल की जानकारी को ताज़ा करता है", + "TaskOptimizeDatabaseDescription": "डेटाबेस को छोटा करके जगह ख़ाली करता है। लाइब्रेरी का निरीक्षण या डेटाबेस में बदलाव के बाद इस कार्य को चलाने से कार्यकुशलता बढ़ सकती है।", + "TaskRefreshChannels": "चैनल ताज़ा करें", "TaskRefreshChapterImages": "अध्याय छवियाँ निकालें", "TaskCleanLogsDescription": "{0} दिन से अधिक पुरानी लॉग फ़ाइलें हटाता है।", "TaskCleanCacheDescription": "उन कैश फ़ाइलों को हटाता है जिनकी अब सिस्टम को आवश्यकता नहीं है।", @@ -99,8 +99,8 @@ "TaskRefreshTrickplayImagesDescription": "नियत संग्रहों में चलचित्रों का ट्रीकप्लै दर्शनों को सृजन करे.", "TaskAudioNormalization": "श्रव्य सामान्यीकरण", "TaskAudioNormalizationDescription": "श्रव्य सामान्यीकरण के लिए फाइलें अन्वेषण करें।", - "TaskDownloadMissingLyrics": "लापता गानों के बोल डाउनलोड करेँ", - "TaskDownloadMissingLyricsDescription": "गानों के बोल डाउनलोड करता है", + "TaskDownloadMissingLyrics": "बाकी बोल डाउनलोड करें", + "TaskDownloadMissingLyricsDescription": "गीत के बोल डाउनलोड करें", "TaskExtractMediaSegments": "मीडिया सेगमेंट स्कैन", "TaskExtractMediaSegmentsDescription": "मीडियासेगमेंट सक्षम प्लगइन्स से मीडिया सेगमेंट निकालता है या प्राप्त करता है।", "TaskMoveTrickplayImages": "ट्रिकप्ले छवि स्थान माइग्रेट करें", @@ -110,7 +110,7 @@ "LyricDownloadFailureFromForItem": "{0} के लिए {1} से बोल (Lyrics) डाउनलोड करने में विफल रहा", "NameExtraBehindTheScenes": "परदे के पीछे", "NameExtraClip": "क्लिप", - "NameExtraDeletedScene": "हटाया गया दृश्य", + "NameExtraDeletedScene": "काटे गए सीन", "NameExtraFeaturette": "फीचरेट", "NameExtraInterview": "साक्षात्कार", "NameExtraNumbered": "{0} {1}", @@ -121,5 +121,5 @@ "NameExtraThemeVideo": "थीम वीडियो", "NameExtraTrailer": "ट्रेलर", "NameExtraUnknown": "अतिरिक्त", - "CleanupUserDataTaskDescription": "कम से कम 90 दिनों से अनुपस्थित मीडिया से सभी उपयोगकर्ता डेटा (देखने की स्थिति, पसंदीदा स्थिति आदि) को साफ़ करता है।" + "CleanupUserDataTaskDescription": "९० दिन या उससे ज़्यादा से ग़ायब मीडिया से सभी यूज़र डेटा (देखें गए, पसंदीदा आदि) को हटाता है।" } -- cgit v1.2.3 From 6f1053ad5f00cb5827a1a981226547e07bad589a Mon Sep 17 00:00:00 2001 From: Naina Prakash Date: Fri, 16 Oct 2026 22:25:08 -0400 Subject: Translated using Weblate (Hindi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hi/ --- .../Localization/Core/hi.json | 110 ++++++++++----------- 1 file changed, 55 insertions(+), 55 deletions(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json index 491345b380..766a09ffc9 100644 --- a/Emby.Server.Implementations/Localization/Core/hi.json +++ b/Emby.Server.Implementations/Localization/Core/hi.json @@ -2,103 +2,103 @@ "HeaderNextUp": "इसके आगे", "HeaderLiveTV": "लाइव टीवी", "HeaderFavoriteShows": "पसंदीदा शो", - "HeaderFavoriteEpisodes": "पसंदीदा प्रकरण", - "HeaderContinueWatching": "देखना जारी रखें", + "HeaderFavoriteEpisodes": "पसंदीदा भाग", + "HeaderContinueWatching": "देखते रहें", "Genres": "शैलियां", - "Forced": "बलपूर्वक", + "Forced": "ज़रूरी", "Folders": "फ़ोल्डर", "Favorites": "पसंदीदा", "FailedLoginAttemptWithUserName": "{0} से संप्रवेश असफल हुआ", - "Default": "प्राथमिक", + "Default": "मूल", "Collections": "संग्रह", "ChapterNameValue": "अध्याय {0}", "Books": "किताबें", - "AuthenticationSucceededWithUserName": "{0} सफलतापूर्वक प्रमाणित किया गया", + "AuthenticationSucceededWithUserName": "{0} प्रमाणीकरण सफल हुआ", "Artists": "कलाकार", "AppDeviceValues": "एप: {0}, उपकरण: {1}", "NotificationOptionPluginUninstalled": "प्लगइन अनइंस्टाल हो गया", "NotificationOptionPluginInstalled": "प्लगइन इनस्टॉल हो गया", - "NotificationOptionPluginError": "प्लगइन फ़ैल हो गया", - "NotificationOptionInstallationFailed": "इंस्टालेशन फ़ैल हो गया", + "NotificationOptionPluginError": "प्लगिन असफल", + "NotificationOptionInstallationFailed": "इंस्टालेशन असफल", "NotificationOptionAudioPlaybackStopped": "संगीत बंद कर दिया गया", - "NotificationOptionAudioPlayback": "संगीत शुरू कर दिया गया", - "NotificationOptionCameraImageUploaded": "कैमरा फोटो अपलोड किया गया", - "NotificationOptionApplicationUpdateInstalled": "एप्लीकेशन अपडेट इनस्टॉल कर दिया है", + "NotificationOptionAudioPlayback": "आवाज़ चलनी शुरू हो गई", + "NotificationOptionCameraImageUploaded": "कैमरा फोटो अपलोड हो गई", + "NotificationOptionApplicationUpdateInstalled": "एप्लीकेशन अपडेट इनस्टॉल हो गया", "NotificationOptionApplicationUpdateAvailable": "एप्लीकेशन अपडेट उपलब्ध है", "NewVersionIsAvailable": "जेलीफिन सर्वर का एक नया वर्जन डाउनलोड के लिए उपलब्ध है।", - "NameSeasonUnknown": "अनजान भाग", - "NameSeasonNumber": "भाग {0}", - "NameInstallFailed": "{0} इनस्टॉल करते समय फेल हो गया है", + "NameSeasonUnknown": "अनजान कड़ी", + "NameSeasonNumber": "कड़ी {0}", + "NameInstallFailed": "{0} इंस्टॉलेशन असफल रहा", "MusicVideos": "संगीत वीडियो", "Music": "संगीत", - "Movies": "फ़िल्म", + "Movies": "फ़िल्में", "MixedContent": "मिला-जुला कंटेंट", "Latest": "सबसे नया", "LabelIpAddressValue": "आई पी एड्रेस: {0}", - "HomeVideos": "होम चलचित्र", - "NotificationOptionVideoPlayback": "वीडियो प्लेबैक शुरू हुआ", - "NotificationOptionUserLockedOut": "उपयोगकर्ता लॉक हो गया", - "NotificationOptionTaskFailed": "निर्धारित कार्य विफलता", - "NotificationOptionServerRestartRequired": "सर्वर पुनरारंभ आवश्यक है", + "HomeVideos": "घर के वीडियो", + "NotificationOptionVideoPlayback": "वीडियो चल गया", + "NotificationOptionUserLockedOut": "यूज़र लॉक हो गया", + "NotificationOptionTaskFailed": "निर्धारित कार्य विफल रहा", + "NotificationOptionServerRestartRequired": "सर्वर रिस्टार्ट की ज़रूरत है", "NotificationOptionPluginUpdateInstalled": "प्लगिन अपडेट इंस्टॉल हो गया है", - "NotificationOptionNewLibraryContent": "नई सामग्री जोड़ी गई", - "LabelRunningTimeValue": "चलने का समय: {0}", - "Inherit": "इनहेरिट", - "NotificationOptionVideoPlaybackStopped": "चलचित्र रुका हुआ", - "PluginUninstalledWithName": "{0} अनइंस्टॉल हुए", - "PluginInstalledWithName": "{0} इंस्टॉल हुए", + "NotificationOptionNewLibraryContent": "नई चीजें जोड़ी गई", + "LabelRunningTimeValue": "अवधि : {0}", + "Inherit": "ऊपर से लागू", + "NotificationOptionVideoPlaybackStopped": "वीडियो बंद हो गया", + "PluginUninstalledWithName": "{0} अनइंस्टॉल हो गए", + "PluginInstalledWithName": "{0} इंस्टॉल हो गए", "Photos": "तस्वीरें", "External": "बाहरी", "PluginUpdatedWithName": "{0} अपडेट हुए", "UserStartedPlayingItemWithValues": "{0} {2} पर {1} चला रहे हैं", - "UserStoppedPlayingItemWithValues": "{0} ने {2} पर {1} खेलना खत्म किया", + "UserStoppedPlayingItemWithValues": "{0} ने {2} पर {1} ख़त्म कर दिया", "StartupEmbyServerIsLoading": "जेलीफ़िन सर्वर लोड हो रहा है। कृपया थोड़ी देर में दोबारा कोशिश करें।", - "UserCreatedWithName": "उपयोगकर्ता {0} बनाया गया", + "UserCreatedWithName": "यूज़र {0} बनाया गया", "UserDownloadingItemWithValues": "{0} डाउनलोड हो रहा है", "UserOfflineFromDevice": "{0} {1} से डिस्कनेक्ट हो गया है", "Undefined": "अनिर्धारित", "UserOnlineFromDevice": "{0} {1} से ऑनलाइन है", "Shows": "शो", - "UserPasswordChangedWithName": "उपयोगकर्ता {0} के लिए पासवर्ड बदल दिया गया है", - "UserDeletedWithName": "उपयोगकर्ता {0} हटा दिया गया", - "SubtitleDownloadFailureFromForItem": "{1} के लिए {0} से उपशीर्षक डाउनलोड करने में विफल", + "UserPasswordChangedWithName": "{0} यूज़र का पासवर्ड बदल दिया गया है", + "UserDeletedWithName": "यूज़र {0} हटा दिया गया", + "SubtitleDownloadFailureFromForItem": "{1} के लिए {0} से सबटाइटल डाउनलोड करने में विफल", "ScheduledTaskFailedWithName": "{0}असफल", - "UserLockedOutWithName": "उपयोगकर्ता {0} को लॉक आउट कर दिया गया है", + "UserLockedOutWithName": "यूज़र {0} को बाहर लॉक कर दिया गया है", "TvShows": "टीवी शो", "HearingImpaired": "मूक बधिर", "TasksMaintenanceCategory": "रखरखाव", "VersionNumber": "{0} पाठान्तर", "TasksLibraryCategory": "लाइब्रेरी", "TaskOptimizeDatabase": "डेटाबेस ट्यून करें", - "TaskDownloadMissingSubtitles": "लापता अनुलेख डाउनलोड करें", - "TaskRefreshLibrary": "माध्यम संग्राहत को छाने", - "TaskCleanActivityLog": "क्रियाकलाप लॉग साफ करें", - "TasksChannelsCategory": "इंटरनेट प्रणाली", - "TasksApplicationCategory": "अनुप्रयोग", - "TaskRefreshPeople": "लोगोकी जानकारी ताज़ी करें", + "TaskDownloadMissingSubtitles": "बाक़ी सबटाइटल डाउनलोड करें", + "TaskRefreshLibrary": "मीडिया लाइब्रेरी को छानें", + "TaskCleanActivityLog": "एक्टिविटी लॉग हटायें", + "TasksChannelsCategory": "इंटरनेट चैनल", + "TasksApplicationCategory": "ऐप्लिकेशन", + "TaskRefreshPeople": "लोगों की जानकारी ताज़ा करें", "TaskKeyframeExtractor": "कीफ़्रेम एक्सट्रैक्टर", "TaskCleanActivityLogDescription": "कॉन्फ़िगर की गई आयु से पुरानी गतिविधि लॉग प्रविष्टियां हटाता है।", - "TaskRefreshChapterImagesDescription": "अध्याय वाले वीडियो के लिए थंबनेल बनाता है।", + "TaskRefreshChapterImagesDescription": "अध्याय वाले वीडियो के लिए झलकें बनाता है।", "TaskRefreshLibraryDescription": "नई फ़ाइलों के लिए आपकी मीडिया लाइब्रेरी को स्कैन करता है और मेटाडेटा को ताज़ा करता है।", - "TaskCleanLogs": "स्वच्छ लॉग निर्देशिका", - "TaskUpdatePluginsDescription": "प्लगइन्स के लिए अपडेट डाउनलोड और इंस्टॉल करें जो स्वचालित रूप से अपडेट करने के लिए कॉन्फ़िगर किए गए हैं।", - "TaskCleanTranscode": "ट्रांसकोड डाइरेक्टरी साफ़ करें", + "TaskCleanLogs": "लॉग डाइरेक्टरी ख़ाली करें", + "TaskUpdatePluginsDescription": "अपने आप अपडेट होने वाले निर्धारित प्लगिंस के लिए अपडेट डाउनलोड और इंस्टॉल करता है।", + "TaskCleanTranscode": "ट्रांसकोड डाइरेक्टरी ख़ाली करें", "TaskCleanTranscodeDescription": "एक दिन से ज़्यादा पुरानी ट्रांसकोड फ़ाइलें हटाता है।", "TaskRefreshChannelsDescription": "इंटरनेट चैनल की जानकारी को ताज़ा करता है।", "TaskOptimizeDatabaseDescription": "डेटाबेस को छोटा करके जगह ख़ाली करता है। लाइब्रेरी का निरीक्षण या डेटाबेस में बदलाव के बाद इस कार्य को चलाने से कार्यकुशलता बढ़ सकती है।", "TaskRefreshChannels": "चैनल ताज़ा करें", - "TaskRefreshChapterImages": "अध्याय छवियाँ निकालें", + "TaskRefreshChapterImages": "अध्याय की तस्वीरें निकालें", "TaskCleanLogsDescription": "{0} दिन से अधिक पुरानी लॉग फ़ाइलें हटाता है।", - "TaskCleanCacheDescription": "उन कैश फ़ाइलों को हटाता है जिनकी अब सिस्टम को आवश्यकता नहीं है।", - "TaskUpdatePlugins": "अद्यतन प्लगइन्स", + "TaskCleanCacheDescription": "अनावश्यक अस्थायी डेटा फ़ाइलों को हटाता है।", + "TaskUpdatePlugins": "प्लगिन अपडेट करें", "TaskRefreshPeopleDescription": "आपकी मीडिया लाइब्रेरी में अभिनेताओं और निर्देशकों के लिए मेटाडेटा अपडेट करता है।", - "TaskCleanCache": "स्वच्छ कैश निर्देशिका", - "TaskDownloadMissingSubtitlesDescription": "मेटाडेटा कॉन्फ़िगरेशन के आधार पर लापता उपशीर्षक के लिए इंटरनेट खोजता है।", + "TaskCleanCache": "अस्थायी डेटा डाइरेक्टरी ख़ाली करें", + "TaskDownloadMissingSubtitlesDescription": "मेटाडेटा कॉन्फ़िगरेशन के आधार पर लापता सबटाइटल की इंटरनेट पर खोज करता है।", "TaskKeyframeExtractorDescription": "अधिक सटीक एचएलएस प्लेलिस्ट बनाने के लिए वीडियो फ़ाइलों से मुख्य-फ़्रेम निकालता है। यह कार्य लंबे समय तक चल सकता है।", - "TaskRefreshTrickplayImages": "ट्रिकप्लै चित्रों को सृजन करे", - "TaskRefreshTrickplayImagesDescription": "नियत संग्रहों में चलचित्रों का ट्रीकप्लै दर्शनों को सृजन करे.", - "TaskAudioNormalization": "श्रव्य सामान्यीकरण", - "TaskAudioNormalizationDescription": "श्रव्य सामान्यीकरण के लिए फाइलें अन्वेषण करें।", + "TaskRefreshTrickplayImages": "ट्रिकप्ले तस्वीरें बनायें", + "TaskRefreshTrickplayImagesDescription": "चुनिंदा लाइब्रेरी के वीडियो के लिए ट्रिकप्ले तस्वीरें (सीन झलकियां) बनाता है।", + "TaskAudioNormalization": "ध्वनि सामान्यीकरण", + "TaskAudioNormalizationDescription": "ध्वनि सामान्यीकरण के लिए फाइलें छानें।", "TaskDownloadMissingLyrics": "बाकी बोल डाउनलोड करें", "TaskDownloadMissingLyricsDescription": "गीत के बोल डाउनलोड करें", "TaskExtractMediaSegments": "मीडिया सेगमेंट स्कैन", @@ -108,18 +108,18 @@ "CleanupUserDataTask": "यूज़र डेटा सफाई कार्य", "Original": "असली", "LyricDownloadFailureFromForItem": "{0} के लिए {1} से बोल (Lyrics) डाउनलोड करने में विफल रहा", - "NameExtraBehindTheScenes": "परदे के पीछे", + "NameExtraBehindTheScenes": "पर्दे के पीछे", "NameExtraClip": "क्लिप", "NameExtraDeletedScene": "काटे गए सीन", "NameExtraFeaturette": "फीचरेट", "NameExtraInterview": "साक्षात्कार", "NameExtraNumbered": "{0} {1}", "NameExtraSample": "नमूना", - "NameExtraScene": "दृश्य", + "NameExtraScene": "सीन", "NameExtraShort": "शॉर्ट", - "NameExtraThemeSong": "थीम सॉन्ग", - "NameExtraThemeVideo": "थीम वीडियो", + "NameExtraThemeSong": "विशेष गीत", + "NameExtraThemeVideo": "विशेष वीडियो", "NameExtraTrailer": "ट्रेलर", - "NameExtraUnknown": "अतिरिक्त", + "NameExtraUnknown": "और भी", "CleanupUserDataTaskDescription": "९० दिन या उससे ज़्यादा से ग़ायब मीडिया से सभी यूज़र डेटा (देखें गए, पसंदीदा आदि) को हटाता है।" } -- cgit v1.2.3 From 08c2ba6e0b6499122822f1c5b18bc426104538b4 Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Sat, 17 Oct 2026 08:46:20 -0400 Subject: Translated using Weblate (Japanese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ja/ --- Emby.Server.Implementations/Localization/Core/ja.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json index 24008575d4..4181ebe13b 100644 --- a/Emby.Server.Implementations/Localization/Core/ja.json +++ b/Emby.Server.Implementations/Localization/Core/ja.json @@ -12,7 +12,7 @@ "HeaderContinueWatching": "視聴をつづける", "HeaderFavoriteEpisodes": "お気に入りのエピソード", "HeaderFavoriteShows": "お気に入りの番組", - "HeaderLiveTV": "ライブTV", + "HeaderLiveTV": "テレビ放送", "HeaderNextUp": "次に再生", "HomeVideos": "ホームビデオ", "Inherit": "継承", -- cgit v1.2.3 From c584f050fe22f650c1d1f501dc15c0ff72c0a380 Mon Sep 17 00:00:00 2001 From: damecon Date: Mon, 19 Oct 2026 02:46:42 -0400 Subject: Translated using Weblate (Korean) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ko/ --- Emby.Server.Implementations/Localization/Core/ko.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index 280d96effe..72a521743e 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -117,5 +117,9 @@ "NameExtraScene": "장면", "NameExtraTrailer": "예고편", "NameExtraUnknown": "부가영상", - "NameExtraThemeSong": "주제가" + "NameExtraThemeSong": "주제가", + "NameExtraBehindTheScenes": "비하인드 영상", + "NameExtraFeaturette": "피처렛", + "NameExtraShort": "단편", + "NameExtraThemeVideo": "테마 영상" } -- cgit v1.2.3 From c7ce6a1b56ee7dcc1896250df0f2f9deaa828a81 Mon Sep 17 00:00:00 2001 From: rimasx Date: Thu, 22 Oct 2026 06:52:29 -0400 Subject: Translated using Weblate (Estonian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/et/ --- Emby.Server.Implementations/Localization/Core/et.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations/Localization') diff --git a/Emby.Server.Implementations/Localization/Core/et.json b/Emby.Server.Implementations/Localization/Core/et.json index a7afcf5b77..10e0e6c3b3 100644 --- a/Emby.Server.Implementations/Localization/Core/et.json +++ b/Emby.Server.Implementations/Localization/Core/et.json @@ -95,16 +95,16 @@ "HearingImpaired": "Kuulmispuudega", "TaskKeyframeExtractorDescription": "Eraldab videofailidest võtmekaadrid, et luua täpsemaid HLS-i esitusloendeid. See võib kesta pikka aega.", "TaskKeyframeExtractor": "Eralda võtmekaadrid", - "TaskRefreshTrickplayImages": "Loo trickplay pildid", - "TaskRefreshTrickplayImagesDescription": "Loob trickplay eelvaated videotele lubatud meediakogudes.", + "TaskRefreshTrickplayImages": "Loo kerimispildid (trickplay)", + "TaskRefreshTrickplayImagesDescription": "Loob kerimispidid videotele lubatud meediakogudes.", "TaskAudioNormalization": "Normaliseeri helitugevus", "TaskAudioNormalizationDescription": "Otsib failidest helitugevuse normaliseerimise teavet.", "TaskDownloadMissingLyrics": "Hangi puuduvad laulusõnad", "TaskDownloadMissingLyricsDescription": "Laulusõnade allalaadimine", - "TaskMoveTrickplayImagesDescription": "Liigutab trickplay pildid meediakogu sätete kohaselt.", + "TaskMoveTrickplayImagesDescription": "Teisaldab kerimispildid meediakogu sätete kohaselt.", "TaskExtractMediaSegments": "Skaneeri meedialõike", "TaskExtractMediaSegmentsDescription": "Eraldab või võtab meedialõigud MediaSegment'i toega pluginatest.", - "TaskMoveTrickplayImages": "Muuda trickplay piltide asukoht", + "TaskMoveTrickplayImages": "Muuda kerimispiltide asukoht", "CleanupUserDataTask": "Puhasta kasutajaandmed", "CleanupUserDataTaskDescription": "Puhastab kõik kasutajaandmed (vaatamise olek, lemmikute olek jne) meediast, mida pole enam vähemalt 90 päeva saadaval olnud.", "LyricDownloadFailureFromForItem": "Laulusõnade hankimine teenusest {0} loole {1} nurjus", -- cgit v1.2.3