diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b620e82a29..8599e96c62 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,6 +50,9 @@ You have two options for adding a new localization to CotEditor.app. Choose one - CotEditor/Resources/Localizables/ - Packages/EditorCore/Sources/CharacterInfo/Resources/ - Packages/EditorCore/Sources/FileEncoding/Resources/ + - Packages/EditorCore/Sources/LineEnding/Resources/ + - Packages/EditorCore/Sources/StringUtils/Resources/ + - Packages/EditorCore/Sources/Syntax/Resources/ - Note that you don't need to localize the UnicodeBlock.strings file in Packages/Libraries/Sources/CharacterInfo/. It will be done by @1024jp based on Apple's localization data. - Option 2: Communicate with the maintainer personally and work with a provided localization template (.xcloc file): - Send a message to the maintainer (@1024jp) either by creating a new issue on GitHub or by e-mail to ask for the localization template (.xcloc file) for your language. When you receive the .xcloc file, open it in Xcode and fill each cell of your language column in the tables. When finished, send the template file back to the maintainer. diff --git a/CotEditor/Resources/Localizables/Application/Localizable.xcstrings b/CotEditor/Resources/Localizables/Application/Localizable.xcstrings index 49e8952649..e0808063a8 100644 --- a/CotEditor/Resources/Localizables/Application/Localizable.xcstrings +++ b/CotEditor/Resources/Localizables/Application/Localizable.xcstrings @@ -5910,6 +5910,481 @@ } } }, + "Shortcut.CustomizationError.alreadyTaken.description" : { + "extractionState" : "extracted_with_value", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "„%@“ je již obsazeno příkazem „%@“." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "„%@“ wird bereits vom „%@“-Befehl verwendet." + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "“%1$@” is already taken by the “%2$@” command." + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "“%@” is already taken by the “%@” command." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "“%@” ya está tomado por el comando “%@”." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "« %@ » est déjà associé à la commande « %@ »." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Il tasto “%@” è giù utilizzato." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "“%@”はすでに“%@”コマンドで使用されています。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "‘%1$@’는 이미 ‘%2$@’ 명령에 의해 사용되고 있습니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "'%1$@' is al bezet door het commando '%2$@'." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "„%1$@” jest już zajęte przez polecenie „%2$@”." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "“%@” já está sendo usado pelo comando “%@”." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "“%@”, halihazırda “%@” komutu tarafından alınmış." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "“%@”已被“%@”命令使用。" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "「%@」已被「%@」命令使用。" + } + } + } + }, + "Shortcut.CustomizationError.shiftOnlyModifier.description" : { + "extractionState" : "extracted_with_value", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klávesu Shift lze použít pouze s jinou modifikační klávesou." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Shift-Taste kann nur mit einer anderen Modifikatortaste verwendet werden." + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "The Shift key can be used only with another modifier key." + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "The Shift key can be used only with another modifier key." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La tecla Mayús solo se puede usar con otra tecla modificadora." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "La touche Maj ne peut être utilisée que avec une autre touche de modification." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Il tasto Maiuscolo va abbinato ad un altro tasto modificatore." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Shiftキーはほかの修飾キーと合わせて使う必要があります。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Shift 키는 다른 조합 키와 함께 사용되어야 합니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De Shift-toets kan alleen worden gebruikt in combinatie met een andere speciale toets." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klawisz Shift może być użyty wyłącznie z innym klawiszem modyfikującym." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "A tecla Shift só pode ser usada em conjunto com outra tecla de modificação." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Shift düğmesi yalnızca başka bir değiştirici düğme ile kullanılabilir." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Shift键只能配合其他修饰键使用。" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "Shift鍵只能配合其他修飾鍵使用。" + } + } + } + }, + "Shortcut.CustomizationError.singleType.description" : { + "extractionState" : "extracted_with_value", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jednotlivou klávesu nelze pro zkratku použít." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einzelne Tasten sind für einen Kurzbefehl ungültig." + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Single type is invalid for a shortcut." + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Single type is invalid for a shortcut." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El tipo único no es válido para un atajo de teclado." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Un raccourci clavier ne peut pas être composé que d’une seule touche." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Un solo tasto non è una scorciatoia valida." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "単体のキーはショートカットに登録できません。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "단일 키는 단축키로 사용될 수 없습니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enkel type is ongeldig voor een snelkoppeling." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pojedynczy klawisz jest nieprawidłowy jako skrót." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não é possível usar uma única tecla como atalho." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bir kısayol için tekli tür geçersizdir." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "单独的按键类型不能被用作快捷键。" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "單獨的按鍵類型不能被用作快捷鍵。" + } + } + } + }, + "Shortcut.CustomizationError.unsupported.description" : { + "extractionState" : "extracted_with_value", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kombinace „%@“ není pro přizpůsobení zkratky podporována." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Kombination „%@“ wird für die Anpassung des Kurzbefehles nicht unterstützt." + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "The combination “%@” is not supported for the shortcut customization." + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "The combination “%@” is not supported for the shortcut customisation." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La combinación “%@” no es compatible con la personalización del atajo de teclado." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "La combinaison « %@ » n’est pas supportée comme combinaison de raccourci clavier." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "La combinazione “%@” non è supportata per la personalizzazione del key binding." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "“%@”の組み合わせはショートカットのカスタマイズに対応していません。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "‘%@’ 조합은 사용자화 단축키로 지원되지 않습니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De combinatie '%@' wordt niet ondersteund voor het aanpassen van de snelkoppeling." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kombinacja „%@” nie jest obsługiwana w przypadku dostosowywania skrótów." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "A combinação “%@” não é suportada pela customização de combinações de teclas." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "“%@” kombinasyonu, düğme bağıntısı özelleştirmesi için desteklenmiyor." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "组合键“%@”无法被用作自定义绑定键。" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "組合鍵「%@」無法用作自訂綁定熱鍵。" + } + } + } + }, + "SyntaxName.none" : { + "extractionState" : "extracted_with_value", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žádný" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ohne" + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "None" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "None" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nada" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucun" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "なし" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "없음" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nenhum" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hiçbiri" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "無" + } + } + } + }, "ThemeImportAlert.button.install" : { "comment" : "button label", "extractionState" : "extracted_with_value", diff --git a/CotEditor/Resources/Localizables/Application/Intents.xcstrings b/CotEditor/Resources/Localizables/Models/Intents.xcstrings similarity index 100% rename from CotEditor/Resources/Localizables/Application/Intents.xcstrings rename to CotEditor/Resources/Localizables/Models/Intents.xcstrings diff --git a/CotEditor/Resources/Localizables/Models/Shortcut.xcstrings b/CotEditor/Resources/Localizables/Models/Shortcut.xcstrings deleted file mode 100644 index 962180e858..0000000000 --- a/CotEditor/Resources/Localizables/Models/Shortcut.xcstrings +++ /dev/null @@ -1,386 +0,0 @@ -{ - "sourceLanguage" : "en", - "strings" : { - "Shortcut.CustomizationError.alreadyTaken.description" : { - "extractionState" : "extracted_with_value", - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "„%@“ je již obsazeno příkazem „%@“." - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "„%@“ wird bereits vom „%@“-Befehl verwendet." - } - }, - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "“%1$@” is already taken by the “%2$@” command." - } - }, - "en-GB" : { - "stringUnit" : { - "state" : "translated", - "value" : "“%@” is already taken by the “%@” command." - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "“%@” ya está tomado por el comando “%@”." - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "« %@ » est déjà associé à la commande « %@ »." - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Il tasto “%@” è giù utilizzato." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "“%@”はすでに“%@”コマンドで使用されています。" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "‘%1$@’는 이미 ‘%2$@’ 명령에 의해 사용되고 있습니다." - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "'%1$@' is al bezet door het commando '%2$@'." - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "„%1$@” jest już zajęte przez polecenie „%2$@”." - } - }, - "pt" : { - "stringUnit" : { - "state" : "translated", - "value" : "“%@” já está sendo usado pelo comando “%@”." - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "“%@”, halihazırda “%@” komutu tarafından alınmış." - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "“%@”已被“%@”命令使用。" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "「%@」已被「%@」命令使用。" - } - } - } - }, - "Shortcut.CustomizationError.shiftOnlyModifier.description" : { - "extractionState" : "extracted_with_value", - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Klávesu Shift lze použít pouze s jinou modifikační klávesou." - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Die Shift-Taste kann nur mit einer anderen Modifikatortaste verwendet werden." - } - }, - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "The Shift key can be used only with another modifier key." - } - }, - "en-GB" : { - "stringUnit" : { - "state" : "translated", - "value" : "The Shift key can be used only with another modifier key." - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La tecla Mayús solo se puede usar con otra tecla modificadora." - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "La touche Maj ne peut être utilisée que avec une autre touche de modification." - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Il tasto Maiuscolo va abbinato ad un altro tasto modificatore." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "Shiftキーはほかの修飾キーと合わせて使う必要があります。" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "Shift 키는 다른 조합 키와 함께 사용되어야 합니다." - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "De Shift-toets kan alleen worden gebruikt in combinatie met een andere speciale toets." - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Klawisz Shift może być użyty wyłącznie z innym klawiszem modyfikującym." - } - }, - "pt" : { - "stringUnit" : { - "state" : "translated", - "value" : "A tecla Shift só pode ser usada em conjunto com outra tecla de modificação." - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Shift düğmesi yalnızca başka bir değiştirici düğme ile kullanılabilir." - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "Shift键只能配合其他修饰键使用。" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "Shift鍵只能配合其他修飾鍵使用。" - } - } - } - }, - "Shortcut.CustomizationError.singleType.description" : { - "extractionState" : "extracted_with_value", - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Jednotlivou klávesu nelze pro zkratku použít." - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Einzelne Tasten sind für einen Kurzbefehl ungültig." - } - }, - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Single type is invalid for a shortcut." - } - }, - "en-GB" : { - "stringUnit" : { - "state" : "translated", - "value" : "Single type is invalid for a shortcut." - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El tipo único no es válido para un atajo de teclado." - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Un raccourci clavier ne peut pas être composé que d’une seule touche." - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Un solo tasto non è una scorciatoia valida." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "単体のキーはショートカットに登録できません。" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "단일 키는 단축키로 사용될 수 없습니다." - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enkel type is ongeldig voor een snelkoppeling." - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pojedynczy klawisz jest nieprawidłowy jako skrót." - } - }, - "pt" : { - "stringUnit" : { - "state" : "translated", - "value" : "Não é possível usar uma única tecla como atalho." - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bir kısayol için tekli tür geçersizdir." - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "单独的按键类型不能被用作快捷键。" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "單獨的按鍵類型不能被用作快捷鍵。" - } - } - } - }, - "Shortcut.CustomizationError.unsupported.description" : { - "extractionState" : "extracted_with_value", - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Kombinace „%@“ není pro přizpůsobení zkratky podporována." - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Die Kombination „%@“ wird für die Anpassung des Kurzbefehles nicht unterstützt." - } - }, - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "The combination “%@” is not supported for the shortcut customization." - } - }, - "en-GB" : { - "stringUnit" : { - "state" : "translated", - "value" : "The combination “%@” is not supported for the shortcut customisation." - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La combinación “%@” no es compatible con la personalización del atajo de teclado." - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "La combinaison « %@ » n’est pas supportée comme combinaison de raccourci clavier." - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "La combinazione “%@” non è supportata per la personalizzazione del key binding." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "“%@”の組み合わせはショートカットのカスタマイズに対応していません。" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "‘%@’ 조합은 사용자화 단축키로 지원되지 않습니다." - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "De combinatie '%@' wordt niet ondersteund voor het aanpassen van de snelkoppeling." - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Kombinacja „%@” nie jest obsługiwana w przypadku dostosowywania skrótów." - } - }, - "pt" : { - "stringUnit" : { - "state" : "translated", - "value" : "A combinação “%@” não é suportada pela customização de combinações de teclas." - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "“%@” kombinasyonu, düğme bağıntısı özelleştirmesi için desteklenmiyor." - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "组合键“%@”无法被用作自定义绑定键。" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "組合鍵「%@」無法用作自訂綁定熱鍵。" - } - } - } - } - }, - "version" : "1.0" -} \ No newline at end of file diff --git a/CotEditor/Sources/Application/AppDelegate.swift b/CotEditor/Sources/Application/AppDelegate.swift index 3c9a501c88..a82b37464e 100644 --- a/CotEditor/Sources/Application/AppDelegate.swift +++ b/CotEditor/Sources/Application/AppDelegate.swift @@ -176,7 +176,7 @@ private enum BundleIdentifier { guard let menu = self?.syntaxesMenu else { return } let recolorItem = menu.items.first { $0.action == #selector((any SyntaxChanging).recolorAll) } - let noneItem = NSMenuItem(title: String(localized: "SyntaxName.none", defaultValue: "None", table: "Syntax"), action: #selector((any SyntaxChanging).changeSyntax), keyEquivalent: "") + let noneItem = NSMenuItem(title: String(localized: "SyntaxName.none", defaultValue: "None"), action: #selector((any SyntaxChanging).changeSyntax), keyEquivalent: "") noneItem.representedObject = SyntaxName.none menu.removeAllItems() @@ -573,6 +573,16 @@ private extension Optional { } +private extension UnicodeNormalizationForm { + + /// Unique identifier for menu item. + var tag: Int { + + Self.allCases.firstIndex(of: self)! + } +} + + private extension UserDefaults { /// Migrates the user font setting to new format introduced on CotEditor 4.6.0 (2023-09). diff --git a/CotEditor/Sources/Document Window/Accessory Views/PatternSortView.swift b/CotEditor/Sources/Document Window/Accessory Views/PatternSortView.swift index 32b52473e4..dab4413868 100644 --- a/CotEditor/Sources/Document Window/Accessory Views/PatternSortView.swift +++ b/CotEditor/Sources/Document Window/Accessory Views/PatternSortView.swift @@ -345,6 +345,24 @@ private extension PatternSortView.SortKey { } +extension SortPatternError: @retroactive LocalizedError { + + public var errorDescription: String? { + + switch self { + case .emptyPattern: + String(localized: "Empty pattern", + table: "PatternSort", + comment: "error message (“pattern” is a regular expression pattern)") + case .invalidRegularExpressionPattern: + String(localized: "Invalid pattern", + table: "PatternSort", + comment: "error message (“pattern” is a regular expression pattern)") + } + } +} + + // MARK: - Preview diff --git a/CotEditor/Sources/Document Window/DocumentWindowController.swift b/CotEditor/Sources/Document Window/DocumentWindowController.swift index 3489317bd4..3f2faeacc8 100644 --- a/CotEditor/Sources/Document Window/DocumentWindowController.swift +++ b/CotEditor/Sources/Document Window/DocumentWindowController.swift @@ -305,7 +305,7 @@ final class DocumentWindowController: NSWindowController, NSWindowDelegate { menu.removeAllItems() - let noneItem = NSMenuItem(title: String(localized: "SyntaxName.none", defaultValue: "None", table: "Syntax"), action: #selector((any SyntaxChanging).changeSyntax), keyEquivalent: "") + let noneItem = NSMenuItem(title: String(localized: "SyntaxName.none", defaultValue: "None"), action: #selector((any SyntaxChanging).changeSyntax), keyEquivalent: "") noneItem.representedObject = SyntaxName.none menu.addItem(noneItem) diff --git a/CotEditor/Sources/Models/Key Binding/Shortcut+Error.swift b/CotEditor/Sources/Models/Key Binding/Shortcut+Error.swift index 856c6a3cdf..80dc86f754 100644 --- a/CotEditor/Sources/Models/Key Binding/Shortcut+Error.swift +++ b/CotEditor/Sources/Models/Key Binding/Shortcut+Error.swift @@ -42,23 +42,19 @@ extension Shortcut { switch self { case .singleType: String(localized: "Shortcut.CustomizationError.singleType.description", - defaultValue: "Single type is invalid for a shortcut.", - table: "Shortcut") + defaultValue: "Single type is invalid for a shortcut.") case .alreadyTaken(let shortcut, let name): String(localized: "Shortcut.CustomizationError.alreadyTaken.description", - defaultValue: "“\(shortcut.symbol)” is already taken by the “\(name)” command.", - table: "Shortcut") + defaultValue: "“\(shortcut.symbol)” is already taken by the “\(name)” command.") case .shiftOnlyModifier: String(localized: "Shortcut.CustomizationError.shiftOnlyModifier.description", - defaultValue: "The Shift key can be used only with another modifier key.", - table: "Shortcut") + defaultValue: "The Shift key can be used only with another modifier key.") case .unsupported(let shortcut): String(localized: "Shortcut.CustomizationError.unsupported.description", - defaultValue: "The combination “\(shortcut.symbol)” is not supported for the shortcut customization.", - table: "Shortcut") + defaultValue: "The combination “\(shortcut.symbol)” is not supported for the shortcut customization.") } } } diff --git a/CotEditor/Sources/Settings Window/Panes/FormatSettingsView.swift b/CotEditor/Sources/Settings Window/Panes/FormatSettingsView.swift index 2c66abc957..567d41b3c9 100644 --- a/CotEditor/Sources/Settings Window/Panes/FormatSettingsView.swift +++ b/CotEditor/Sources/Settings Window/Panes/FormatSettingsView.swift @@ -131,7 +131,7 @@ struct FormatSettingsView: View { .gridColumnAlignment(.trailing) Picker(selection: $syntax) { - Text(String(localized: "SyntaxName.none", defaultValue: "None", table: "Syntax")) + Text(String(localized: "SyntaxName.none", defaultValue: "None")) .tag(SyntaxName.none) Divider() diff --git a/CotEditor/Sources/Utilities/Libraries/SortPatternError+Localization.swift b/CotEditor/Sources/Utilities/Libraries/SortPatternError+Localization.swift deleted file mode 100644 index 1278008c18..0000000000 --- a/CotEditor/Sources/Utilities/Libraries/SortPatternError+Localization.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// SortPatternError+Localization.swift -// -// CotEditor -// https://coteditor.com -// -// Created by 1024jp on 2018-01-05. -// -// --------------------------------------------------------------------------- -// -// © 2018-2024 1024jp -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import LineSort - -extension SortPatternError: @retroactive LocalizedError { - - public var errorDescription: String? { - - switch self { - case .emptyPattern: - String(localized: "Empty pattern", - table: "PatternSort", - comment: "error message (“pattern” is a regular expression pattern)") - case .invalidRegularExpressionPattern: - String(localized: "Invalid pattern", - table: "PatternSort", - comment: "error message (“pattern” is a regular expression pattern)") - } - } -} diff --git a/Packages/EditorCore/Package.swift b/Packages/EditorCore/Package.swift index 4bb34d441d..c3e22bf6c8 100644 --- a/Packages/EditorCore/Package.swift +++ b/Packages/EditorCore/Package.swift @@ -73,7 +73,7 @@ let package = Package( .target(name: "Invisible"), - .target(name: "LineEnding", dependencies: ["ValueRange"]), + .target(name: "LineEnding", dependencies: ["ValueRange"], resources: [.process("Resources")]), .testTarget(name: "LineEndingTests", dependencies: ["LineEnding", "StringUtils"]), .target(name: "LineSort", dependencies: ["StringUtils"]), @@ -82,10 +82,10 @@ let package = Package( .target(name: "SemanticVersioning"), .testTarget(name: "SemanticVersioningTests", dependencies: ["SemanticVersioning"]), - .target(name: "StringUtils"), + .target(name: "StringUtils", resources: [.process("Resources")]), .testTarget(name: "StringUtilsTests", dependencies: ["StringUtils"]), - .target(name: "Syntax", dependencies: ["StringUtils", "ValueRange"]), + .target(name: "Syntax", dependencies: ["StringUtils", "ValueRange"], resources: [.process("Resources")]), .testTarget(name: "SyntaxTests", dependencies: ["Syntax"]), .target(name: "TextClipping"), diff --git a/CotEditor/Sources/Utilities/Libraries/LineEnding+Localization.swift b/Packages/EditorCore/Sources/LineEnding/LineEnding+Localization.swift similarity index 85% rename from CotEditor/Sources/Utilities/Libraries/LineEnding+Localization.swift rename to Packages/EditorCore/Sources/LineEnding/LineEnding+Localization.swift index 864c3cd891..212c0b88b5 100644 --- a/CotEditor/Sources/Utilities/Libraries/LineEnding+Localization.swift +++ b/Packages/EditorCore/Sources/LineEnding/LineEnding+Localization.swift @@ -1,5 +1,6 @@ // // LineEnding+Localization.swift +// LineEnding // // CotEditor // https://coteditor.com @@ -23,9 +24,7 @@ // limitations under the License. // -import LineEnding - -extension LineEnding { +public extension LineEnding { var description: String { @@ -33,28 +32,29 @@ extension LineEnding { case .lf: String(localized: "LineEnding.lf.description", defaultValue: "macOS / Unix", - table: "LineEnding") + bundle: .module) case .cr: String(localized: "LineEnding.cr.description", defaultValue: "Classic Mac OS", - table: "LineEnding") + bundle: .module) case .crlf: String(localized: "LineEnding.crlf.description", - defaultValue: "Windows", table: "LineEnding") + defaultValue: "Windows", + bundle: .module) case .nel: String(localized: "LineEnding.nel.description", defaultValue: "Unix Next Line", - table: "LineEnding", + bundle: .module, comment: "Since this is a technical term, it should be left as-is.") case .lineSeparator: String(localized: "LineEnding.lineSeparator.description", defaultValue: "Unix Line Separator", - table: "LineEnding", + bundle: .module, comment: "Since this is a technical term, it should be left as-is.") case .paragraphSeparator: String(localized: "LineEnding.paragraphSeparator.description", defaultValue: "Unix Paragraph Separator", - table: "LineEnding", + bundle: .module, comment: "Since this is a technical term, it should be left as-is.") } } diff --git a/CotEditor/Resources/Localizables/Models/LineEnding.xcstrings b/Packages/EditorCore/Sources/LineEnding/Resources/Localizable.xcstrings similarity index 100% rename from CotEditor/Resources/Localizables/Models/LineEnding.xcstrings rename to Packages/EditorCore/Sources/LineEnding/Resources/Localizable.xcstrings diff --git a/CotEditor/Resources/Localizables/Models/UnicodeNormalization.xcstrings b/Packages/EditorCore/Sources/StringUtils/Resources/Localizable.xcstrings similarity index 100% rename from CotEditor/Resources/Localizables/Models/UnicodeNormalization.xcstrings rename to Packages/EditorCore/Sources/StringUtils/Resources/Localizable.xcstrings diff --git a/CotEditor/Sources/Utilities/Libraries/UnicodeNormalizationForm.swift b/Packages/EditorCore/Sources/StringUtils/UnicodeNormalizationForm+Localizable.swift similarity index 79% rename from CotEditor/Sources/Utilities/Libraries/UnicodeNormalizationForm.swift rename to Packages/EditorCore/Sources/StringUtils/UnicodeNormalizationForm+Localizable.swift index 78c5c18cad..45285d13d6 100644 --- a/CotEditor/Sources/Utilities/Libraries/UnicodeNormalizationForm.swift +++ b/Packages/EditorCore/Sources/StringUtils/UnicodeNormalizationForm+Localizable.swift @@ -1,5 +1,6 @@ // -// UnicodeNormalizationForm.swift +// UnicodeNormalizationForm+Localizable.swift +// StringUtils // // CotEditor // https://coteditor.com @@ -23,9 +24,7 @@ // limitations under the License. // -import StringUtils - -extension UnicodeNormalizationForm { +public extension UnicodeNormalizationForm { /// The localized name. var localizedName: String { @@ -34,31 +33,31 @@ extension UnicodeNormalizationForm { case .nfd: String(localized: "UnicodeNormalization.nfd.label", defaultValue: "NFD", - table: "UnicodeNormalization") + bundle: .module) case .nfc: String(localized: "UnicodeNormalization.nfc.label", defaultValue: "NFC", - table: "UnicodeNormalization") + bundle: .module) case .nfkd: String(localized: "UnicodeNormalization.nfkd.label", defaultValue: "NFKD", - table: "UnicodeNormalization") + bundle: .module) case .nfkc: String(localized: "UnicodeNormalization.nfkc.label", defaultValue: "NFKC", - table: "UnicodeNormalization") + bundle: .module) case .nfkcCasefold: String(localized: "UnicodeNormalization.nfkcCasefold.label", defaultValue: "NFKC Casefold", - table: "UnicodeNormalization") + bundle: .module) case .modifiedNFD: String(localized: "UnicodeNormalization.modifiedNFD.label", defaultValue: "Modified NFD", - table: "UnicodeNormalization") + bundle: .module) case .modifiedNFC: String(localized: "UnicodeNormalization.modifiedNFC.label", defaultValue: "Modified NFC", - table: "UnicodeNormalization") + bundle: .module) } } @@ -70,45 +69,38 @@ extension UnicodeNormalizationForm { case .nfd: String(localized: "UnicodeNormalization.nfd.description", defaultValue: "Canonical Decomposition", - table: "UnicodeNormalization", + bundle: .module, comment: "description for NFD") case .nfc: String(localized: "UnicodeNormalization.nfc.description", defaultValue: "Canonical Decomposition, followed by Canonical Composition", - table: "UnicodeNormalization", + bundle: .module, comment: "description for NFC") case .nfkd: String(localized: "UnicodeNormalization.nfkd.description", defaultValue: "Compatibility Decomposition", - table: "UnicodeNormalization", + bundle: .module, comment: "description for NFKD") case .nfkc: String(localized: "UnicodeNormalization.nfkc.description", defaultValue: "Compatibility Decomposition, followed by Canonical Composition", - table: "UnicodeNormalization", + bundle: .module, comment: "description for NFKC") case .nfkcCasefold: String(localized: "UnicodeNormalization.nfkcCasefold.description", defaultValue: "Applying NFKC, case folding, and removal of default-ignorable code points", - table: "UnicodeNormalization", + bundle: .module, comment: "description for NFKD Casefold") case .modifiedNFD: String(localized: "UnicodeNormalization.modifiedNFD.description", defaultValue: "Unofficial NFD-based normalization form used in HFS+", - table: "UnicodeNormalization", + bundle: .module, comment: "description for Modified NFD") case .modifiedNFC: String(localized: "UnicodeNormalization.modifiedNFC.description", defaultValue: "Unofficial NFC-based normalization form corresponding to Modified NFD", - table: "UnicodeNormalization", + bundle: .module, comment: "description for Modified NFC") } } - - - /// Unique identifier for menu item. - var tag: Int { - - Self.allCases.firstIndex(of: self)! - } } diff --git a/CotEditor/Resources/Localizables/Models/Syntax.xcstrings b/Packages/EditorCore/Sources/Syntax/Resources/Localizable.xcstrings similarity index 92% rename from CotEditor/Resources/Localizables/Models/Syntax.xcstrings rename to Packages/EditorCore/Sources/Syntax/Resources/Localizable.xcstrings index 77034dafa5..e5be97ed41 100644 --- a/CotEditor/Resources/Localizables/Models/Syntax.xcstrings +++ b/Packages/EditorCore/Sources/Syntax/Resources/Localizable.xcstrings @@ -193,101 +193,6 @@ } } }, - "SyntaxName.none" : { - "extractionState" : "extracted_with_value", - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Žádný" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ohne" - } - }, - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "None" - } - }, - "en-GB" : { - "stringUnit" : { - "state" : "translated", - "value" : "None" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nada" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aucun" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nessuno" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "なし" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "없음" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Geen" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Brak" - } - }, - "pt" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nenhum" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Hiçbiri" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "无" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "無" - } - } - } - }, "SyntaxType.attributes.label" : { "extractionState" : "extracted_with_value", "localizations" : { diff --git a/CotEditor/Sources/Models/Syntax/Syntax+Localization.swift b/Packages/EditorCore/Sources/Syntax/Syntax+Localization.swift similarity index 82% rename from CotEditor/Sources/Models/Syntax/Syntax+Localization.swift rename to Packages/EditorCore/Sources/Syntax/Syntax+Localization.swift index 0c002166c1..11ce2c1429 100644 --- a/CotEditor/Sources/Models/Syntax/Syntax+Localization.swift +++ b/Packages/EditorCore/Sources/Syntax/Syntax+Localization.swift @@ -1,5 +1,6 @@ // // Syntax+Localization.swift +// Syntax // // CotEditor // https://coteditor.com @@ -23,10 +24,7 @@ // limitations under the License. // -import Foundation -import Syntax - -extension Syntax.Kind { +public extension Syntax.Kind { var label: String { @@ -34,19 +32,19 @@ extension Syntax.Kind { case .general: String(localized: "Syntax.Kind.general.label", defaultValue: "General", - table: "Syntax", + bundle: .module, comment: "syntax kind") case .code: String(localized: "Syntax.Kind.code.label", defaultValue: "Code", - table: "Syntax", + bundle: .module, comment: "syntax kind") } } } -extension SyntaxType { +public extension SyntaxType { var label: String { @@ -54,43 +52,43 @@ extension SyntaxType { case .keywords: String(localized: "SyntaxType.keywords.label", defaultValue: "Keywords", - table: "Syntax") + bundle: .module) case .commands: String(localized: "SyntaxType.commands.label", defaultValue: "Commands", - table: "Syntax") + bundle: .module) case .types: String(localized: "SyntaxType.types.label", defaultValue: "Types", - table: "Syntax") + bundle: .module) case .attributes: String(localized: "SyntaxType.attributes.label", defaultValue: "Attributes", - table: "Syntax") + bundle: .module) case .variables: String(localized: "SyntaxType.variables.label", defaultValue: "Variables", - table: "Syntax") + bundle: .module) case .values: String(localized: "SyntaxType.values.label", defaultValue: "Values", - table: "Syntax") + bundle: .module) case .numbers: String(localized: "SyntaxType.numbers.label", defaultValue: "Numbers", - table: "Syntax") + bundle: .module) case .strings: String(localized: "SyntaxType.strings.label", defaultValue: "Strings", - table: "Syntax") + bundle: .module) case .characters: String(localized: "SyntaxType.characters.label", defaultValue: "Characters", - table: "Syntax") + bundle: .module) case .comments: String(localized: "SyntaxType.comments.label", defaultValue: "Comments", - table: "Syntax") + bundle: .module) } } }