From 16db474a0d7bdf2f96d832666d8319a05d991c70 Mon Sep 17 00:00:00 2001 From: Tim Xie Date: Wed, 14 Aug 2024 14:41:13 -0700 Subject: [PATCH] more localisation support --- CubeTime/Helper/Helper.swift | 4 +- CubeTime/Helper/HelperButtons.swift | 5 +- CubeTime/Models.swift | 2 +- CubeTime/Onboarding/Updates.swift | 2 +- CubeTime/Sessions/NewSessionRootView.swift | 14 +- CubeTime/Sessions/SessionCard.swift | 8 +- CubeTime/Sessions/SessionsView.swift | 4 +- CubeTime/Sessions/Tools/ToolsList.swift | 37 ++- CubeTime/Settings/AppearanceSettings.swift | 4 +- CubeTime/Settings/GeneralSettings.swift | 4 +- CubeTime/Settings/SettingsView.swift | 2 +- CubeTime/Stats/StatsDetailView.swift | 6 +- .../Stats/TimeTrend/TimeTrendDetailView.swift | 2 +- .../StopwatchManager/ExportViewModel.swift | 6 +- .../StopwatchManager+Stats.swift | 2 +- Localizable.xcstrings | 224 ++++++++++++++++-- 16 files changed, 255 insertions(+), 71 deletions(-) diff --git a/CubeTime/Helper/Helper.swift b/CubeTime/Helper/Helper.swift index 0a4b7285..39633d7c 100644 --- a/CubeTime/Helper/Helper.swift +++ b/CubeTime/Helper/Helper.swift @@ -15,7 +15,7 @@ let sessionTypeForID: [SessionType: Session.Type] = [ let sessionDescriptions: [SessionType: String] = [ .multiphase: String(localized: "A multiphase session gives you the ability to breakdown your solves into sections, such as memo/exec stages in blindfolded solving or stages in 3x3 solves.\n\nTap anywhere on the timer during a solve to record a phase lap. You can access your breakdown statistics in each time card and view overall statistics in the Stats view."), .playground: String(localized: "A playground session allows you to quickly change the scramble type within a session without having to specify a scramble type for the whole session."), - .compsim: String(localized: "A comp sim (Competition Simulation) session mimics a competition scenario better by recording a non-rolling session. Your solves will be split up into averages of 5 that can be accessed in your times and statistics view.\n\nStart by choosing a target to reach.") + .compsim: String(localized: "A compsim (Competition Simulation) session mimics a competition scenario better by recording a non-rolling session. Your solves will be split up into averages of 5 that can be accessed in your times and statistics view.\n\nStart by choosing a target to reach.") ] @@ -321,7 +321,7 @@ func getAvgOfSolveGroup(_ compsimsolvegroup: CompSimSolveGroup) -> CalculatedAve let trimmedSolves: [Solve] = sorted.prefix(trim) + sorted.suffix(trim) return CalculatedAverage( - name: "Comp Sim", + name: "Compsim", average: sorted.dropFirst(trim).dropLast(trim) .reduce(0, { $0 + $1.timeIncPen }) / Double(3), accountedSolves: sorted, diff --git a/CubeTime/Helper/HelperButtons.swift b/CubeTime/Helper/HelperButtons.swift index 9c35550f..0a4ac737 100644 --- a/CubeTime/Helper/HelperButtons.swift +++ b/CubeTime/Helper/HelperButtons.swift @@ -409,14 +409,15 @@ struct ContextMenuButton: View { } var body: some View { - Button(role: title == "Delete Session" ? .destructive : nil, action: delayedAction) { + Button(role: systemImage == "trash" ? .destructive : nil, action: delayedAction) { HStack { Text(title) if image != nil { Image(uiImage: image!) } } - }.disabled(disableButton ?? false) + } + .disabled(disableButton ?? false) } private var image: UIImage? { diff --git a/CubeTime/Models.swift b/CubeTime/Models.swift index 66df12eb..195bcf80 100644 --- a/CubeTime/Models.swift +++ b/CubeTime/Models.swift @@ -120,7 +120,7 @@ extension CompSimSolveGroup { } var avg: CalculatedAverage? { - return StopwatchManager.getCalculatedAverage(forSolves: self.solves!.allObjects as! [Solve], name: String(localized: "Comp Sim Group"), isCompsim: true) + return StopwatchManager.getCalculatedAverage(forSolves: self.solves!.allObjects as! [Solve], name: String(localized: "Compsim Group"), isCompsim: true) } } diff --git a/CubeTime/Onboarding/Updates.swift b/CubeTime/Onboarding/Updates.swift index 09129cce..592d7f5d 100644 --- a/CubeTime/Onboarding/Updates.swift +++ b/CubeTime/Onboarding/Updates.swift @@ -109,7 +109,7 @@ let updatesList: [String: (majorAdditions: [ListPoint]?, bugFixes: [ ListPoint(1, "fixed various dynamic type bugs"), ListPoint(1, "fixed calculator tool bugs"), - ListPoint(1, "fixed comp sim crashing"), + ListPoint(1, "fixed compsim crashing"), ListPoint(1, "fixed time distribution crashing"), ListPoint(1, "fixed inverted iPad trackpad gesture to show penalty bar"), ListPoint(1, "fixed select all only selected shown solves"), diff --git a/CubeTime/Sessions/NewSessionRootView.swift b/CubeTime/Sessions/NewSessionRootView.swift index d3cd4bb2..af3f8b04 100644 --- a/CubeTime/Sessions/NewSessionRootView.swift +++ b/CubeTime/Sessions/NewSessionRootView.swift @@ -84,29 +84,29 @@ struct NewSessionRootView: View { VStack(alignment: .leading, spacing: 48) { - NewSessionTypeCardGroup(title: "Normal Sessions") { + NewSessionTypeCardGroup(title: String(localized: "Normal Sessions")) { NavigationLink(destination: NewSessionView(sessionType: SessionType.standard, typeName: "Standard", showNewSessionPopUp: $showNewSessionPopUp)) { - NewSessionTypeCard(name: "Standard Session", icon: SessionTypeIcon(iconName: "timer.square")) + NewSessionTypeCard(name: String(localized: "Standard Session"), icon: SessionTypeIcon(iconName: "timer.square")) } CTDivider() .padding(.leading, 48) NavigationLink(destination: NewSessionView(sessionType: SessionType.multiphase, typeName: "Multiphase", showNewSessionPopUp: $showNewSessionPopUp)) { - NewSessionTypeCard(name: "Multiphase", icon: SessionTypeIcon(size: 24, iconName: "square.stack", padding: (10, 6))) + NewSessionTypeCard(name: String(localized: "Multiphase"), icon: SessionTypeIcon(size: 24, iconName: "square.stack", padding: (10, 6))) } CTDivider() .padding(.leading, 48) NavigationLink(destination: NewSessionView(sessionType: SessionType.playground, typeName: "Playground", showNewSessionPopUp: $showNewSessionPopUp)) { - NewSessionTypeCard(name: "Playground", icon: SessionTypeIcon(size: 24, iconName: "square.on.square")) + NewSessionTypeCard(name: String(localized: "Playground"), icon: SessionTypeIcon(size: 24, iconName: "square.on.square")) } } - NewSessionTypeCardGroup(title: "Other Sessions") { - NavigationLink(destination: NewSessionView(sessionType: SessionType.compsim, typeName: "Comp Sim", showNewSessionPopUp: $showNewSessionPopUp)) { - NewSessionTypeCard(name: "Comp Sim", icon: SessionTypeIcon(iconName: "globe.asia.australia", weight: .medium)) + NewSessionTypeCardGroup(title: String(localized: "Other Sessions")) { + NavigationLink(destination: NewSessionView(sessionType: SessionType.compsim, typeName: "Compsim", showNewSessionPopUp: $showNewSessionPopUp)) { + NewSessionTypeCard(name: String(localized: "Compsim"), icon: SessionTypeIcon(iconName: "globe.asia.australia", weight: .medium)) } } } diff --git a/CubeTime/Sessions/SessionCard.swift b/CubeTime/Sessions/SessionCard.swift index 1198c010..5d886427 100644 --- a/CubeTime/Sessions/SessionCard.swift +++ b/CubeTime/Sessions/SessionCard.swift @@ -153,7 +153,7 @@ struct SessionCard: View { .contextMenu(menuItems: { ContextMenuButton(delay: false, action: { isShowingCustomizeDialog = true }, - title: "Customise", + title: String(localized: "Customise"), systemImage: "pencil", disableButton: false); ContextMenuButton(delay: true, @@ -163,13 +163,13 @@ struct SessionCard: View { try! managedObjectContext.save() } }, - title: item.pinned ? "Unpin" : "Pin", + title: item.pinned ? String(localized: "Unpin") : String(localized: "Pin"), systemImage: item.pinned ? "pin.slash" : "pin", disableButton: false); Divider() ContextMenuButton(delay: false, action: { isShowingDeleteDialog = true }, - title: "Delete Session", + title: String(localized: "Delete Session"), systemImage: "trash", disableButton: allSessions.count <= 1) .foregroundColor(Color.red) @@ -181,7 +181,7 @@ struct SessionCard: View { .tint(Color("accent")) } - .confirmationDialog(String("Are you sure you want to delete \"\(self.item.name ?? "this session")\"? All solves will be deleted and this cannot be undone."), isPresented: $isShowingDeleteDialog, titleVisibility: .visible) { + .confirmationDialog(String(localized: "Are you sure you want to delete \"\(self.item.name ?? "this session")\"? All solves will be deleted and this cannot be undone."), isPresented: $isShowingDeleteDialog, titleVisibility: .visible) { Button("Confirm", role: .destructive) { if item == stopwatchManager.currentSession { var next: Session? = nil diff --git a/CubeTime/Sessions/SessionsView.swift b/CubeTime/Sessions/SessionsView.swift index f3e58d7e..2d530f6d 100644 --- a/CubeTime/Sessions/SessionsView.swift +++ b/CubeTime/Sessions/SessionsView.swift @@ -327,10 +327,10 @@ struct EventPicker: View { onTapRun: { sessionEventType = Int32(index) }) { - Image(element.name) + Image(element.imageName) .renderingMode(.template) .resizable() - .frame(width: imageSize, height: imageSize) + .frame(width: imageSize * 1.2, height: imageSize * 1.2) } } } diff --git a/CubeTime/Sessions/Tools/ToolsList.swift b/CubeTime/Sessions/Tools/ToolsList.swift index c9f35820..81d210aa 100644 --- a/CubeTime/Sessions/Tools/ToolsList.swift +++ b/CubeTime/Sessions/Tools/ToolsList.swift @@ -5,21 +5,28 @@ class ToolsViewModel: ObservableObject { } +enum ToolType: Identifiable { + case timerOnly, scrambleOnly, scrambleGenerator, calculator + + var id: ToolType { self } +} + struct Tool: Identifiable, Equatable { - var id: String { - get { return name } + var id: ToolType { + get { return self.toolType } } let name: String + let toolType: ToolType let iconName: String let description: String } let tools: [Tool] = [ - Tool(name: "Timer Only", iconName: "stopwatch", description: "Just a timer. No scrambles are shown. Your solves are **not** recorded and are not saved to a session."), - Tool(name: "Scramble Only", iconName: "cube", description: "Displays one scramble at a time. A timer is not shown. Tap to generate the next scramble."), - Tool(name: "Scramble Generator", iconName: "server.rack", description: "Generate multiple scrambles at once, to share, save or use."), - Tool(name: "Calculator", iconName: "function", description: "Simple average and mean calculator."), + Tool(name: String(localized: "Timer Only"), toolType: .timerOnly, iconName: "stopwatch", description: String(localized: "Just a timer. No scrambles are shown. Your solves are **not** recorded and are not saved to a session.")), + Tool(name: String(localized: "Scramble Only"), toolType: .scrambleOnly, iconName: "cube", description: String(localized: "Displays one scramble at a time. A timer is not shown. Tap to generate the next scramble.")), + Tool(name: String(localized: "Scramble Generator"), toolType: .scrambleGenerator, iconName: "server.rack", description: String(localized: "Generate multiple scrambles at once, to share, save or use.")), + Tool(name: String(localized: "Calculator"), toolType: .calculator, iconName: "function", description: String(localized: "Simple average and mean calculator.")), /* Tool(name: "Tracker", iconName: "scope", description: "Track someone's average at a comp. Calculates times needed for a chance for a target, BPA, WPA, and more."), Tool(name: "Scorecard Generator", iconName: "printer", description: "Export scorecards for use at meetups (or comps!)."), @@ -76,27 +83,19 @@ struct ToolsList: View { Group { if let tool = toolsViewModel.currentTool { - switch (tool.name) { - case "Timer Only": + switch (tool.toolType) { + case .timerOnly: TimerOnlyTool() - case "Scramble Only": + case .scrambleOnly: ScrambleOnlyTool() - case "Scramble Generator": + case .scrambleGenerator: ScrambleGeneratorTool() - case "Calculator": + case .calculator: CalculatorTool() - /* - case "Tracker": - EmptyView() - - case "Scorecard Generator": - EmptyView() - */ - default: EmptyView() } diff --git a/CubeTime/Settings/AppearanceSettings.swift b/CubeTime/Settings/AppearanceSettings.swift index bd1456df..2c9db9fe 100644 --- a/CubeTime/Settings/AppearanceSettings.swift +++ b/CubeTime/Settings/AppearanceSettings.swift @@ -160,8 +160,8 @@ struct AppearanceSettingsView: View { } } - SettingsDragger(text: "Font Weight", value: $fontWeight, in: 300...800) - SettingsDragger(text: "Font Casualness", value: $fontCasual, in: 0...1) + SettingsDragger(text: String(localized: "Font Weight"), value: $fontWeight, in: 300...800) + SettingsDragger(text: String(localized: "Font Casualness"), value: $fontCasual, in: 0...1) SettingsToggle(String(localized: "Cursive Font"), $fontCursive) } diff --git a/CubeTime/Settings/GeneralSettings.swift b/CubeTime/Settings/GeneralSettings.swift index d55d57ad..be2e69f7 100644 --- a/CubeTime/Settings/GeneralSettings.swift +++ b/CubeTime/Settings/GeneralSettings.swift @@ -1,8 +1,10 @@ import SwiftUI -enum InputMode: String, Codable, CaseIterable { +enum InputMode: LocalizedStringKey, Codable, CaseIterable, Identifiable { case timer = "Timer" case typing = "Typing" + + var id: InputMode { self } /*, stackmat, smartcube, virtual*/ } diff --git a/CubeTime/Settings/SettingsView.swift b/CubeTime/Settings/SettingsView.swift index 4340915b..f7fe0b69 100644 --- a/CubeTime/Settings/SettingsView.swift +++ b/CubeTime/Settings/SettingsView.swift @@ -209,7 +209,7 @@ struct SettingsDetail: View { ZStack { RoundedRectangle(cornerRadius: 12, style: .continuous) .fill(Color("overlay0")) - .matchedGeometryEffect(id: "bg " + currentCard.name, in: namespace) + .matchedGeometryEffect(id: "bg \(currentCard.id)", in: namespace) .ignoresSafeArea() .shadowLight(x: 0, y: 3) diff --git a/CubeTime/Stats/StatsDetailView.swift b/CubeTime/Stats/StatsDetailView.swift index 638a8657..e222dd3d 100644 --- a/CubeTime/Stats/StatsDetailView.swift +++ b/CubeTime/Stats/StatsDetailView.swift @@ -122,7 +122,7 @@ struct StatsDetailView: View { Text("|") .offset(y: -1) // slight offset of bar - Text(solves.name == "Comp Sim Solve" ? "compsim" : solves.name.lowercased()) + Text(solves.name == "Compsim Solve" ? "compsim" : solves.name.lowercased()) Spacer() @@ -142,9 +142,9 @@ struct StatsDetailView: View { let shareStr = getShareStr(solves: solves) HStack(spacing: 8) { - CTCopyButton(toCopy: shareStr, buttonText: "Copy Average") + CTCopyButton(toCopy: shareStr, buttonText: String(localized: "Copy Average")) - CTShareButton(toShare: shareStr, buttonText: "Share Average") + CTShareButton(toShare: shareStr, buttonText: String(localized: "Share Average")) } .padding(.top, 16) .padding(.bottom, 4) diff --git a/CubeTime/Stats/TimeTrend/TimeTrendDetailView.swift b/CubeTime/Stats/TimeTrend/TimeTrendDetailView.swift index f0522b24..a6c2d3df 100644 --- a/CubeTime/Stats/TimeTrend/TimeTrendDetailView.swift +++ b/CubeTime/Stats/TimeTrend/TimeTrendDetailView.swift @@ -54,7 +54,7 @@ struct TimeTrendDetail: View { @State var selectedLines = [true] let labels: [(label: String, type: CTButtonType)] = [ - ("time", .halfcoloured(nil)), + (String(localized: "time"), .halfcoloured(nil)), // ("ao5", .green), // ("ao12", .red), // ("ao100", .orange) diff --git a/CubeTime/StopwatchManager/ExportViewModel.swift b/CubeTime/StopwatchManager/ExportViewModel.swift index 1d753c81..27444cb8 100644 --- a/CubeTime/StopwatchManager/ExportViewModel.swift +++ b/CubeTime/StopwatchManager/ExportViewModel.swift @@ -59,7 +59,7 @@ class ExportFormat: ReferenceFileDocument { class CSVExportFormat: ExportFormat { override func getName() -> String { - return "CSV (generic)" + return String(localized: "CSV (generic)") } static var _readableContentTypes: [UTType] = [.commaSeparatedText] @@ -145,7 +145,7 @@ let styles = ##""" class ODSExportFormat: ExportFormat { override func getName() -> String { - return "ODF (MS Excel/Google Sheets)" + return String(localized: "ODF (MS Excel/Google Sheets)") } static var _readableContentTypes: [UTType] = [.zip] @@ -303,7 +303,7 @@ class ODSExportFormat: ExportFormat { class CSTimerExportFormat: ExportFormat { override func getName() -> String { - return "JSON (csTimer)" + return String(localized: "JSON (csTimer)") } static var _readableContentTypes: [UTType] = [.commaSeparatedText] diff --git a/CubeTime/StopwatchManager/StopwatchManager+Stats.swift b/CubeTime/StopwatchManager/StopwatchManager+Stats.swift index 96961610..57efa807 100644 --- a/CubeTime/StopwatchManager/StopwatchManager+Stats.swift +++ b/CubeTime/StopwatchManager/StopwatchManager+Stats.swift @@ -540,7 +540,7 @@ extension StopwatchManager { return (nil, []) } else { var bestAverage: CalculatedAverage? -// var bestAverage: CalculatedAverage = calculateAverage(((compsimSession.solvegroups!.firstObject as! CompSimSolveGroup).solves!.array as! [Solves]), "Best Comp Sim", true)! +// var bestAverage: CalculatedAverage = calculateAverage(((compsimSession.solvegroups!.firstObject as! CompSimSolveGroup).solves!.array as! [Solves]), "Best Compsim", true)! for solvegroup in compsimSolveGroups { if solvegroup.solves!.allObjects.count == 5 { diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 240f02e7..b932b666 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -126,7 +126,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "%lld d.p" + "value" : "%lld位数" } } } @@ -301,14 +301,8 @@ } } }, - "A comp sim (Competition Simulation) session mimics a competition scenario better by recording a non-rolling session. Your solves will be split up into averages of 5 that can be accessed in your times and statistics view.\n\nStart by choosing a target to reach." : { + "A compsim (Competition Simulation) session mimics a competition scenario better by recording a non-rolling session. Your solves will be split up into averages of 5 that can be accessed in your times and statistics view.\n\nStart by choosing a target to reach." : { "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "A Compsim (Competition Simulation) session mimics a competition scenario better by recording a non-rolling session. Your solves will be split up into averages of 5 that can be accessed in your times and statistics view.\n\nStart by choosing a target to reach." - } - }, "zh-Hans" : { "stringUnit" : { "state" : "translated", @@ -419,6 +413,16 @@ } } }, + "Are you sure you want to delete \"%@\"? All solves will be deleted and this cannot be undone." : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "确定删除\"%@\"?此操作会删除分组里所有还原成绩。" + } + } + } + }, "Are you sure you want to delete this solve?" : { "localizations" : { "zh-Hans" : { @@ -558,6 +562,9 @@ } } } + }, + "Calculator" : { + }, "Cancel" : { "localizations" : { @@ -639,34 +646,28 @@ } } }, - "Comp Sim Group" : { + "Compsim" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Compsim Group" + "value" : "Compsim" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "比赛模拟还原组" + "value" : "比赛模拟模式" } } } }, - "Compsim" : { + "Compsim Group" : { "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Compsim" - } - }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "比赛模拟模式" + "value" : "比赛模拟还原组" } } } @@ -706,7 +707,17 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "复制" + "value" : "拷贝" + } + } + } + }, + "Copy Average" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "拷贝" } } } @@ -716,7 +727,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "复制打乱" + "value" : "拷贝打乱" } } } @@ -741,6 +752,16 @@ } } }, + "CSV (generic)" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV (通用)" + } + } + } + }, "CubeTime" : { "localizations" : { "zh-Hans" : { @@ -841,6 +862,16 @@ } } }, + "Customise" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "编辑" + } + } + } + }, "Customise Session" : { "localizations" : { "zh-Hans" : { @@ -911,6 +942,16 @@ } } }, + "Delete Session" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "删除分组" + } + } + } + }, "Delete Solve Group" : { "localizations" : { "zh-Hans" : { @@ -940,6 +981,9 @@ } } } + }, + "Displays one scramble at a time. A timer is not shown. Tap to generate the next scramble." : { + }, "DNF" : { "localizations" : { @@ -1061,6 +1105,16 @@ } } }, + "Font Casualness" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "字体随意性" + } + } + } + }, "Font Settings" : { "localizations" : { "zh-Hans" : { @@ -1071,6 +1125,16 @@ } } }, + "Font Weight" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "字体粗细" + } + } + } + }, "General" : { "localizations" : { "zh-Hans" : { @@ -1090,6 +1154,9 @@ } } } + }, + "Generate multiple scrambles at once, to share, save or use." : { + }, "Generate!" : { "localizations" : { @@ -1270,6 +1337,19 @@ } } } + }, + "JSON (csTimer)" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "JSON (csTimer)" + } + } + } + }, + "Just a timer. No scrambles are shown. Your solves are **not** recorded and are not saved to a session." : { + }, "L' D R2 B2 D2 F2 R2 B2 D R2 D R2 U B' R F2 R U' F L2 D'" : { "localizations" : { @@ -1481,6 +1561,16 @@ } } }, + "Normal Sessions" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "普通分组" + } + } + } + }, "not enough solves to\ndisplay graph" : { "localizations" : { "zh-Hans" : { @@ -1501,6 +1591,16 @@ } } }, + "ODF (MS Excel/Google Sheets)" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "ODF (MS Excel/Google Sheets)" + } + } + } + }, "OK" : { "localizations" : { "zh-Hans" : { @@ -1651,6 +1751,16 @@ } } }, + "Pin" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "标星" + } + } + } + }, "Pin Session?" : { "localizations" : { "zh-Hans" : { @@ -1800,6 +1910,12 @@ } } } + }, + "Scramble Generator" : { + + }, + "Scramble Only" : { + }, "Scramble Size: " : { "localizations" : { @@ -1941,6 +2057,16 @@ } } }, + "Share Average" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "分享" + } + } + } + }, "Show a button in the top right corner to enter zen mode." : { "localizations" : { "zh-Hans" : { @@ -2042,6 +2168,9 @@ } } } + }, + "Simple average and mean calculator." : { + }, "Skewb" : { "localizations" : { @@ -2103,6 +2232,16 @@ } } }, + "Standard Session" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "单项分组" + } + } + } + }, "Start Over" : { "localizations" : { "zh-Hans" : { @@ -2253,6 +2392,16 @@ } } }, + "time" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "单次" + } + } + } + }, "Time" : { "localizations" : { "zh-Hans" : { @@ -2313,6 +2462,16 @@ } } }, + "Timer" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "计时器" + } + } + } + }, "Timer Interface" : { "localizations" : { "zh-Hans" : { @@ -2332,6 +2491,9 @@ } } } + }, + "Timer Only" : { + }, "Timer Settings" : { "localizations" : { @@ -2443,6 +2605,16 @@ } } }, + "Typing" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "手动输入" + } + } + } + }, "Unlock Scramble" : { "localizations" : { "zh-Hans" : { @@ -2473,6 +2645,16 @@ } } }, + "Unpin" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "取消星标" + } + } + } + }, "Use Inspection" : { "localizations" : { "zh-Hans" : {