[iOS & tvOS] Localize Existing Strings (#1361)

This commit is contained in:
Joe Kribs 2024-12-13 12:40:37 -07:00 committed by GitHub
parent d001a96d6c
commit b0583125f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 99 additions and 99 deletions

View File

@ -73,7 +73,7 @@ enum DeviceType: String, Displayable, Codable, CaseIterable {
case .xbox:
return "Xbox"
case .other:
return "Other"
return L10n.other
}
}

View File

@ -51,7 +51,7 @@ extension MediaStream {
var properties: [TextPair] = []
if let value = type {
properties.append(.init(title: "Type", subtitle: value.rawValue))
properties.append(.init(title: L10n.type, subtitle: value.rawValue))
}
if let value = codec {
@ -63,7 +63,7 @@ extension MediaStream {
}
if let value = language {
properties.append(.init(title: "Language", subtitle: value))
properties.append(.init(title: L10n.language, subtitle: value))
}
if let value = timeBase {
@ -107,7 +107,7 @@ extension MediaStream {
}
if let value = channels {
properties.append(.init(title: "Channels", subtitle: value.description))
properties.append(.init(title: L10n.channels, subtitle: value.description))
}
if let value = sampleRate {
@ -115,7 +115,7 @@ extension MediaStream {
}
if let value = isDefault {
properties.append(.init(title: "Default", subtitle: value.description))
properties.append(.init(title: L10n.default, subtitle: value.description))
}
if let value = isForced {
@ -195,7 +195,7 @@ extension MediaStream {
}
if let value = deliveryURL {
properties.append(.init(title: "URL", subtitle: value))
properties.append(.init(title: L10n.url, subtitle: value))
}
if let value = deliveryURL {

View File

@ -17,7 +17,7 @@ enum CustomDeviceProfileAction: String, CaseIterable, Displayable, Storable {
var displayTitle: String {
switch self {
case .add:
return "Add"
return L10n.add
case .replace:
return "Replace"
}

View File

@ -41,7 +41,7 @@ enum MultiTapAction: String, GestureAction {
case .none:
return L10n.none
case .jump:
return "Jump"
return L10n.jump
}
}
}
@ -58,11 +58,11 @@ enum DoubleTouchAction: String, GestureAction {
case .none:
return L10n.none
case .aspectFill:
return "Aspect Fill"
return L10n.aspectFill
case .gestureLock:
return "Gesture Lock"
case .pausePlay:
return "Pause/Play"
return L10n.playAndPause
}
}
}
@ -83,17 +83,17 @@ enum PanAction: String, GestureAction {
case .none:
return L10n.none
case .audioffset:
return "Audio Offset"
return L10n.audioOffset
case .brightness:
return "Brightness"
case .playbackSpeed:
return "Playback Speed"
return L10n.playbackSpeed
case .scrub:
return "Scrub"
case .slowScrub:
return "Slow Scrub"
case .subtitleOffset:
return "Subtitle Offset"
return L10n.subtitleOffset
case .volume:
return "Volume"
}
@ -110,7 +110,7 @@ enum PinchAction: String, GestureAction {
case .none:
return L10n.none
case .aspectFill:
return "Aspect Fill"
return L10n.aspectFill
}
}
}
@ -125,7 +125,7 @@ enum SwipeAction: String, GestureAction {
case .none:
return L10n.none
case .jump:
return "Jump"
return L10n.jump
}
}
}

View File

@ -55,7 +55,7 @@ extension ItemFilterType: Displayable {
case .genres:
L10n.genres
case .letter:
"Letter"
L10n.letter
case .sortBy:
L10n.sort
case .sortOrder:
@ -65,7 +65,7 @@ extension ItemFilterType: Displayable {
case .traits:
L10n.filters
case .years:
"Years"
L10n.years
}
}
}

View File

@ -22,13 +22,13 @@ enum ItemSortBy: String, CaseIterable, Displayable, Codable {
var displayTitle: String {
switch self {
case .premiereDate:
return "Premiere date"
return L10n.premiereDate
case .name:
return "Name"
return L10n.name
case .dateAdded:
return "Date added"
return L10n.dateAdded
case .random:
return "Random"
return L10n.random
}
}
}

View File

