feat: add Hungarian (Magyar) translation support

Add complete Hungarian localization with 91 translations contributed by
Balázs. This release also includes improvements to the translation
import tooling to better support international contributors.

Changes:
- Add Hungarian (hu) language with full translation coverage
- Add Balázs to translator credits
- Improve blanki18n.swift CSV parser:
  - Support semicolon-separated CSVs (common in European locales)
  - Add multi-encoding support (UTF-8, Mac Central European, MacRoman, Windows-1252, ISO Latin-2)
  - Intelligently detect delimiter and encoding
  - Fix encoding detection to properly handle Hungarian characters (ő, ű, á, etc.)
- Register Hungarian locale in Xcode project (knownRegions)
- Update documentation to reflect 11 supported languages
- Fix CodeQL workflow scheme name
- Bump version to 1.0.13

Thanks to Balázs for the excellent translation work!
This commit is contained in:
Cody Bromley
2025-10-28 00:23:37 -05:00
parent 749190d0a5
commit 9fc6518093
9 changed files with 593 additions and 14 deletions
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
fi
xcodebuild clean build \
-project Blankie.xcodeproj \
-scheme "Blankie (Universal)" \
-scheme "Blankie" \
-destination "platform=macOS,arch=x86_64" \
-configuration Debug \
-quiet \
+3 -2
View File
@@ -235,6 +235,7 @@
"en-GB",
es,
fr,
hu,
it,
ja,
ko,
@@ -509,7 +510,7 @@
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 14.6;
MARKETING_VERSION = 1.0.12;
MARKETING_VERSION = 1.0.13;
PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)";
"PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*]" = "$(PRODUCT_BUNDLE_IDENTIFIER)";
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -553,7 +554,7 @@
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 14.6;
MARKETING_VERSION = 1.0.12;
MARKETING_VERSION = 1.0.13;
PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)";
"PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*]" = "$(PRODUCT_BUNDLE_IDENTIFIER)";
PRODUCT_NAME = "$(TARGET_NAME)";
File diff suppressed because it is too large Load Diff
+3
View File
@@ -11,6 +11,9 @@
"Italiano": [
"davnr"
],
"Magyar": [
"Balázs"
],
"Português": [
"Júlio Coelho"
],
+6
View File
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [1.0.13] - 2025-10-27
### Added
- Hungarian (Magyar) translation support - thanks to **Balázs**
## [1.0.12] - 2025-06-01
### Added
+1
View File
@@ -66,6 +66,7 @@ Blankie is currently available in the following languages:
- Español (es)
- Français (fr)
- Italiano (it)
- Magyar (hu)
- 日本語 (ja)
- 한국어 (ko)
- Português (pt-PT)
+1 -1
View File
@@ -49,7 +49,7 @@ Blankie helps you focus, relax, and sleep better by creating perfect ambient sou
- **Save custom presets** for quick access to your favorite combinations
- **Native macOS app** with automatic light/dark mode support
- **System integration** with media keys and Control Center
- **7+ languages supported** with more coming soon
- **11 languages supported** with more coming soon
## Installation
+28 -6
View File
@@ -25,16 +25,30 @@ func parseCSV(data: String) -> [CSVRow] {
var rows: [CSVRow] = []
let lines = data.components(separatedBy: .newlines)
guard !lines.isEmpty else { return rows }
// Detect delimiter by counting occurrences in the header line
let header = lines[0]
let semicolonCount = header.components(separatedBy: ";").count - 1
let commaCount = header.components(separatedBy: ",").count - 1
let delimiter = semicolonCount > commaCount ? ";" : ","
// Skip header
for line in lines.dropFirst() where !line.isEmpty {
let columns = line.components(separatedBy: ",")
let columns = line.components(separatedBy: delimiter)
.map { $0.trimmingCharacters(in: .init(charactersIn: "\"")) }
if columns.count >= 4 {
// For semicolon CSVs, check both source (index 1) and target (index 2) columns
// Use whichever is non-empty, preferring target if both exist
let sourceValue = columns[1].trimmingCharacters(in: .whitespacesAndNewlines)
let targetValue = columns[2].trimmingCharacters(in: .whitespacesAndNewlines)
let translation = !targetValue.isEmpty ? targetValue : sourceValue
rows.append(
CSVRow(
key: columns[0],
target: columns[2],
target: translation,
state: columns[3]
))
}
@@ -100,11 +114,19 @@ func readTranslationFile(at path: String) -> [CSVRow] {
}
return parseJSON(data: fileData)
} else if path.hasSuffix(".csv") {
guard let fileData = try? String(contentsOfFile: path, encoding: .utf8) else {
print("Error: Could not read file at \(path)")
exit(1)
// Try multiple encodings (UTF-8, Mac Central European Roman, MacRoman, Windows-1252, ISO Latin-2)
// Mac Central European Roman is for Central/Eastern European languages like Hungarian
let encodings: [String.Encoding] = [.utf8, String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.macCentralEurRoman.rawValue))), .macOSRoman, .windowsCP1252, .isoLatin2]
for encoding in encodings {
if let fileData = try? String(contentsOfFile: path, encoding: encoding) {
print("Successfully read file using encoding: \(encoding)")
return parseCSV(data: fileData)
}
}
return parseCSV(data: fileData)
print("Error: Could not read file at \(path) with any supported encoding")
exit(1)
} else {
print("Error: Only CSV and JSON files are supported")
exit(1)
+4 -4
View File
@@ -23,11 +23,11 @@ const title = "Ambient sound mixer for macOS";
<BackgroundVideo videoSrc={wavesVideo} posterSrc={wavesPoster.src} />
<div class="relative z-10 max-w-4xl mx-auto px-5 text-center py-16 mt-8">
<p
class="text-base mt-6 flex items-center justify-center gap-2 bg-blue-900/10 rounded px-3 py-1 mx-auto w-fit border border-blue-300 border-opacity-20 font-normal tracking-wide"
class="text-base mt-6 flex items-center justify-center gap-2 bg-blue-900/10 rounded px-3 py-1 mx-auto w-fit border border-blue-300 border-opacity-20 font-normal tracking-wide max-w-[800px]"
aria-label="Multiple language support announcement"
>
🌍 Now available in Deutsch, Español, Français, Italiano, 日本語,
한국어, Português, Türkçe and 简体中文!
🌍&nbsp;Now available in Deutsch, Español, Français, Italiano, Magyar, 日本語,
한국어, Português, Türkçe&nbsp;and&nbsp;简体中文!
</p>
<h1
@@ -327,7 +327,7 @@ const title = "Ambient sound mixer for macOS";
</svg>
</h3>
<p class="text-mid-gray base">
Available in 7 languages, with ongoing community contributions.
Available in 11 languages, with ongoing community contributions.
</p>
<p
class="text-xs text-yellow-300 mt-2 opacity-0 group-hover:opacity-100 transition-opacity"