mirror of
https://github.com/tappollo/Xcode-Quick-Localization.git
synced 2026-01-12 22:37:18 +08:00
258 lines
9.6 KiB
Objective-C
258 lines
9.6 KiB
Objective-C
//
|
|
// QuickLocalization.m
|
|
// QuickLocalization
|
|
//
|
|
// Created by Zitao Xiong on 5/12/13.
|
|
// Copyright (c) 2013 nanaimostudio. All rights reserved.
|
|
//
|
|
|
|
#import "QuickLocalization.h"
|
|
#import "RCXcode.h"
|
|
#import "OLSettingController.h"
|
|
#import "QLMenuGenerator.h"
|
|
#import "QLIDEHelper.h"
|
|
|
|
static NSString *localizeRegexs[] = {
|
|
@"NSLocalizedString\\s*\\(\\s*@\"(.*)\"\\s*,\\s*(.*)\\s*\\)",
|
|
@"localizedStringForKey:\\s*@\"(.*)\"\\s*value:\\s*(.*)\\s*table:\\s*(.*)",
|
|
@"NSLocalizedStringFromTable\\s*\\(\\s*@\"(.*)\"\\s*,\\s*(.*)\\s*,\\s*(.*)\\s*\\)",
|
|
@"NSLocalizedStringFromTableInBundle\\s*\\(\\s*@\"(.*)\"\\s*,\\s*(.*)\\s*,\\s*(.*)\\s*,\\s*(.*)\\s*\\)",
|
|
@"NSLocalizedStringWithDefaultValue\\s*\\(\\s*@\"(.*)\"\\s*,\\s*(.*)\\s*,\\s*(.*)\\s*,\\s*(.*)\\s*,\\s*(.*)\\s*\\)"
|
|
};
|
|
|
|
static NSString *objcStringRegexs = @"@([\"'])(?:(?=(\\\\?))\\2.)*?\\1";
|
|
static NSString *swiftStringRegexs = @"([\"'])(?:(?=(\\\\?))\\2.)*?\\1";
|
|
|
|
static NSString * const QLShouldUseNilForComment = @"QLShouldUseNilForComment";
|
|
static NSString * const QLShouldUseSnippetForComment = @"QLShouldUseSnippetForComment";
|
|
static NSString * const QLShouldUseSwiftSyntax = @"QLShouldUseSwiftSyntax";
|
|
static NSString * const QLShouldUseNSLocalizedStringFromTableInBundle = @"QLShouldUseNSLocalizedStringFromTableInBundle";
|
|
|
|
@interface QuickLocalization ()
|
|
|
|
@property (nonatomic, assign) BOOL shouldUseNilForComment;
|
|
@property (nonatomic, assign) BOOL shouldUseSnippetForComment;
|
|
@property (nonatomic, assign) BOOL shouldUseSwiftSyntax;
|
|
@property (nonatomic, assign) BOOL shouldUseStringFromTableInBundle;
|
|
@property (nonatomic, strong) OLSettingController *settingControlelr;
|
|
@property (nonatomic, strong) NSBundle *bundle;
|
|
@end
|
|
|
|
@implementation QuickLocalization
|
|
|
|
static id sharedPlugin = nil;
|
|
|
|
|
|
+ (void)pluginDidLoad:(NSBundle *)plugin
|
|
{
|
|
static id sharedPlugin = nil;
|
|
static dispatch_once_t onceToken;
|
|
NSString *currentApplicationName = [[NSBundle mainBundle] infoDictionary][@"CFBundleName"];
|
|
if ([currentApplicationName isEqual:@"Xcode"]) {
|
|
dispatch_once(&onceToken, ^{
|
|
sharedPlugin = [[self alloc] initWithBundle:plugin];
|
|
});
|
|
}
|
|
}
|
|
|
|
- (id)initWithBundle:(NSBundle *)plugin
|
|
{
|
|
if (self = [super init]) {
|
|
// reference to plugin's bundle, for resource acccess
|
|
self.bundle = plugin;
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(applicationDidFinishLaunching:)
|
|
name:NSApplicationDidFinishLaunchingNotification
|
|
object:nil];
|
|
|
|
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
|
|
[self createMenuItem];
|
|
}
|
|
|
|
- (void)createMenuItem {
|
|
[QLMenuGenerator generateMenuItems:self.bundle version:[self getBundleVersion] target:self];
|
|
}
|
|
|
|
// Sample Action, for menu item:
|
|
- (void)quickLocalization:(id)sender {
|
|
IDESourceCodeDocument *document = [RCXcode currentSourceCodeDocument];
|
|
NSTextView *textView = [RCXcode currentSourceCodeTextView];
|
|
if (!document || !textView) {
|
|
return;
|
|
}
|
|
|
|
NSArray *selectedRanges = [textView selectedRanges];
|
|
if ([selectedRanges count] > 0) {
|
|
NSRange range = [[selectedRanges objectAtIndex:0] rangeValue];
|
|
BOOL replaced = [self replaceTextInRange:range textView:textView];
|
|
//if user selected text is not being replaced, try using line replacing
|
|
if (!replaced) {
|
|
NSRange lineRange = [textView.textStorage.string lineRangeForRange:range];
|
|
[self replaceTextInRange:lineRange textView:textView];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (BOOL)replaceTextInRange:(NSRange)range textView:(NSTextView *)textView {
|
|
NSString *line = [textView.textStorage.string substringWithRange:range];
|
|
|
|
NSRegularExpression *localizedRex = [[NSRegularExpression alloc] initWithPattern:localizeRegexs[0] options:NSRegularExpressionCaseInsensitive error:nil];
|
|
NSArray *localizedMatches = [localizedRex matchesInString:line options:0 range:NSMakeRange(0, [line length])];
|
|
|
|
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:[self currentStringRegexs] options:NSRegularExpressionCaseInsensitive error:nil];
|
|
NSArray *matches = [regex matchesInString:line options:0 range:NSMakeRange(0, [line length])];
|
|
NSUInteger addedLength = 0;
|
|
for (int i = 0; i < [matches count]; i++) {
|
|
NSTextCheckingResult *result = [matches objectAtIndex:i];
|
|
NSRange matchedRangeInLine = result.range;
|
|
NSRange matchedRangeInDocument = NSMakeRange(range.location + matchedRangeInLine.location + addedLength, matchedRangeInLine.length);
|
|
if ([self isRange:matchedRangeInLine inSkipedRanges:localizedMatches]) {
|
|
continue;
|
|
}
|
|
NSString *string = [line substringWithRange:matchedRangeInLine];
|
|
// NSLog(@"string index:%d, %@", i, string);
|
|
NSString *outputString;
|
|
|
|
NSString *savedFormatString = [[NSUserDefaults standardUserDefaults] objectForKey:kQLFormatStringKey];
|
|
|
|
NSUInteger placeHolderCount = QL_CountOccurentOfStringWithSubString(savedFormatString, @"%@");
|
|
if (placeHolderCount == 2) {
|
|
outputString = [NSString stringWithFormat:savedFormatString, string, string];
|
|
}
|
|
else if (placeHolderCount == 1) {
|
|
outputString = [NSString stringWithFormat:savedFormatString, string];
|
|
}
|
|
else {
|
|
outputString = [NSString stringWithFormat:@"placeholder incorrect, format string is %@", savedFormatString];
|
|
}
|
|
|
|
addedLength = addedLength + outputString.length - string.length;
|
|
if ([textView shouldChangeTextInRange:matchedRangeInDocument replacementString:outputString]) {
|
|
[textView.textStorage replaceCharactersInRange:matchedRangeInDocument
|
|
withAttributedString:[[NSAttributedString alloc] initWithString:outputString]];
|
|
[textView didChangeText];
|
|
}
|
|
|
|
}
|
|
|
|
if (matches.count) {
|
|
return YES;
|
|
}
|
|
else {
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
- (NSString *)currentStringRegexs {
|
|
BOOL isSwiftSyntax = [[NSUserDefaults standardUserDefaults] boolForKey:kQLFormatStringSwiftSyntax];
|
|
if (isSwiftSyntax) {
|
|
return swiftStringRegexs;
|
|
}
|
|
else {
|
|
return objcStringRegexs;
|
|
}
|
|
}
|
|
|
|
- (BOOL)isRange:(NSRange)range inSkipedRanges:(NSArray *)ranges {
|
|
for (int i = 0; i < [ranges count]; i++) {
|
|
NSTextCheckingResult *result = [ranges objectAtIndex:i];
|
|
NSRange skippedRange = result.range;
|
|
if (skippedRange.location <= range.location && skippedRange.location + skippedRange.length > range.location + range.length) {
|
|
return YES;
|
|
}
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (void)toggleNilOption {
|
|
self.shouldUseNilForComment = !self.shouldUseNilForComment;
|
|
|
|
if (self.shouldUseNilForComment) {
|
|
self.shouldUseSnippetForComment = NO;
|
|
}
|
|
}
|
|
|
|
- (void)toggleSnippetOption {
|
|
self.shouldUseSnippetForComment = !self.shouldUseSnippetForComment;
|
|
if (self.shouldUseSnippetForComment) {
|
|
self.shouldUseNilForComment = NO;
|
|
}
|
|
}
|
|
|
|
- (void)toggleSwiftOption {
|
|
self.shouldUseSwiftSyntax = !self.shouldUseSwiftSyntax;
|
|
}
|
|
|
|
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
|
|
if ([menuItem action] == @selector(toggleNilOption)) {
|
|
[menuItem setState:[self shouldUseNilForComment] ? NSOnState : NSOffState];
|
|
}
|
|
else if ([menuItem action] == @selector(toggleSnippetOption)) {
|
|
[menuItem setState:[self shouldUseSnippetForComment] ? NSOnState : NSOffState];
|
|
}
|
|
else if ([menuItem action] == @selector(toggleSwiftOption)) {
|
|
[menuItem setState:[self shouldUseSwiftSyntax] ? NSOnState : NSOffState];
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
- (void)toggleStringFromTableInBundle {
|
|
self.shouldUseStringFromTableInBundle = !self.shouldUseStringFromTableInBundle;
|
|
}
|
|
|
|
#pragma mark Preferences
|
|
- (BOOL)shouldUseStringFromTableInBundle {
|
|
return [[NSUserDefaults standardUserDefaults] boolForKey:QLShouldUseNSLocalizedStringFromTableInBundle];
|
|
}
|
|
|
|
- (void)setShouldUseStringFromTableInBundle:(BOOL)shouldUseStringFromTableInBundle {
|
|
[[NSUserDefaults standardUserDefaults] setBool:shouldUseStringFromTableInBundle forKey:QLShouldUseNSLocalizedStringFromTableInBundle];
|
|
}
|
|
|
|
- (BOOL)shouldUseNilForComment {
|
|
return [[NSUserDefaults standardUserDefaults] boolForKey:QLShouldUseNilForComment];
|
|
}
|
|
|
|
- (void)setShouldUseNilForComment:(BOOL)shouldUseNilForComment {
|
|
[[NSUserDefaults standardUserDefaults] setBool:shouldUseNilForComment forKey:QLShouldUseNilForComment];
|
|
}
|
|
|
|
- (BOOL)shouldUseSnippetForComment {
|
|
return [[NSUserDefaults standardUserDefaults] boolForKey:QLShouldUseSnippetForComment];
|
|
}
|
|
|
|
- (void)setShouldUseSnippetForComment:(BOOL)shouldUseSnippetForComment {
|
|
[[NSUserDefaults standardUserDefaults] setBool:shouldUseSnippetForComment forKey:QLShouldUseSnippetForComment];
|
|
}
|
|
|
|
- (BOOL)shouldUseSwiftSyntax {
|
|
return [[NSUserDefaults standardUserDefaults] boolForKey:QLShouldUseSwiftSyntax];
|
|
}
|
|
|
|
- (void)setShouldUseSwiftSyntax:(BOOL)shouldUseSwiftSyntax {
|
|
[[NSUserDefaults standardUserDefaults] setBool:shouldUseSwiftSyntax forKey:QLShouldUseSwiftSyntax];
|
|
}
|
|
|
|
- (NSString *)getBundleVersion
|
|
{
|
|
NSString *bundleVersion = [[self.bundle infoDictionary] objectForKey:@"CFBundleShortVersionString"];
|
|
return bundleVersion;
|
|
}
|
|
|
|
- (void)showSettingWindow
|
|
{
|
|
if (self.settingControlelr == nil) {
|
|
self.settingControlelr = [[OLSettingController alloc] initWithWindowNibName:@"OLSettingController"];
|
|
self.settingControlelr.bundle = self.bundle;
|
|
|
|
}
|
|
|
|
[self.settingControlelr showWindow:self.settingControlelr];
|
|
}
|
|
@end
|