Skip to content

Commit 42a29b2

Browse files
authored
Merge pull request #1089 from kiwix/safe-translations-applied
Safe translations applied
2 parents d2cda4f + e39238a commit 42a29b2

File tree

97 files changed

+612
-459
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+612
-459
lines changed

.github/workflows/ci.yml

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ jobs:
3333
# /!\ important: this checks out code from the HEAD of the PR instead of the main branch (for pull_request_target)
3434
ref: ${{ github.event.pull_request.head.sha || github.ref }}
3535

36+
- name: Validate localizations
37+
run: python localizations.py validate
38+
3639
- name: Add Apple Store Key
3740
run: echo "${{ secrets.APPLE_STORE_AUTH_KEY }}" | base64 --decode -o ${{ env.APPLE_STORE_AUTH_KEY_PATH}}
3841

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,7 @@ Brewfile.lock.json
8181

8282
# this is CI specific
8383
Brewfile_CI
84-
Brewfile_CI.lock.json
84+
Brewfile_CI.lock.json
85+
86+
# Generated localization swift file:
87+
Support/LocalString.swift

.pre-commit-config.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ default_install_hook_types: [post-merge, post-checkout, post-rewrite]
33
repos:
44
- repo: local
55
hooks:
6+
- id: generate_localizations
7+
name: "Generate localization swift file"
8+
entry: python localizations.py generate
9+
language: python
10+
always_run: true
11+
stages: [post-checkout, post-merge, post-rewrite]
612
- id: xcodegen
713
name: Generate project files for Xcode
814
description: "Generate project file for Xcode"

.swiftlint.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
disabled_rules:
22
- trailing_whitespace
33
included:
4-
- Views/Settings/
4+
- Views/Settings/
5+
excluded:
6+
- Support/LocalString.swift