@ -34,7 +34,7 @@ enum PlaybackButtonType: String, CaseIterable, Displayable, Defaults.Serializabl
case .large:
return "Large"
case .compact:
return "Compact"
return L10n.compact
}
}
}

View File

@ -17,9 +17,9 @@ enum StreamType: String, Displayable {
var displayTitle: String {
switch self {
case .direct:
return "Direct"
return L10n.direct
case .transcode:
return "Transcode"
return L10n.transcode
case .hls:
return "HLS"
}

View File

@ -19,7 +19,7 @@ enum TimestampType: String, CaseIterable, Defaults.Serializable, Displayable {
case .split:
return "Split"
case .compact:
return "Compact"
return L10n.compact
}
}
}

View File

@ -21,11 +21,11 @@ enum UserAccessPolicy: String, CaseIterable, Codable, Displayable {
var displayTitle: String {
switch self {
case .none:
"None"
L10n.none
case .requireDeviceAuthentication:
"Device Authentication"
case .requirePin:
"Pin"
L10n.pin
}
}
}

View File

@ -25,23 +25,23 @@ enum VideoPlayerActionButton: String, CaseIterable, Defaults.Serializable, Displ
var displayTitle: String {
switch self {
// case .advanced:
// return "Advanced"
// return L10n.advanced
case .aspectFill:
return "Aspect Fill"
return L10n.aspectFill
case .audio:
return "Audio"
return L10n.audio
case .autoPlay:
return "Auto Play"
return L10n.autoPlay
case .chapters:
return "Chapters"
return L10n.chapters
case .playbackSpeed:
return "Playback Speed"
return L10n.playbackSpeed
case .playNextItem:
return "Play Next Item"
return L10n.playNextItem
case .playPreviousItem:
return "Play Previous Item"
return L10n.playPreviousItem
case .subtitles:
return "Subtitles"
return L10n.subtitles
}
}

View File

