[Meta] Automatic String Organization (#1372)
* Automate String Organization. * Comment the script so it's easier to maintain? Or messier? * Linting post comments * Rename ShellScript -> Alphabetize Strings for tvOS * use swift regex, add error messages, clean up separators * Only search for ./Translations/en.lproj/Localizable.strings * Purge Unused Strings Script * Organize Translation Scripts into a Folder. Update references at the project level. * clean up --------- Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com>
This commit is contained in:
parent
a6bd093960
commit
af602d3d98
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// Swiftfin is subject to the terms of the Mozilla Public
|
||||
// License, v2.0. If a copy of the MPL was not distributed with this
|
||||
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// Get the English localization file
|
||||
let fileURL = URL(fileURLWithPath: "./Translations/en.lproj/Localizable.strings")
|
||||
|
||||
// This regular expression pattern matches lines of the format:
|
||||
// "Key" = "Value";
|
||||
let regex = #/^\"(?<key>[^\"]+)\"\s*=\s*\"(?<value>[^\"]+)\";/#
|
||||
|
||||
// Attempt to read the file content.
|
||||
guard let content = try? String(contentsOf: fileURL, encoding: .utf8) else {
|
||||
print("Unable to read file: \(fileURL.path)")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Split file content by newlines to process line by line.
|
||||
let strings = content.components(separatedBy: .newlines)
|
||||
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||
.filter { !$0.isEmpty && !$0.hasPrefix("//") }
|
||||
|
||||
let entries = strings.reduce(into: [String: String]()) {
|
||||
if let match = $1.firstMatch(of: regex) {
|
||||
let key = String(match.output.key)
|
||||
let value = String(match.output.value)
|
||||
$0[key] = value
|
||||
} else {
|
||||
print("Error: Invalid line format in \(fileURL.path): \($1)")
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the keys alphabetically for consistent ordering.
|
||||
let sortedKeys = entries.keys.sorted { $0.localizedCaseInsensitiveCompare($1) == .orderedAscending }
|
||||
let newContent = sortedKeys.map { "/// \(entries[$0]!)\n\"\($0)\" = \"\(entries[$0]!)\";" }.joined(separator: "\n\n")
|
||||
|
||||
// Write the updated, sorted, and commented localizations back to the file.
|
||||
do {
|
||||
try newContent.write(to: fileURL, atomically: true, encoding: .utf8)
|
||||
|
||||
if let derivedFileDirectory = ProcessInfo.processInfo.environment["DERIVED_FILE_DIR"] {
|
||||
try? "".write(toFile: derivedFileDirectory + "/alphabetizeStrings.txt", atomically: true, encoding: .utf8)
|
||||
}
|
||||
} catch {
|
||||
print("Error: Failed to write to \(fileURL.path)")
|
||||
exit(1)
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
//
|
||||
// Swiftfin is subject to the terms of the Mozilla Public
|
||||
// License, v2.0. If a copy of the MPL was not distributed with this
|
||||
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// Path to the English localization file
|
||||
let localizationFile = "./Translations/en.lproj/Localizable.strings"
|
||||
|
||||
// Directories to scan for Swift files
|
||||
let directoriesToScan = ["./Shared", "./Swiftfin", "./Swiftfin tvOS"]
|
||||
|
||||
// File to exclude from scanning
|
||||
let excludedFile = "./Shared/Strings/Strings.swift"
|
||||
|
||||
// Regular expressions to match localization entries and usage in Swift files
|
||||
// Matches lines like "Key" = "Value";
|
||||
let localizationRegex = #/^\"(?<key>[^\"]+)\"\s*=\s*\"(?<value>[^\"]+)\";$/#
|
||||
|
||||
// Matches usage like L10n.key in Swift files
|
||||
let usageRegex = #/L10n\.(?<key>[a-zA-Z0-9_]+)/#
|
||||
|
||||
// Attempt to load the localization file's content
|
||||
guard let localizationContent = try? String(contentsOfFile: localizationFile, encoding: .utf8) else {
|
||||
print("Unable to read localization file at \(localizationFile)")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Split the file into lines and initialize a dictionary for localization entries
|
||||
let localizationLines = localizationContent.components(separatedBy: .newlines)
|
||||
var localizationEntries = [String: String]()
|
||||
|
||||
// Parse each line to extract key-value pairs
|
||||
for line in localizationLines {
|
||||
let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
// Skip empty lines or comments
|
||||
if trimmed.isEmpty || trimmed.hasPrefix("//") { continue }
|
||||
|
||||
// Match valid localization entries and add them to the dictionary
|
||||
if let match = line.firstMatch(of: localizationRegex) {
|
||||
let key = String(match.output.key)
|
||||
let value = String(match.output.value)
|
||||
localizationEntries[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
// Set to store all keys found in the codebase
|
||||
var usedKeys = Set<String>()
|
||||
|
||||
// Function to scan a directory recursively for Swift files
|
||||
func scanDirectory(_ path: String) {
|
||||
let fileManager = FileManager.default
|
||||
guard let enumerator = fileManager.enumerator(atPath: path) else { return }
|
||||
|
||||
for case let file as String in enumerator {
|
||||
let filePath = "\(path)/\(file)"
|
||||
|
||||
// Skip the excluded file
|
||||
if filePath == excludedFile { continue }
|
||||
|
||||
// Process only Swift files
|
||||
if file.hasSuffix(".swift") {
|
||||
if let fileContent = try? String(contentsOfFile: filePath, encoding: .utf8) {
|
||||
for line in fileContent.components(separatedBy: .newlines) {
|
||||
// Find all matches for L10n.key in each line
|
||||
let matches = line.matches(of: usageRegex)
|
||||
for match in matches {
|
||||
let key = String(match.output.key)
|
||||
usedKeys.insert(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scan all specified directories
|
||||
for directory in directoriesToScan {
|
||||
scanDirectory(directory)
|
||||
}
|
||||
|
||||
// MARK: - Remove Unused Keys
|
||||
|
||||
// Identify keys in the localization file that are not used in the codebase
|
||||
let unusedKeys = localizationEntries.keys.filter { !usedKeys.contains($0) }
|
||||
|
||||
// Remove unused keys from the dictionary
|
||||
unusedKeys.forEach { localizationEntries.removeValue(forKey: $0) }
|
||||
|
||||
// MARK: - Write Updated Localizable.strings
|
||||
|
||||
// Sort keys alphabetically for consistent formatting
|
||||
let sortedKeys = localizationEntries.keys.sorted { $0.localizedCaseInsensitiveCompare($1) == .orderedAscending }
|
||||
|
||||
// Reconstruct the localization file with sorted and updated entries
|
||||
let updatedContent = sortedKeys.map { "/// \(localizationEntries[$0]!)\n\"\($0)\" = \"\(localizationEntries[$0]!)\";" }
|
||||
.joined(separator: "\n\n")
|
||||
|
||||
// Attempt to write the updated content back to the localization file
|
||||
do {
|
||||
try updatedContent.write(toFile: localizationFile, atomically: true, encoding: .utf8)
|
||||
print("Localization file updated. Removed \(unusedKeys.count) unused keys.")
|
||||
} catch {
|
||||
print("Error: Failed to write updated localization file.")
|
||||
exit(1)
|
||||
}
|
|
@ -1346,8 +1346,6 @@ internal enum L10n {
|
|||
internal static let weekly = L10n.tr("Localizable", "weekly", fallback: "Weekly")
|
||||
/// This will be created as a new item on your Jellyfin Server.
|
||||
internal static let willBeCreatedOnServer = L10n.tr("Localizable", "willBeCreatedOnServer", fallback: "This will be created as a new item on your Jellyfin Server.")
|
||||
/// WIP
|
||||
internal static let wip = L10n.tr("Localizable", "wip", fallback: "WIP")
|
||||
/// Writer
|
||||
internal static let writer = L10n.tr("Localizable", "writer", fallback: "Writer")
|
||||
/// Year
|
||||
|
|
|
@ -762,7 +762,6 @@
|
|||
E1763A292BF3046A004DF6AB /* AddUserButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1763A282BF3046A004DF6AB /* AddUserButton.swift */; };
|
||||
E1763A2B2BF3046E004DF6AB /* UserGridButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1763A2A2BF3046E004DF6AB /* UserGridButton.swift */; };
|
||||
E1763A642BF3C9AA004DF6AB /* ListRowButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1763A632BF3C9AA004DF6AB /* ListRowButton.swift */; };
|
||||
E1763A662BF3CA83004DF6AB /* FullScreenMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1763A652BF3CA83004DF6AB /* FullScreenMenu.swift */; };
|
||||
E1763A6A2BF3D177004DF6AB /* PublicUserButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1763A692BF3D177004DF6AB /* PublicUserButton.swift */; };
|
||||
E1763A712BF3F67C004DF6AB /* SwiftfinStore+Mappings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1763A702BF3F67C004DF6AB /* SwiftfinStore+Mappings.swift */; };
|
||||
E1763A722BF3F67C004DF6AB /* SwiftfinStore+Mappings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1763A702BF3F67C004DF6AB /* SwiftfinStore+Mappings.swift */; };
|
||||
|
@ -1272,6 +1271,7 @@
|
|||
4E6C27072C8BD0AD00FD2185 /* ActiveSessionDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSessionDetailView.swift; sourceTree = "<group>"; };
|
||||
4E71D6882C80910900A0174D /* EditCustomDeviceProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditCustomDeviceProfileView.swift; sourceTree = "<group>"; };
|
||||
4E73E2A52C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackBitrateTestSize.swift; sourceTree = "<group>"; };
|
||||
4E75B34A2D164AC100D16531 /* PurgeUnusedStrings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurgeUnusedStrings.swift; sourceTree = "<group>"; };
|
||||
4E762AAD2C3A1A95004D1579 /* PlaybackBitrate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlaybackBitrate.swift; sourceTree = "<group>"; };
|
||||
4E884C642CEBB2FF004CF6AD /* LearnMoreModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LearnMoreModal.swift; sourceTree = "<group>"; };
|
||||
4E8B34E92AB91B6E0018F305 /* ItemFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemFilter.swift; sourceTree = "<group>"; };
|
||||
|
@ -1324,6 +1324,7 @@
|
|||
4EC2B1A82CC97C0400D866BE /* ServerUserDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerUserDetailsView.swift; sourceTree = "<group>"; };
|
||||
4EC50D602C934B3A00FC3D0E /* ServerTasksViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerTasksViewModel.swift; sourceTree = "<group>"; };
|
||||
4EC6C16A2C92999800FC904B /* TranscodeSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranscodeSection.swift; sourceTree = "<group>"; };
|
||||
4EC71FBB2D161FE300D0B3A8 /* AlphabetizeStrings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlphabetizeStrings.swift; sourceTree = "<group>"; };
|
||||
4ECDAA9D2C920A8E0030F2F5 /* TranscodeReason.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranscodeReason.swift; sourceTree = "<group>"; };
|
||||
4ECF5D812D0A3D0200F066B1 /* AddAccessScheduleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccessScheduleView.swift; sourceTree = "<group>"; };
|
||||
4ECF5D892D0A57EF00F066B1 /* DynamicDayOfWeek.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicDayOfWeek.swift; sourceTree = "<group>"; };
|
||||
|
@ -1695,7 +1696,6 @@
|
|||
E1763A282BF3046A004DF6AB /* AddUserButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddUserButton.swift; sourceTree = "<group>"; };
|
||||
E1763A2A2BF3046E004DF6AB /* UserGridButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserGridButton.swift; sourceTree = "<group>"; };
|
||||
E1763A632BF3C9AA004DF6AB /* ListRowButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRowButton.swift; sourceTree = "<group>"; };
|
||||
E1763A652BF3CA83004DF6AB /* FullScreenMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenMenu.swift; sourceTree = "<group>"; };
|
||||
E1763A692BF3D177004DF6AB /* PublicUserButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicUserButton.swift; sourceTree = "<group>"; };
|
||||
E1763A702BF3F67C004DF6AB /* SwiftfinStore+Mappings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SwiftfinStore+Mappings.swift"; sourceTree = "<group>"; };
|
||||
E1763A732BF3FA4C004DF6AB /* AppLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLoadingView.swift; sourceTree = "<group>"; };
|
||||
|
@ -2462,6 +2462,15 @@
|
|||
path = ActiveSessionDetailView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4E75B34D2D16583900D16531 /* Translations */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4EC71FBB2D161FE300D0B3A8 /* AlphabetizeStrings.swift */,
|
||||
4E75B34A2D164AC100D16531 /* PurgeUnusedStrings.swift */,
|
||||
);
|
||||
path = Translations;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4E8F74A32CE03D3100CC8969 /* ItemEditorView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2692,6 +2701,14 @@
|
|||
path = ServerUserDetailsView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4EC71FBA2D161FD800D0B3A8 /* Scripts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4E75B34D2D16583900D16531 /* Translations */,
|
||||
);
|
||||
path = Scripts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4ECF5D822D0A3D0200F066B1 /* AddAccessScheduleView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -3004,6 +3021,7 @@
|
|||
534D4FE126A7D7CC000A7A48 /* Translations */,
|
||||
5377CBF2263B596A003A4E83 /* Products */,
|
||||
53D5E3DB264B47EE00BADDC8 /* Frameworks */,
|
||||
4EC71FBA2D161FD800D0B3A8 /* Scripts */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
|
@ -4753,6 +4771,7 @@
|
|||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 535870712669D21700D05A09 /* Build configuration list for PBXNativeTarget "Swiftfin tvOS" */;
|
||||
buildPhases = (
|
||||
4EC71FBD2D1620AF00D0B3A8 /* Alphabetize Strings */,
|
||||
6286F0A3271C0ABA00C40ED5 /* Run Swiftgen.swift */,
|
||||
BD83D7852B55EEB600652C24 /* Run SwiftFormat */,
|
||||
5358705C2669D21600D05A09 /* Sources */,
|
||||
|
@ -4800,6 +4819,7 @@
|
|||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 5377CC1B263B596B003A4E83 /* Build configuration list for PBXNativeTarget "Swiftfin iOS" */;
|
||||
buildPhases = (
|
||||
4EC71FBC2D16201C00D0B3A8 /* Alphabetize Strings */,
|
||||
6286F09E271C093000C40ED5 /* Run Swiftgen.swift */,
|
||||
BD0BA2282AD64BB200306A8D /* Run SwiftFormat */,
|
||||
5377CBED263B596A003A4E83 /* Sources */,
|
||||
|
@ -4989,6 +5009,46 @@
|
|||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
4EC71FBC2D16201C00D0B3A8 /* Alphabetize Strings */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$(SRCROOT)/Translations/en.lproj/Localizable.strings",
|
||||
);
|
||||
name = "Alphabetize Strings";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/alphabetizeStrings.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "xcrun --sdk macosx swift \"${SRCROOT}/Scripts/Translations/AlphabetizeStrings.swift\"\n";
|
||||
};
|
||||
4EC71FBD2D1620AF00D0B3A8 /* Alphabetize Strings */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$(SRCROOT)/Translations/en.lproj/Localizable.strings",
|
||||
);
|
||||
name = "Alphabetize Strings";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/alphabetizeStrings.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "xcrun --sdk macosx swift \"${SRCROOT}/Scripts/Translations/AlphabetizeStrings.swift\"\n";
|
||||
};
|
||||
6286F09E271C093000C40ED5 /* Run Swiftgen.swift */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
/// Access
|
||||
"access" = "Access";
|
||||
|
||||
/// Accessibility
|
||||
"accessibility" = "Accessibility";
|
||||
|
||||
/// The End Time must come after the Start Time.
|
||||
"accessScheduleInvalidTime" = "The End Time must come after the Start Time.";
|
||||
|
||||
|
@ -22,9 +25,6 @@
|
|||
/// Define the allowed hours for usage and restrict access outside those times.
|
||||
"accessSchedulesDescription" = "Define the allowed hours for usage and restrict access outside those times.";
|
||||
|
||||
/// Accessibility
|
||||
"accessibility" = "Accessibility";
|
||||
|
||||
/// Active
|
||||
"active" = "Active";
|
||||
|
||||
|
@ -37,11 +37,14 @@
|
|||
/// Add
|
||||
"add" = "Add";
|
||||
|
||||
/// Add Access Schedule
|
||||
"addAccessSchedule" = "Add Access Schedule";
|
||||
|
||||
/// Add API key
|
||||
"addAPIKey" = "Add API key";
|
||||
|
||||
/// Add Access Schedule
|
||||
"addAccessSchedule" = "Add Access Schedule";
|
||||
/// Additional security access for users signed in to this device. This does not change any Jellyfin server user settings.
|
||||
"additionalSecurityAccessDescription" = "Additional security access for users signed in to this device. This does not change any Jellyfin server user settings.";
|
||||
|
||||
/// Add Server
|
||||
"addServer" = "Add Server";
|
||||
|
@ -55,9 +58,6 @@
|
|||
/// Add User
|
||||
"addUser" = "Add User";
|
||||
|
||||
/// Additional security access for users signed in to this device. This does not change any Jellyfin server user settings.
|
||||
"additionalSecurityAccessDescription" = "Additional security access for users signed in to this device. This does not change any Jellyfin server user settings.";
|
||||
|
||||
/// Administrator
|
||||
"administrator" = "Administrator";
|
||||
|
||||
|
@ -67,15 +67,15 @@
|
|||
/// Age %@
|
||||
"agesGroup" = "Age %@";
|
||||
|
||||
/// Aired
|
||||
"aired" = "Aired";
|
||||
|
||||
/// Air Time
|
||||
"airTime" = "Air Time";
|
||||
|
||||
/// Airs %s
|
||||
"airWithDate" = "Airs %s";
|
||||
|
||||
/// Aired
|
||||
"aired" = "Aired";
|
||||
|
||||
/// Album Artist
|
||||
"albumArtist" = "Album Artist";
|
||||
|
||||
|
@ -88,12 +88,6 @@
|
|||
/// All Media
|
||||
"allMedia" = "All Media";
|
||||
|
||||
/// All Servers
|
||||
"allServers" = "All Servers";
|
||||
|
||||
/// View and manage all registered users on the server, including their permissions and activity status.
|
||||
"allUsersDescription" = "View and manage all registered users on the server, including their permissions and activity status.";
|
||||
|
||||
/// Allow collection management
|
||||
"allowCollectionManagement" = "Allow collection management";
|
||||
|
||||
|
@ -103,6 +97,12 @@
|
|||
/// Allow media item editing
|
||||
"allowItemEditing" = "Allow media item editing";
|
||||
|
||||
/// All Servers
|
||||
"allServers" = "All Servers";
|
||||
|
||||
/// View and manage all registered users on the server, including their permissions and activity status.
|
||||
"allUsersDescription" = "View and manage all registered users on the server, including their permissions and activity status.";
|
||||
|
||||
/// Alternate
|
||||
"alternate" = "Alternate";
|
||||
|
||||
|
@ -127,12 +127,12 @@
|
|||
/// API Keys
|
||||
"apiKeysTitle" = "API Keys";
|
||||
|
||||
/// App Icon
|
||||
"appIcon" = "App Icon";
|
||||
|
||||
/// Appearance
|
||||
"appearance" = "Appearance";
|
||||
|
||||
/// App Icon
|
||||
"appIcon" = "App Icon";
|
||||
|
||||
/// Application Name
|
||||
"applicationName" = "Application Name";
|
||||
|
||||
|
@ -202,12 +202,12 @@
|
|||
/// Tests your server connection to assess internet speed and adjust bandwidth automatically.
|
||||
"birateAutoDescription" = "Tests your server connection to assess internet speed and adjust bandwidth automatically.";
|
||||
|
||||
/// Birth year
|
||||
"birthYear" = "Birth year";
|
||||
|
||||
/// Birthday
|
||||
"birthday" = "Birthday";
|
||||
|
||||
/// Birth year
|
||||
"birthYear" = "Birth year";
|
||||
|
||||
/// Auto
|
||||
"bitrateAuto" = "Auto";
|
||||
|
||||
|
@ -316,12 +316,12 @@
|
|||
/// Channels
|
||||
"channels" = "Channels";
|
||||
|
||||
/// Chapter Slider
|
||||
"chapterSlider" = "Chapter Slider";
|
||||
|
||||
/// Chapters
|
||||
"chapters" = "Chapters";
|
||||
|
||||
/// Chapter Slider
|
||||
"chapterSlider" = "Chapter Slider";
|
||||
|
||||
/// Cinematic
|
||||
"cinematic" = "Cinematic";
|
||||
|
||||
|
@ -418,15 +418,15 @@
|
|||
/// Cover Artist
|
||||
"coverArtist" = "Cover Artist";
|
||||
|
||||
/// Create & Join Groups
|
||||
"createAndJoinGroups" = "Create & Join Groups";
|
||||
|
||||
/// Create API Key
|
||||
"createAPIKey" = "Create API Key";
|
||||
|
||||
/// Enter the application name for the new API key.
|
||||
"createAPIKeyMessage" = "Enter the application name for the new API key.";
|
||||
|
||||
/// Create & Join Groups
|
||||
"createAndJoinGroups" = "Create & Join Groups";
|
||||
|
||||
/// Create a pin to sign in to %@ on this device
|
||||
"createPinForUser" = "Create a pin to sign in to %@ on this device";
|
||||
|
||||
|
@ -472,6 +472,9 @@
|
|||
/// Custom failed logins
|
||||
"customFailedLogins" = "Custom failed logins";
|
||||
|
||||
/// Customize
|
||||
"customize" = "Customize";
|
||||
|
||||
/// Custom Profile
|
||||
"customProfile" = "Custom Profile";
|
||||
|
||||
|
@ -481,9 +484,6 @@
|
|||
/// Custom sessions
|
||||
"customSessions" = "Custom sessions";
|
||||
|
||||
/// Customize
|
||||
"customize" = "Customize";
|
||||
|
||||
/// Daily
|
||||
"daily" = "Daily";
|
||||
|
||||
|
@ -637,6 +637,9 @@
|
|||
/// Plays content in its original format. May cause playback issues on unsupported media types.
|
||||
"directDescription" = "Plays content in its original format. May cause playback issues on unsupported media types.";
|
||||
|
||||
/// Director
|
||||
"director" = "Director";
|
||||
|
||||
/// Direct Play
|
||||
"directPlay" = "Direct Play";
|
||||
|
||||
|
@ -646,9 +649,6 @@
|
|||
/// Direct Stream
|
||||
"directStream" = "Direct Stream";
|
||||
|
||||
/// Director
|
||||
"director" = "Director";
|
||||
|
||||
/// Disabled
|
||||
"disabled" = "Disabled";
|
||||
|
||||
|
@ -679,15 +679,15 @@
|
|||
/// Edit
|
||||
"edit" = "Edit";
|
||||
|
||||
/// Editor
|
||||
"editor" = "Editor";
|
||||
|
||||
/// Edit Server
|
||||
"editServer" = "Edit Server";
|
||||
|
||||
/// Edit Users
|
||||
"editUsers" = "Edit Users";
|
||||
|
||||
/// Editor
|
||||
"editor" = "Editor";
|
||||
|
||||
/// Enable all devices
|
||||
"enableAllDevices" = "Enable all devices";
|
||||
|
||||
|
@ -700,12 +700,12 @@
|
|||
/// End Date
|
||||
"endDate" = "End Date";
|
||||
|
||||
/// End Time
|
||||
"endTime" = "End Time";
|
||||
|
||||
/// Ended
|
||||
"ended" = "Ended";
|
||||
|
||||
/// End Time
|
||||
"endTime" = "End Time";
|
||||
|
||||
/// Engineer
|
||||
"engineer" = "Engineer";
|
||||
|
||||
|
@ -754,12 +754,12 @@
|
|||
/// Every
|
||||
"every" = "Every";
|
||||
|
||||
/// Every %1$@
|
||||
"everyInterval" = "Every %1$@";
|
||||
|
||||
/// Everyday
|
||||
"everyday" = "Everyday";
|
||||
|
||||
/// Every %1$@
|
||||
"everyInterval" = "Every %1$@";
|
||||
|
||||
/// Executed
|
||||
"executed" = "Executed";
|
||||
|
||||
|
@ -937,12 +937,12 @@
|
|||
/// Letter
|
||||
"letter" = "Letter";
|
||||
|
||||
/// Letter Picker
|
||||
"letterPicker" = "Letter Picker";
|
||||
|
||||
/// Letterer
|
||||
"letterer" = "Letterer";
|
||||
|
||||
/// Letter Picker
|
||||
"letterPicker" = "Letter Picker";
|
||||
|
||||
/// Library
|
||||
"library" = "Library";
|
||||
|
||||
|
@ -958,15 +958,15 @@
|
|||
/// Live TV
|
||||
"liveTV" = "Live TV";
|
||||
|
||||
/// Live TV access
|
||||
"liveTvAccess" = "Live TV access";
|
||||
|
||||
/// Live TV Channels
|
||||
"liveTVChannels" = "Live TV Channels";
|
||||
|
||||
/// Live TV Programs
|
||||
"liveTVPrograms" = "Live TV Programs";
|
||||
|
||||
/// Live TV access
|
||||
"liveTvAccess" = "Live TV access";
|
||||
|
||||
/// Live TV recording management
|
||||
"liveTvRecordingManagement" = "Live TV recording management";
|
||||
|
||||
|
@ -1000,12 +1000,6 @@
|
|||
/// Management
|
||||
"management" = "Management";
|
||||
|
||||
/// Maximum parental rating
|
||||
"maxParentalRating" = "Maximum parental rating";
|
||||
|
||||
/// Content with a higher rating will be hidden from this user.
|
||||
"maxParentalRatingDescription" = "Content with a higher rating will be hidden from this user.";
|
||||
|
||||
/// Maximum Bitrate
|
||||
"maximumBitrate" = "Maximum Bitrate";
|
||||
|
||||
|
@ -1030,6 +1024,12 @@
|
|||
/// Maximum sessions policy
|
||||
"maximumSessionsPolicy" = "Maximum sessions policy";
|
||||
|
||||
/// Maximum parental rating
|
||||
"maxParentalRating" = "Maximum parental rating";
|
||||
|
||||
/// Content with a higher rating will be hidden from this user.
|
||||
"maxParentalRatingDescription" = "Content with a higher rating will be hidden from this user.";
|
||||
|
||||
/// Media
|
||||
"media" = "Media";
|
||||
|
||||
|
@ -1096,12 +1096,12 @@
|
|||
/// New Password
|
||||
"newPassword" = "New Password";
|
||||
|
||||
/// New User
|
||||
"newUser" = "New User";
|
||||
|
||||
/// News
|
||||
"news" = "News";
|
||||
|
||||
/// New User
|
||||
"newUser" = "New User";
|
||||
|
||||
/// Next
|
||||
"next" = "Next";
|
||||
|
||||
|
@ -1129,6 +1129,9 @@
|
|||
/// No local servers found
|
||||
"noLocalServersFound" = "No local servers found";
|
||||
|
||||
/// None
|
||||
"none" = "None";
|
||||
|
||||
/// No overview available
|
||||
"noOverviewAvailable" = "No overview available";
|
||||
|
||||
|
@ -1138,24 +1141,21 @@
|
|||
/// No results.
|
||||
"noResults" = "No results.";
|
||||
|
||||
/// Normal
|
||||
"normal" = "Normal";
|
||||
|
||||
/// No runtime limit
|
||||
"noRuntimeLimit" = "No runtime limit";
|
||||
|
||||
/// No session
|
||||
"noSession" = "No session";
|
||||
|
||||
/// No title
|
||||
"noTitle" = "No title";
|
||||
|
||||
/// None
|
||||
"none" = "None";
|
||||
|
||||
/// Normal
|
||||
"normal" = "Normal";
|
||||
|
||||
/// Type: %@ not implemented yet :(
|
||||
"notImplementedYetWithType" = "Type: %@ not implemented yet :(";
|
||||
|
||||
/// No title
|
||||
"noTitle" = "No title";
|
||||
|
||||
/// Official Rating
|
||||
"officialRating" = "Official Rating";
|
||||
|
||||
|
@ -1207,12 +1207,12 @@
|
|||
/// Password
|
||||
"password" = "Password";
|
||||
|
||||
/// Changes the Jellyfin server user password. This does not change any Swiftfin settings.
|
||||
"passwordChangeWarning" = "Changes the Jellyfin server user password. This does not change any Swiftfin settings.";
|
||||
|
||||
/// User password has been changed.
|
||||
"passwordChangedMessage" = "User password has been changed.";
|
||||
|
||||
/// Changes the Jellyfin server user password. This does not change any Swiftfin settings.
|
||||
"passwordChangeWarning" = "Changes the Jellyfin server user password. This does not change any Swiftfin settings.";
|
||||
|
||||
/// New passwords do not match.
|
||||
"passwordsDoNotMatch" = "New passwords do not match.";
|
||||
|
||||
|
@ -1240,18 +1240,6 @@
|
|||
/// Play / Pause
|
||||
"playAndPause" = "Play / Pause";
|
||||
|
||||
/// Play From Beginning
|
||||
"playFromBeginning" = "Play From Beginning";
|
||||
|
||||
/// Play Next Item
|
||||
"playNextItem" = "Play Next Item";
|
||||
|
||||
/// Play on active
|
||||
"playOnActive" = "Play on active";
|
||||
|
||||
/// Play Previous Item
|
||||
"playPreviousItem" = "Play Previous Item";
|
||||
|
||||
/// Playback
|
||||
"playback" = "Playback";
|
||||
|
||||
|
@ -1267,6 +1255,18 @@
|
|||
/// Played
|
||||
"played" = "Played";
|
||||
|
||||
/// Play From Beginning
|
||||
"playFromBeginning" = "Play From Beginning";
|
||||
|
||||
/// Play Next Item
|
||||
"playNextItem" = "Play Next Item";
|
||||
|
||||
/// Play on active
|
||||
"playOnActive" = "Play on active";
|
||||
|
||||
/// Play Previous Item
|
||||
"playPreviousItem" = "Play Previous Item";
|
||||
|
||||
/// Posters
|
||||
"posters" = "Posters";
|
||||
|
||||
|
@ -1402,6 +1402,9 @@
|
|||
/// Replace unlocked metadata with new information.
|
||||
"replaceMetadataDescription" = "Replace unlocked metadata with new information.";
|
||||
|
||||
/// Required
|
||||
"required" = "Required";
|
||||
|
||||
/// Require device authentication when signing in to the user.
|
||||
"requireDeviceAuthDescription" = "Require device authentication when signing in to the user.";
|
||||
|
||||
|
@ -1414,9 +1417,6 @@
|
|||
/// Require a local pin when signing in to the user. This pin is unrecoverable.
|
||||
"requirePinDescription" = "Require a local pin when signing in to the user. This pin is unrecoverable.";
|
||||
|
||||
/// Required
|
||||
"required" = "Required";
|
||||
|
||||
/// Reset
|
||||
"reset" = "Reset";
|
||||
|
||||
|
@ -1525,12 +1525,12 @@
|
|||
/// Server Logs
|
||||
"serverLogs" = "Server Logs";
|
||||
|
||||
/// Server URL
|
||||
"serverURL" = "Server URL";
|
||||
|
||||
/// Servers
|
||||
"servers" = "Servers";
|
||||
|
||||
/// Server URL
|
||||
"serverURL" = "Server URL";
|
||||
|
||||
/// Session
|
||||
"session" = "Session";
|
||||
|
||||
|
@ -1660,15 +1660,15 @@
|
|||
/// Subtitle Offset
|
||||
"subtitleOffset" = "Subtitle Offset";
|
||||
|
||||
/// Subtitle Size
|
||||
"subtitleSize" = "Subtitle Size";
|
||||
|
||||
/// Subtitles
|
||||
"subtitles" = "Subtitles";
|
||||
|
||||
/// Settings only affect some subtitle types
|
||||
"subtitlesDisclaimer" = "Settings only affect some subtitle types";
|
||||
|
||||
/// Subtitle Size
|
||||
"subtitleSize" = "Subtitle Size";
|
||||
|
||||
/// Success
|
||||
"success" = "Success";
|
||||
|
||||
|
@ -1720,18 +1720,18 @@
|
|||
/// Failed
|
||||
"taskFailed" = "Failed";
|
||||
|
||||
/// Sets the duration (in minutes) in between task triggers.
|
||||
"taskTriggerInterval" = "Sets the duration (in minutes) in between task triggers.";
|
||||
|
||||
/// Sets the maximum runtime (in hours) for this task trigger.
|
||||
"taskTriggerTimeLimit" = "Sets the maximum runtime (in hours) for this task trigger.";
|
||||
|
||||
/// Tasks
|
||||
"tasks" = "Tasks";
|
||||
|
||||
/// Tasks are operations that are scheduled to run periodically or can be triggered manually.
|
||||
"tasksDescription" = "Tasks are operations that are scheduled to run periodically or can be triggered manually.";
|
||||
|
||||
/// Sets the duration (in minutes) in between task triggers.
|
||||
"taskTriggerInterval" = "Sets the duration (in minutes) in between task triggers.";
|
||||
|
||||
/// Sets the maximum runtime (in hours) for this task trigger.
|
||||
"taskTriggerTimeLimit" = "Sets the maximum runtime (in hours) for this task trigger.";
|
||||
|
||||
/// Tbps
|
||||
"terabitsPerSecond" = "Tbps";
|
||||
|
||||
|
@ -1858,18 +1858,18 @@
|
|||
/// This user will require device authentication.
|
||||
"userDeviceAuthRequiredDescription" = "This user will require device authentication.";
|
||||
|
||||
/// This user will require a pin.
|
||||
"userPinRequiredDescription" = "This user will require a pin.";
|
||||
|
||||
/// User %@ requires device authentication
|
||||
"userRequiresDeviceAuthentication" = "User %@ requires device authentication";
|
||||
|
||||
/// Username
|
||||
"username" = "Username";
|
||||
|
||||
/// A username is required
|
||||
"usernameRequired" = "A username is required";
|
||||
|
||||
/// This user will require a pin.
|
||||
"userPinRequiredDescription" = "This user will require a pin.";
|
||||
|
||||
/// User %@ requires device authentication
|
||||
"userRequiresDeviceAuthentication" = "User %@ requires device authentication";
|
||||
|
||||
/// Users
|
||||
"users" = "Users";
|
||||
|
||||
|
@ -1927,9 +1927,6 @@
|
|||
/// This will be created as a new item on your Jellyfin Server.
|
||||
"willBeCreatedOnServer" = "This will be created as a new item on your Jellyfin Server.";
|
||||
|
||||
/// WIP
|
||||
"wip" = "WIP";
|
||||
|
||||
/// Writer
|
||||
"writer" = "Writer";
|
||||
|
||||
|
@ -1943,5 +1940,4 @@
|
|||
"yellow" = "Yellow";
|
||||
|
||||
/// Yes
|
||||
"yes" = "Yes";
|
||||
|
||||
"yes" = "Yes";
|
Loading…
Reference in New Issue