Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds localisation support! #37

Merged
merged 14 commits into from
Jul 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
- [Getting Started](#toolbox-getting-started)
- [Download](#floppy_disk-download)
- [Development](#gear-development)
- [Localisation](#speech_balloon-localisation)
<!-- - [Running Tests](#test_tube-running-tests) -->
- [Builds](#triangular_flag_on_post-builds)
- [Roadmap](https://github.com/users/othyn/projects/1)
Expand Down Expand Up @@ -132,6 +133,29 @@ Build and run through Xcode as you normally would to a macOS target. As a note,

This is a side project, so feel free to submit a PR for any functionality/bug fixes and go ham. There aren't any contributing guidelines as of yet, code style is handled by `swiftlint` (`$ brew install swiftlint`) and should automatically fix the style upon build (there is a GH Action setup for this also).

### :speech_balloon: Localisation

This project supports localisation! Please see the pinned [language support issue](https://github.com/othyn/macos-auto-clicker/issues/10) as a discussion place for new language support. See the [`auto-clicker/Localisation`](https://github.com/othyn/macos-auto-clicker/tree/main/auto-clicker/Localisation) project directory to view currently supported languages, the project default being `en-GB`.

The project makes use of the Apple default `Localizable.strings` and `Localizable.stringsdict` to support local translations. See [issue #36](https://github.com/othyn/macos-auto-clicker/issues/36) for some useful links and resources for implementing translations. The short tutorial is;

0. Clone this project locally to somewhere on your machine.
1. Open up the project **workspace** in Xcode.
2. Click on the top level project item in the project file browser sidebar in Xcode.
3. Click on the project under the Project heading.
4. Click on the 'Info' tab.
5. Scroll down to 'Localizations'.
6. Click the plus button at the bottom of the table displaying the current project languages and select the language you intend to provide translations for.
7. On the on screen prompt **change nothing** and just click 'Finish'
8. The new language will appear in the `Localisation` folder as a new version of the `Localizable.strings` file, with the language code identifier along with the file and appearing as the parent directory in the `Localisation` folder. E.g. for France this would be `fr`. You will know if the translation file has been picked up by Xcode as the language will appear with the amount of `Localizable.strings` translation files found by Xcode in that 'Localizations' table mentioned in step 5.
9. Once you have written the translations by providing the appropriate new strings in the language you've chosen as are required by the application, please submit a new PR to merge in the new language as a supported language.

<div align=center>
<img width="90%" src="art/ref/readme_localisations_guide.jpg"/>
</div>

More information on Apple's localisation practices can be found on [their official docs](https://developer.apple.com/localization/), and [their official docs for Xcode](https://developer.apple.com/documentation/xcode/localization). There is also an application, [`mohakapt/Stringz`](https://github.com/mohakapt/Stringz), who's sole intent is to make writing and maintaining language translations easier. See [issue #36](https://github.com/othyn/macos-auto-clicker/issues/36) for some more useful links and resources for implementing translations.

<!-- Running Tests -->

<!-- ### :test_tube: Running Tests
Expand Down
Binary file added art/ref/readme_localisations_guide.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
71 changes: 61 additions & 10 deletions auto-clicker.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
allowLocationSimulation = "YES"
showNonLocalizedStrings = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
Expand Down
4 changes: 2 additions & 2 deletions auto-clicker/Build Assets/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>dd113b7</string>
<string>3b6832b</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>with ♥️ by Othyn</string>
<string>Othyn ~ Ben Tindall</string>
</dict>
</plist>
4 changes: 3 additions & 1 deletion auto-clicker/Constants/Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import Cocoa
import Defaults

extension Defaults.Keys {
static let windowSize = Key<CGVector>("window_size", default: CGVector(dx: 550, dy: 430))

static let windowShouldKeepOnTop = Key<Bool>("window_should_keep_on_top", default: false)

static let appearanceSelectedTheme = Key<Theme>("appearance_selected_theme", default: .Blue)
static let appearanceSelectedTheme = Key<ThemeService>("appearance_selected_theme", default: ThemeService())

static let autoClickerState = Key<FormState>("user_form_state", default: FormState())
}
95 changes: 41 additions & 54 deletions auto-clicker/Enums/Theme.swift → auto-clicker/Enums/Colour.swift
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
//
// Theme.swift
// Colour.swift
// auto-clicker
//
// Created by Ben Tindall on 28/03/2022.
// Created by Ben Tindall on 11/07/2022.
//

import Foundation
import SwiftUI
import Defaults

// TODO: This should really be a struct with a type Theme.Colour to store the Enum side of things...

// Commented out colours are macOS 12 only
enum Theme: String, Identifiable, CaseIterable, Defaults.Serializable {
static let lightness: Double = 1.4
static let darkness: Double = 0.6

static let macOS12Colours: [Theme] = [.Brown, .Cyan, .Indigo, .Mint, .Teal]

enum Colour: ThemeColour, Identifiable, CaseIterable, Codable {
case Black,
Blue,
Brown,
Expand All @@ -34,13 +26,46 @@ enum Theme: String, Identifiable, CaseIterable, Defaults.Serializable {
White,
Yellow

var id: String {
self.rawValue
var id: ThemeColour {
self.normalised
}

// Even with the manual colour space calculated for the built in colours, I can't seem to get it to work
// So I'll just exclude them from the randomisation
var backgroundColour: ThemeColour {
var localised: LocalizedStringKey {
switch self {
case .Black:
return "colour_black"
case .Blue:
return "colour_blue"
case .Brown:
return "colour_brown"
case .Cyan:
return "colour_cyan"
case .Gray:
return "colour_gray"
case .Green:
return "colour_green"
case .Indigo:
return "colour_indigo"
case .Mint:
return "colour_mint"
case .Orange:
return "colour_orange"
case .Pink:
return "colour_pink"
case .Purple:
return "colour_purple"
case .Red:
return "colour_red"
case .Teal:
return "colour_teal"
case .White:
return "colour_white"
case .Yellow:
return "colour_yellow"
}
}

var normalised: ThemeColour {
switch self {
case .Black:
return .black
Expand Down Expand Up @@ -94,42 +119,4 @@ enum Theme: String, Identifiable, CaseIterable, Defaults.Serializable {
return .yellow
}
}

var fontColour: ThemeColour {
switch self {
case .Black,
.Blue,
.Brown,
.Cyan,
.Gray,
.Indigo,
.Mint,
.Orange,
.Pink,
.Purple,
.Red,
.Teal:
return .white
case .Green,
.White,
.Yellow:
return .black
}
}

func randomise() {
var newTheme: Theme

if #available(macOS 12.0, *) {
repeat {
newTheme = Theme.allCases.randomElement()!
} while newTheme == Defaults[.appearanceSelectedTheme]
} else {
repeat {
newTheme = Theme.allCases.randomElement()!
} while newTheme == Defaults[.appearanceSelectedTheme] || Theme.macOS12Colours.contains(newTheme)
}

Defaults[.appearanceSelectedTheme] = newTheme
}
}
16 changes: 11 additions & 5 deletions auto-clicker/Enums/Duration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import Foundation
import SwiftUI

enum Duration: String, CustomStringConvertible, CaseIterable, Identifiable, Codable {
case milliseconds = "Millisecond(s)"
case seconds = "Second(s)"
case minutes = "Minute(s)"
case hours = "Hour(s)"
case milliseconds = "duration_milliseconds"
case seconds = "duration_seconds"
case minutes = "duration_minutes"
case hours = "duration_hours"

var id: String {
self.rawValue
Expand All @@ -22,6 +22,10 @@ enum Duration: String, CustomStringConvertible, CaseIterable, Identifiable, Coda
self.rawValue
}

var localised: LocalizedStringKey {
LocalizedStringKey(self.description)
}

var textView: some View {
switch self {
case .milliseconds, .seconds, .minutes, .hours:
Expand All @@ -32,7 +36,9 @@ enum Duration: String, CustomStringConvertible, CaseIterable, Identifiable, Coda
func buttonView(action: @escaping () -> Void) -> some View {
switch self {
case .milliseconds, .seconds, .minutes, .hours:
return Button(self.description, action: action)
return Button(action: action) {
Text(self.localised, comment: "Duration option buttons")
}
}
}

Expand Down
11 changes: 11 additions & 0 deletions auto-clicker/Extensions/CGVector+DefaultsSerializable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// CGVector+DefaultsSerializable.swift
// auto-clicker
//
// Created by Ben Tindall on 11/07/2022.
//

import Foundation
import Defaults

extension CGVector: DefaultsSerializable {}
17 changes: 17 additions & 0 deletions auto-clicker/Extensions/Color+ExpressibleByStringLiteral.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// Color+ExpressibleByStringLiteral.swift
// auto-clicker
//
// Created by Ben Tindall on 11/07/2022.
//

// Required to use Color as an Enum type in Colour

import Foundation
import SwiftUI

extension Color: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self.init(value)
}
}
4 changes: 2 additions & 2 deletions auto-clicker/Extensions/ThemeColour.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ public typealias ThemeColour = Color

extension ThemeColour {
var lighter: ThemeColour {
self.changeBrightness(Theme.lightness)
self.changeBrightness(ThemeService.lightness)
}

var darker: ThemeColour {
self.changeBrightness(Theme.darkness)
self.changeBrightness(ThemeService.darkness)
}
}
9 changes: 4 additions & 5 deletions auto-clicker/Init/AutoClickerApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ struct AutoClickerApp: App {
var body: some Scene {
Settings {
SettingsView()
.background(self.activeTheme.backgroundColour)
.foregroundColor(self.activeTheme.fontColour)
.font(.system(size: 16))
}

Expand All @@ -35,14 +33,15 @@ struct AutoClickerApp: App {
PermissionsView()
}
}
.frame(minWidth: WindowStateService.width, minHeight: WindowStateService.height)
.frame(maxWidth: WindowStateService.width, maxHeight: WindowStateService.height)
.frame(minWidth: WindowStateService.width,
// maxWidth: WindowStateService.width,
minHeight: WindowStateService.height,
maxHeight: WindowStateService.height)
.onAppear(perform: self.permissionsService.pollAccessibilityPrivileges)
}
.windowStyle(.hiddenTitleBar)
.commands {
HelpCommands()
// OptionsCommands(keepWindowOnTop: self.$keepWindowOnTop)
}
}
}
62 changes: 62 additions & 0 deletions auto-clicker/Localisation/en-GB.lproj/Localizable.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"permissions_help_title" = "Permissions Required 🔐";
"permissions_help_first_paragraph" = "macOS requires that this app has accessibility permissions granted to it in order to press the specified keys or buttons.";
"permissions_help_second_paragraph" = "You should have already been prompted by macOS to grant these permissions but if not, here's a handy button to get you there:";
"permissions_help_open_sys_pref_btn" = "Open System Preferences";
"permissions_help_unlock_note" = "The app will automatically unlock within a few seconds of the permission being granted.";

"main_window_comma" = ",";
"main_window_full_stop" = ".";
"main_window_every" = "Every";
"main_window_press" = "press";
"main_window_time" = "time";
"main_window_times" = "times";
"main_window_repeat" = "repeat";
"main_window_wait" = "Wait";
"main_window_second" = "second";
"main_window_seconds" = "seconds";
"main_window_before_starting" = " before starting";
"main_window_start_btn" = "START";
"main_window_stop_btn" = "STOP";
"main_window_stat_box_next_press_at" = "NEXT PRESS AT";
"main_window_stat_box_final_press_at" = "FINAL PRESS AT";
"main_window_repo_vanity" = "with ♥️ by Othyn";

"duration_milliseconds" = "Millisecond(s)";
"duration_seconds" = "Second(s)";
"duration_minutes" = "Minutes(s)";
"duration_hours" = "Hours(s)";
"duration_modal_cancel_button" = "Cancel";

"key_listener_modal_press_prompt" = "Press your desired input...";
"key_listener_modal_dismiss_key_prompt" = "Hold the escape key when done.";
"key_listener_modal_dismiss_key_override" = "To use the escape key itself, press it twice.";

"settings_general" = "General";
"settings_keyboard_shortcuts" = "Keyboard Shortcuts";
"settings_window" = "Window";
"settings_appearance" = "Appearance";
"settings_keyboard_shortcuts_start" = "Start auto clicker";
"settings_keyboard_shortcuts_stop" = "Stop auto clicker";
"settings_window_stay_ontop" = "Window should stay ontop";

"help_commands_request_a_feature" = "Request a feature...";
"help_commands_report_a_bug" = "Report a problem...";

"colour_black" = "Black";
"colour_blue" = "Blue";
"colour_brown" = "Brown";
"colour_cyan" = "Cyan";
"colour_gray" = "Gray";
"colour_green" = "Green";
"colour_indigo" = "Indigo";
"colour_mint" = "Mint";
"colour_orange" = "Orange";
"colour_pink" = "Pink";
"colour_purple" = "Purple";
"colour_red" = "Red";
"colour_teal" = "Teal";
"colour_white" = "White";
"colour_yellow" = "Yellow";

// NumberField (Legacy)
"min: %lld, max: %lld" = "min: %lld, max: %lld";
30 changes: 30 additions & 0 deletions auto-clicker/Localisation/en-GB.lproj/Localizable.stringsdict
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>StringKey</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@VARIABLE@</string>
<key>VARIABLE</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string></string>
<key>zero</key>
<string></string>
<key>one</key>
<string></string>
<key>two</key>
<string></string>
<key>few</key>
<string></string>
<key>many</key>
<string></string>
<key>other</key>
<string></string>
</dict>
</dict>
</dict>
</plist>
Loading