@ -64,13 +64,13 @@ struct OrderedSectionSelectorView<Element: Displayable & Hashable>: View {
Text(L10n.enabled)
Spacer()
if editMode?.wrappedValue.isEditing ?? false {
Button("Done") {
Button(L10n.done) {
withAnimation {
editMode?.wrappedValue = .inactive
}
}
} else {
Button("Edit") {
Button(L10n.edit) {
withAnimation {
editMode?.wrappedValue = .active
}

View File

@ -51,7 +51,7 @@ struct AppSettingsView: View {
// )
// }
//
// ChevronButton("Logs")
// ChevronButton(L10n.logs)
// .onSelect {
// router.route(to: \.log)
// }
@ -68,7 +68,7 @@ struct AppSettingsView: View {
// Button {
// removeAllServersSelected = true
// } label: {
// Text("Remove All Servers")
// Text(L10n.removeAllServers)
// }
// }
// }
@ -79,9 +79,9 @@ struct AppSettingsView: View {
//// viewModel.resetUserSettings()
// }
// } message: {
// Text("Reset all settings back to defaults.")
// Text(L10n.resetAllSettings)
// }
// .alert("Remove All Servers", isPresented: $removeAllServersSelected) {
// .alert(L10n.removeAllServers, isPresented: $removeAllServersSelected) {
// Button(L10n.reset, role: .destructive) {
//// viewModel.removeAllServers()
// }

View File

@ -20,7 +20,7 @@ extension ItemView {
var body: some View {
PosterHStack(
title: "Special Features",
title: L10n.specialFeatures,
type: .landscape,
items: items
)

View File

@ -62,13 +62,13 @@ extension SelectUserView {
.buttonBorderShape(.circleBackport)
.disabled(!isEnabled)
Text("Add User")
Text(L10n.addUser)
.font(.title3)
.fontWeight(.semibold)
.foregroundStyle(isEnabled ? .primary : .secondary)
if serverSelection == .all {
Text("Hidden")
Text(L10n.hidden)
.font(.footnote)
.hidden()
}

View File

@ -56,7 +56,7 @@ extension SelectUserView {
Group {
switch serverSelection {
case .all:
Label("All Servers", systemImage: "person.2.fill")
Label(L10n.allServers, systemImage: "person.2.fill")
case let .server(id):
if let server = viewModel.servers.keys.first(where: { $0.id == id }) {
Label(server.name, systemImage: "server.rack")

View File

@ -87,7 +87,7 @@ extension SelectUserView {
.buttonStyle(.card)
.buttonBorderShape(.circleBackport)
// .contextMenu {
// Button("Delete", role: .destructive) {
// Button(L10n.delete, role: .destructive) {
// onDelete()
// }
// }

View File

@ -48,7 +48,7 @@ extension CustomDeviceProfileSettingsView {
profileDetailsView(
title: L10n.useAsTranscodingProfile,
detail: profile.useAsTranscodingProfile ? "Yes" : "No"
detail: profile.useAsTranscodingProfile ? L10n.yes : L10n.no
)
}

View File

@ -95,7 +95,7 @@ extension CustomDeviceProfileSettingsView {
HStack {
Text(L10n.customProfile)
Spacer()
Button("Save") {
Button(L10n.save) {
if createProfile {
customDeviceProfiles.append(profile.value)
}
@ -135,7 +135,7 @@ extension CustomDeviceProfileSettingsView {
}
.navigationTitle(L10n.customProfile)
.alert("Profile not saved", isPresented: $isPresentingNotSaved) {
Button("Close", role: .destructive) {
Button(L10n.close, role: .destructive) {
router.dismissCoordinator()
}
}

View File

@ -82,7 +82,7 @@ struct CustomDeviceProfileSettingsView: View {
Button(role: .destructive) {
deleteProfile(profile)
} label: {
Label("Delete", systemImage: "trash")
Label(L10n.delete, systemImage: "trash")
}
}
}
@ -93,7 +93,7 @@ struct CustomDeviceProfileSettingsView: View {
Text(L10n.profiles)
Spacer()
if customProfiles.isNotEmpty {
Button("Add") {
Button(L10n.add) {
router.route(to: \.createCustomDeviceProfile)
}
}

View File

@ -16,7 +16,7 @@ struct ListColumnsPickerView: View {
var body: some View {
StepperView(
title: "Columns",
title: L10n.columns,
value: $selection,
range: 1 ... 3,
step: 1

View File

@ -22,7 +22,7 @@ extension VideoPlayer {
VStack(spacing: 10) {
Text("Retrieving media information")
Text(L10n.retrievingMediaInformation)
.foregroundColor(.white)
ProgressView()
@ -30,7 +30,7 @@ extension VideoPlayer {
Button {
router.dismissCoordinator()
} label: {
Text("Cancel")
Text(L10n.cancel)
.foregroundColor(.red)
.padding()
.overlay {

View File

@ -20,11 +20,11 @@ extension VideoPlayer {
var displayTitle: String {
switch self {
case .audio:
return "Audio"
return L10n.audio
case .playbackSpeed:
return "Playback Speed"
return L10n.playbackSpeed
case .subtitles:
return "Subtitles"
return L10n.subtitles
}
}
}

View File

@ -87,7 +87,7 @@ struct VideoPlayer: View {
@ViewBuilder
private var loadingView: some View {
Text("Retrieving media information")
Text(L10n.retrievingMediaInformation)
}
var body: some View {

View File

@ -27,7 +27,7 @@ struct AppLoadingView: View {
}
}
.topBarTrailing {
Button("Advanced", systemImage: "gearshape.fill") {}
Button(L10n.advanced, systemImage: "gearshape.fill") {}
.foregroundStyle(.secondary)
.disabled(true)
.opacity(didFailMigration ? 0 : 1)

View File

@ -62,10 +62,10 @@ struct AppSettingsView: View {
Toggle("Use splashscreen", isOn: $selectUserUseSplashscreen)
if selectUserUseSplashscreen {
Picker("Servers", selection: $selectUserAllServersSplashscreen) {
Picker(L10n.servers, selection: $selectUserAllServersSplashscreen) {
Section {
Label("Random", systemImage: "dice.fill")
Label(L10n.random, systemImage: "dice.fill")
.tag(SelectUserServerSelection.all)
}

View File

@ -165,7 +165,7 @@ struct ChannelLibraryView: View {
// We repurposed `LibraryDisplayType` but want different labels
Picker("Channel Display", selection: $channelDisplayType) {
Label("Compact", systemImage: LibraryDisplayType.grid.systemImage)
Label(L10n.compact, systemImage: LibraryDisplayType.grid.systemImage)
.tag(LibraryDisplayType.grid)
Label("Detailed", systemImage: LibraryDisplayType.list.systemImage)

View File

@ -55,7 +55,7 @@ struct EditServerView: View {
}
if isEditing {
ListRowButton("Delete") {
ListRowButton(L10n.delete) {
isPresentingConfirmDeletion = true
}
.foregroundStyle(.red, .red.opacity(0.2))
@ -66,8 +66,8 @@ struct EditServerView: View {
.onChange(of: currentServerURL) { newValue in
viewModel.setCurrentURL(to: newValue)
}
.alert("Delete Server", isPresented: $isPresentingConfirmDeletion) {
Button("Delete", role: .destructive) {
.alert(L10n.deleteServer, isPresented: $isPresentingConfirmDeletion) {
Button(L10n.delete, role: .destructive) {
viewModel.delete()
router.popLast()
}

View File

@ -59,7 +59,7 @@ extension PagingLibraryView {
}
}
Section("Layout") {
Section(L10n.layout) {
Button {
viewType = .grid
} label: {
@ -87,9 +87,9 @@ extension PagingLibraryView {
} label: {
switch viewType {
case .grid:
Label("Layout", systemImage: "square.grid.2x2.fill")
Label(L10n.layout, systemImage: "square.grid.2x2.fill")
case .list:
Label("Layout", systemImage: "square.fill.text.grid.1x2")
Label(L10n.layout, systemImage: "square.fill.text.grid.1x2")
}
}
}

View File

@ -63,13 +63,13 @@ extension SelectUserView {
.clipShape(.circle)
.aspectRatio(1, contentMode: .fill)
Text("Add User")
Text(L10n.addUser)
.font(.title3)
.fontWeight(.semibold)
.foregroundStyle(isEnabled ? .primary : .secondary)
if serverSelection == .all {
Text("Hidden")
Text(L10n.hidden)
.font(.footnote)
.hidden()
}

View File

@ -48,7 +48,7 @@ extension SelectUserView {
private var rowContent: some View {
HStack {
Text("Add User")
Text(L10n.addUser)
.font(.title3)
.fontWeight(.semibold)
.foregroundStyle(isEnabled ? .primary : .secondary)

View File

@ -45,21 +45,21 @@ extension SelectUserView {
var body: some View {
Menu {
Section {
Button("Add Server", systemImage: "plus") {
Button(L10n.addServer, systemImage: "plus") {
router.route(to: \.connectToServer)
}
if let selectedServer {
Button("Edit Server", systemImage: "server.rack") {
Button(L10n.editServer, systemImage: "server.rack") {
router.route(to: \.editServer, selectedServer)
}
}
}
Picker("Servers", selection: _serverSelection) {
Picker(L10n.servers, selection: _serverSelection) {
if viewModel.servers.keys.count > 1 {
Label("All Servers", systemImage: "person.2.fill")
Label(L10n.allServers, systemImage: "person.2.fill")
.tag(SelectUserServerSelection.all)
}
@ -83,7 +83,7 @@ extension SelectUserView {
HStack {
switch serverSelection {
case .all:
Label("All Servers", systemImage: "person.2.fill")
Label(L10n.allServers, systemImage: "person.2.fill")
case let .server(id):
if let server = viewModel.servers.keys.first(where: { $0.id == id }) {
Label(server.name, systemImage: "server.rack")

View File

@ -126,7 +126,7 @@ extension SelectUserView {
}
.buttonStyle(.plain)
.contextMenu {
Button("Delete", role: .destructive) {
Button(L10n.delete, role: .destructive) {
onDelete()
}
}

View File

@ -131,7 +131,7 @@ extension SelectUserView {
}
.onSelect(perform: action)
.contextMenu {
Button("Delete", role: .destructive) {
Button(L10n.delete, role: .destructive) {
onDelete()
}
}

View File

@ -50,7 +50,7 @@ extension CustomDeviceProfileSettingsView {
profileDetailsView(
title: L10n.useAsTranscodingProfile,
detail: profile.useAsTranscodingProfile ? "Yes" : "No"
detail: profile.useAsTranscodingProfile ? L10n.yes : L10n.no
)
}

View File

@ -123,7 +123,7 @@ extension CustomDeviceProfileSettingsView {
}
.navigationTitle(L10n.customProfile)
.topBarTrailing {
Button("Save") {
Button(L10n.save) {
if createProfile {
customDeviceProfiles.append(profile)
} else {
@ -137,7 +137,7 @@ extension CustomDeviceProfileSettingsView {
.disabled(!isValid)
}
.alert("Profile not saved", isPresented: $isPresentingNotSaved) {
Button("Close", role: .destructive) {
Button(L10n.close, role: .destructive) {
router.dismissCoordinator()
}
}

View File

@ -57,6 +57,6 @@ struct GestureSettingsView: View {
CaseIterablePicker("Right Vertical Pan", selection: $verticalPanGestureRight)
}
}
.navigationTitle("Gestures")
.navigationTitle(L10n.gestures)
}
}

View File

@ -20,7 +20,7 @@ struct NativeVideoPlayerSettingsView: View {
Section {
BasicStepper(
title: "Resume Offset",
title: L10n.resumeOffset,
value: $resumeOffset,
range: 0 ... 30,
step: 1
@ -29,9 +29,9 @@ struct NativeVideoPlayerSettingsView: View {
$0.secondLabel
}
} footer: {
Text("Resume content seconds before the recorded resume time")
Text(L10n.resumeOffsetDescription)
}
}
.navigationTitle("Native Player")
.navigationTitle(L10n.nativePlayer)
}
}

View File

@ -89,14 +89,14 @@ struct UserProfileSettingsView: View {
router.route(to: \.quickConnect)
}
ChevronButton("Password")
ChevronButton(L10n.password)
.onSelect {
router.route(to: \.resetUserPassword, viewModel.userSession.user.id)
}
}
Section {
ChevronButton("Security")
ChevronButton(L10n.security)
.onSelect {
router.route(to: \.localSecurity)
}
@ -114,7 +114,7 @@ struct UserProfileSettingsView: View {
}
}
.alert("Reset Settings", isPresented: $isPresentingConfirmReset) {
Button("Reset", role: .destructive) {
Button(L10n.reset, role: .destructive) {
do {
try viewModel.userSession.user.deleteSettings()
} catch {
@ -134,7 +134,7 @@ struct UserProfileSettingsView: View {
router.route(to: \.photoPicker, viewModel)
}
Button("Delete", role: .destructive) {
Button(L10n.delete, role: .destructive) {
viewModel.deleteCurrentUserProfileImage()
}
}

View File

@ -42,7 +42,7 @@ extension VideoPlayerSettingsView {
Text(L10n.subtitle)
} footer: {
// TODO: better wording
Text("Settings only affect some subtitle types")
Text(L10n.subtitlesDisclaimer)
}
}
}

View File

@ -43,7 +43,7 @@ extension UserSignInView {
List {
Section {
CaseIterablePicker("Security", selection: $updateSignInPolicy)
CaseIterablePicker(L10n.security, selection: $updateSignInPolicy)
} footer: {
// TODO: descriptions of each section
@ -59,7 +59,7 @@ extension UserSignInView {
Text(UserAccessPolicy.requireDeviceAuthentication.displayTitle)
.fontWeight(.semibold)
Text("Require device authentication when signing in to the user.")
Text(L10n.requireDeviceAuthDescription)
}
.padding(.bottom, 15)
@ -67,7 +67,7 @@ extension UserSignInView {
Text(UserAccessPolicy.requirePin.displayTitle)
.fontWeight(.semibold)
Text("Require a local pin when signing in to the user. This pin is unrecoverable.")
Text(L10n.requirePinDescription)
}
.padding(.bottom, 15)
@ -75,7 +75,7 @@ extension UserSignInView {
Text(UserAccessPolicy.none.displayTitle)
.fontWeight(.semibold)
Text("Save the user to this device without any local authentication.")
Text(L10n.saveUserWithoutAuthDescription)
}
}
.frame(width: max(10, listSize.width - 50))
@ -84,16 +84,16 @@ extension UserSignInView {
if accessPolicy == .requirePin {
Section {
TextField("Hint", text: $updatePinHint)
TextField(L10n.hint, text: $updatePinHint)
} header: {
Text("Hint")
Text(L10n.hint)
} footer: {
Text("Set a hint when prompting for the pin.")
Text(L10n.setPinHintDescription)
}
}
}
.animation(.linear, value: accessPolicy)
.navigationTitle("Security")
.navigationTitle(L10n.security)
.navigationBarTitleDisplayMode(.inline)
.navigationBarCloseButton {
router.popLast()