App/App_macOS.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ struct Kiwix: App {
5252
}.commands {
5353
SidebarCommands()
5454
CommandGroup(replacing: .importExport) {
55-
OpenFileButton(context: .command) { Text("app_macos_commands.open_file".localized) }
55+
OpenFileButton(context: .command) { Text(LocalString.app_macos_commands_open_file) }
5656
}
5757
CommandGroup(replacing: .newItem) {
58-
Button("app_macos_commands.new".localized) {
58+
Button(LocalString.app_macos_commands_new) {
5959
guard let currentWindow = NSApp.keyWindow,
6060
let controller = currentWindow.windowController else { return }
6161
controller.newWindowForTab(nil)
@@ -85,7 +85,7 @@ struct Kiwix: App {
8585
}
8686
.frame(width: 550, height: 400)
8787
}
88-
Window("payment.donate.title".localized, id: "donation") {
88+
Window(LocalString.payment_donate_title, id: "donation") {
8989
Group {
9090
if let selectedAmount {
9191
PaymentSummary(selectedAmount: selectedAmount, onComplete: {
@@ -187,7 +187,7 @@ struct RootView: View {
187187
Label(navigationItem.name, systemImage: navigationItem.icon)
188188
}
189189
if FeatureFlags.hasLibrary {
190-
Section("app_macos_navigation.button.library".localized) {
190+
Section(LocalString.app_macos_navigation_button_library) {
191191
ForEach(libraryItems, id: \.self) { navigationItem in
192192
Label(navigationItem.name, systemImage: navigationItem.icon)
193193
}

App/CompactViewController.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ final class CompactViewController: UIHostingController<AnyView>, UISearchControl
8484
searchController.delegate = self
8585
searchController.hidesNavigationBarDuringPresentation = false
8686
searchController.showsSearchResultsController = true
87-
searchController.searchBar.searchTextField.placeholder = "common.search".localized
87+
searchController.searchBar.searchTextField.placeholder = LocalString.common_search
8888

8989
searchTextObserver = searchViewModel.$searchText.sink { [weak self] searchText in
9090
guard self?.searchController.searchBar.text != searchText else { return }
@@ -107,7 +107,7 @@ final class CompactViewController: UIHostingController<AnyView>, UISearchControl
107107
trailingNavItemGroups = navigationItem.trailingItemGroups
108108
navigationItem.setRightBarButton(
109109
UIBarButtonItem(
110-
title: "common.button.cancel".localized,
110+
title: LocalString.common_button_cancel,
111111
style: .done,
112112
target: self,
113113
action: #selector(onSearchCancelled)
@@ -185,14 +185,14 @@ private struct CompactView: View {
185185
Button {
186186
presentedSheet = .library
187187
} label: {
188-
Label("common.tab.menu.library".localized, systemImage: "folder")
188+
Label(LocalString.common_tab_menu_library, systemImage: "folder")
189189
}
190190
Spacer()
191191
}
192192
Button {
193193
presentedSheet = .settings
194194
} label: {
195-
Label("common.tab.menu.settings".localized, systemImage: "gear")
195+
Label(LocalString.common_tab_menu_settings, systemImage: "gear")
196196
}
197197
Spacer()
198198
}
@@ -209,7 +209,7 @@ private struct CompactView: View {
209209
Button {
210210
self.presentedSheet = nil
211211
} label: {
212-
Text("common.button.done".localized).fontWeight(.semibold)
212+
Text(LocalString.common_button_done).fontWeight(.semibold)
213213
}
214214
}
215215
}
@@ -285,7 +285,7 @@ private struct Content<LaunchModel>: View where LaunchModel: LaunchProtocol {
285285
}
286286
.toolbar {
287287
ToolbarItemGroup(placement: .primaryAction) {
288-
Button("article_shortcut.random.button.title.ios".localized,
288+
Button(LocalString.article_shortcut_random_button_title_ios,
289289
systemImage: "die.face.5",
290290
action: { browser.loadRandomArticle() })
291291
.disabled(zimFiles.isEmpty)

App/SidebarViewController.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl
105105
},
106106
menu: UIMenu(children: [
107107
UIAction(
108-
title: "sidebar_view.navigation.button.close".localized,
108+
title: LocalString.sidebar_view_navigation_button_close,
109109
image: UIImage(systemName: "xmark.square"),
110110
attributes: .destructive
111111
) { [unowned self] _ in
@@ -114,7 +114,7 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl
114114
navigationViewModel.deleteTab(tabID: tabID)
115115
},
116116
UIAction(
117-
title: "sidebar_view.navigation.button.close_all".localized,
117+
title: LocalString.sidebar_view_navigation_button_close_all,
118118
image: UIImage(systemName: "xmark.square.fill"),
119119
attributes: .destructive
120120
) { [unowned self] _ in
@@ -196,7 +196,7 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl
196196
if case let .tab(objectID) = item,
197197
let tab = try? Database.shared.viewContext.existingObject(with: objectID) as? Tab {
198198
var config = cell.defaultContentConfiguration()
199-
config.text = tab.title ?? "common.tab.menu.new_tab".localized
199+
config.text = tab.title ?? LocalString.common_tab_menu_new_tab
200200
if let zimFile = tab.zimFile, let category = Category(rawValue: zimFile.category) {
201201
config.textProperties.numberOfLines = 1
202202
if let imgData = zimFile.faviconData {
@@ -224,11 +224,11 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl
224224
switch section {
225225
case .tabs:
226226
var config = UIListContentConfiguration.sidebarHeader()
227-
config.text = "common.tab.navigation.title".localized
227+
config.text = LocalString.common_tab_navigation_title
228228
headerView.contentConfiguration = config
229229
case .library:
230230
var config = UIListContentConfiguration.sidebarHeader()
231-
config.text = "common.tab.menu.library".localized
231+
config.text = LocalString.common_tab_menu_library
232232
headerView.contentConfiguration = config
233233
default:
234234
headerView.contentConfiguration = nil
@@ -239,7 +239,7 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl
239239
guard let navigationViewModel,
240240
let item = dataSource.itemIdentifier(for: indexPath),
241241
case let .tab(tabID) = item else { return nil }
242-
let title = "sidebar_view.navigation.button.close".localized
242+
let title = LocalString.sidebar_view_navigation_button_close
243243
let action = UIContextualAction(style: .destructive,
244244
title: title) { [weak navigationViewModel] _, _, _ in
245245
navigationViewModel?.deleteTab(tabID: tabID)

Brewfile

+1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ at_exit do
99
system "cp Support/CoreKiwix.modulemap CoreKiwix.xcframework/ios-arm64/Headers/module.modulemap"
1010
system "cp Support/CoreKiwix.modulemap CoreKiwix.xcframework/ios-arm64_x86_64-simulator/Headers/module.modulemap"
1111
system "cp Support/CoreKiwix.modulemap CoreKiwix.xcframework/macos-arm64_x86_64/Headers/module.modulemap"
12+
system "python localizations.py generate"
1213
system "xcodegen"
1314
end

Model/Brand.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ enum Brand {
4949
static let loadingLogoImage: String = "welcomeLogo"
5050
static var loadingLogoSize: CGSize = ImageInfo.sizeOf(imageName: loadingLogoImage)!
5151

52-
static let aboutText: String = Config.value(for: .aboutText) ?? "settings.about.description".localized
52+
static let aboutText: String = Config.value(for: .aboutText) ?? LocalString.settings_about_description
5353
static let aboutWebsite: String = Config.value(for: .aboutWebsite) ?? "https://www.kiwix.org"
5454
// currently only used under the Kiwix brand
5555
// if this is set to true in Support/Info.plist the support/donation button is hidden (for macOS FTP)

Model/DownloadService.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -254,10 +254,10 @@ final class DownloadService: NSObject, URLSessionDelegate, URLSessionTaskDelegat
254254
Database.shared.performBackgroundTask { context in
255255
// configure notification content
256256
let content = UNMutableNotificationContent()
257-
content.title = "download_service.complete.title".localized
257+
content.title = LocalString.download_service_complete_title
258258
content.sound = .default
259259
if let zimFile = try? context.fetch(ZimFile.fetchRequest(fileID: zimFileID)).first {
260-
content.body = "download_service.complete.description".localizedWithFormat(withArgs: zimFile.name)
260+
content.body = LocalString.download_service_complete_description(withArgs: zimFile.name)
261261
}
262262

263263
// schedule notification

Model/Entities/Errors.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ public enum LibraryRefreshError: LocalizedError {
2323
public var errorDescription: String? {
2424
switch self {
2525
case .retrieve(let description):
26-
let prefix = "library_refresh_error.retrieve.description".localizedWith(comment: "Library Refresh Error")
26+
let prefix = LocalString.library_refresh_error_retrieve_description
2727
return [prefix, description].compactMap({ $0 }).joined(separator: " ")
2828
case .parse:
29-
return "library_refresh_error.parse.description".localizedWith(comment: "Library Refresh Error")
29+
return LocalString.library_refresh_error_parse_description
3030
case .process:
31-
return "library_refresh_error.process.description".localizedWith(comment: "Library Refresh Error")
31+
return LocalString.library_refresh_error_process_description
3232
}
3333
}
3434
}

Model/Payment.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@ struct Payment {
146146
request.supportedNetworks = Self.supportedNetworks
147147
request.requiredBillingContactFields = [.emailAddress]
148148
let recurring: PKRecurringPaymentRequest? = if selectedAmount.isMonthly {
149-
PKRecurringPaymentRequest(paymentDescription: "payment.description.label".localized,
150-
regularBilling: .init(label: "payment.monthly_support.label".localized,
149+
PKRecurringPaymentRequest(paymentDescription: LocalString.payment_description_label,
150+
regularBilling: .init(label: LocalString.payment_monthly_support_label,
151151
amount: NSDecimalNumber(value: selectedAmount.value),
152152
type: .final),
153153
managementURL: URL(string: Self.paymentSubscriptionManagingURL)!)
@@ -157,7 +157,7 @@ struct Payment {
157157
request.recurringPaymentRequest = recurring
158158
request.paymentSummaryItems = [
159159
PKPaymentSummaryItem(
160-
label: "payment.summary.title".localized,
160+
label: LocalString.payment_summary_title,
161161
amount: NSDecimalNumber(value: selectedAmount.value),
162162
type: .final
163163
)

Model/Utilities/String+Extension.swift

-40
Original file line numberDiff line numberDiff line change
@@ -17,46 +17,6 @@ import Foundation
1717

1818
extension String {
1919

20-
var localized: String {
21-
localizedWithFallback()
22-
}
23-
24-
func localizedWith(comment: String) -> String {
25-
localizedWithFallback(comment: comment)
26-
}
27-
28-
func localizedWithFormat(withArgs: CVarArg...) -> String {
29-
let format = localizedWithFallback()
30-
switch withArgs.count {
31-
case 1: return String.localizedStringWithFormat(format, withArgs[0])
32-
case 2: return String.localizedStringWithFormat(format, withArgs[0], withArgs[1])
33-
default: return String.localizedStringWithFormat(format, withArgs)
34-
}
35-
}
36-
37-
private func localizedWithFallback(
38-
bundle: Bundle = Bundle.main,
39-
comment: String = ""
40-
) -> String {
41-
let englishValue: String
42-
if let path = Bundle.main.path(forResource: "en", ofType: "lproj"),
43-
let bundle = Bundle(path: path) {
44-
englishValue = NSLocalizedString(self, bundle: bundle, comment: comment)
45-
if NSLocale.preferredLanguages.first == "en" {
46-
return englishValue
47-
}
48-
} else {
49-
englishValue = ""
50-
}
51-
return NSLocalizedString(
52-
self,
53-
tableName: nil,
54-
bundle: bundle,
55-
value: englishValue, // fall back to this, if translation not found
56-
comment: comment
57-
)
58-
}
59-
6020
func removingPrefix(_ value: String) -> String {
6121
guard hasPrefix(value) else { return self }
6222
return String(dropFirst(value.count))
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// This file is part of Kiwix for iOS & macOS.
2+
//
3+
// Kiwix is free software; you can redistribute it and/or modify it
4+
// under the terms of the GNU General Public License as published by
5+
// the Free Software Foundation; either version 3 of the License, or
6+
// any later version.
7+
//
8+
// Kiwix is distributed in the hope that it will be useful, but
9+
// WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+
// General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU General Public License
14+
// along with Kiwix; If not, see https://www.gnu.org/licenses/.
15+
16+
import Foundation
17+
18+
fileprivate extension String {
19+
20+
var localized: String {
21+
localizedWithFallback()
22+
}
23+
24+
func localizedWithFormat(withArgs: CVarArg...) -> String {
25+
let format = localizedWithFallback()
26+
switch withArgs.count {
27+
case 1: return String.localizedStringWithFormat(format, withArgs[0])
28+
case 2: return String.localizedStringWithFormat(format, withArgs[0], withArgs[1])
29+
default: return String.localizedStringWithFormat(format, withArgs)
30+
}
31+
}
32+
33+
private func localizedWithFallback(
34+
bundle: Bundle = Bundle.main,
35+
comment: String = ""
36+
) -> String {
37+
let englishValue: String
38+
if let path = Bundle.main.path(forResource: "en", ofType: "lproj"),
39+
let bundle = Bundle(path: path) {
40+
englishValue = NSLocalizedString(self, bundle: bundle, comment: comment)
41+
if NSLocale.preferredLanguages.first == "en" {
42+
return englishValue
43+
}
44+
} else {
45+
englishValue = ""
46+
}
47+
return NSLocalizedString(
48+
self,
49+
tableName: nil,
50+
bundle: bundle,
51+
value: englishValue, // fall back to this, if translation not found
52+
comment: comment
53+
)
54+
}
55+
}
56+

Support/br.lproj/Localizable.strings

+2-4
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
"common.tab.list.close" = "Serriñ ar steudenn";
3030
"common.tab.menu.library" = "Levraoueg";
3131
"common.tab.menu.settings" = "Arventennoù";
32-
"common.support.app_name" = "Skoazellañ %@";
3332
"common.export_file.alert.title" = "Ezporzhiañ ar restr";
3433
"common.export_file.alert.description" = "Fellout a ra deoc'h ezporzhiañ ar restr %@?";
3534
"common.export_file.alert.button.title" = "Ezporzhiañ";
@@ -51,7 +50,7 @@
5150
"flavor_tag.help.no_pic" = "lamet eo bet darn vrasañ ar skeudennoù";
5251
"library_refresh_time.last" = "Nevez zo";
5352
"library_refresh_time.never" = "James";
54-
"zim_file_cell_article_count_suffix" = "pennadoù";
53+
"zim_file_cell.article_count.suffix" = "pennadoù";
5554
"zim_file_missing_indicator.help" = "Ar restr ZIM a vank.";
5655
"library.zim_file_context.main_page.label" = "Degemer";
5756
"library.zim_file_context.random.label" = "Pajenn dre zegouezh";
@@ -65,7 +64,7 @@
6564
"zim_file_category.section.empty.message" = "Restr ZIM ebet er rummad-mañ.";
6665
"zim_file_downloads.toolbar.show_sidebar.label" = "Diskouez ar varrenn-gostez";
6766
"zim_file_new_overlay.empty" = "Restr ZIM nevez ebet";
68-
"zim_file_new_button_refresh" = "Freskaat";
67+
"zim_file_new.button.refresh" = "Freskaat";
6968
"zim_file.list.name.text" = "Anv";
7069
"zim_file.list.description.text" = "Deskrivadur";
7170
"zim_file.list.actions.text" = "Oberoù";
@@ -144,7 +143,6 @@
144143
"app_macos_commands.open_file" = "Digeriñ...";
145144
"app_macos_commands.new" = "Steudenn nevez";
146145
"app_macos_navigation.button.library" = "Levraoueg";
147-
"app_macos_navigation.show_sidebar" = "Diskouez ar varrenn-gostez";
148146
"sidebar_view.navigation.button.close" = "Serriñ";
149147
"sidebar_view.navigation.button.close_all" = "Serriñ an holl steudennoù";
150148
"enum.category.wikipedia" = "Wikipedia";

Support/dag.lproj/Localizable.strings

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"flavor_tag.help.mini" = "Sabbu ŋɔ bela koŋko nyɛ din be ni, di yi pa shɛli tuuli yaɣili";
5555
"library_refresh_time.last" = "Kulla saha ŋɔ";
5656
"library_refresh_time.never" = "Abada";
57-
"zim_file_cell_article_count_suffix" = "Lahabaya";
57+
"zim_file_cell.article_count.suffix" = "Lahabaya";
5858
"zim_file_missing_indicator.help" = "Zim fasara nyɛla din kani.";
5959
// Fuzzy
6060
"library.zim_file_details.side_panel.message" = "Piimi zim fasara n- nya bayana";
@@ -71,7 +71,7 @@
7171
"zim_file_downloads.overlay.empty.message" = "Deebu tuma nima kani";
7272
"zim_file_downloads.toolbar.show_sidebar.label" = "Wuhimi \"Sidebar\"";
7373
"zim_file_new_overlay.empty" = "Zim fasara palli kani";
74-
"zim_file_new_button_refresh" = "Kahigi";
74+
"zim_file_new.button.refresh" = "Kahigi";
7575
"zim_file.list.name.text" = "Yuli";
7676
"zim_file.list.description.text" = "Buɣisibu";
7777
"zim_file.list.actions.text" = "Niŋsim";

0 commit comments

Comments
 (0)