From 216a0a186c0503e0dceaa947f652f297c6c968ef Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 22 Aug 2020 16:55:54 +0200 Subject: [PATCH] Implement first version of the new rule engine. --- app/Api/V1/Controllers/RuleController.php | 41 +- app/Api/V1/Requests/RuleTriggerRequest.php | 46 +- app/Factory/TagFactory.php | 18 +- app/Providers/FireflyServiceProvider.php | 15 + app/Support/Search/OperatorQuerySearch.php | 28 +- app/Support/Search/SearchInterface.php | 5 + .../Actions/ActionInterface.php | 8 + app/TransactionRules/Actions/AddTag.php | 44 +- .../Engine/RuleEngineInterface.php | 10 + .../Engine/SearchRuleEngine.php | 169 +++ resources/lang/en_US/firefly.php | 1025 +++++++++-------- .../Search/OperatorQuerySearchTest.php | 2 +- 12 files changed, 825 insertions(+), 586 deletions(-) diff --git a/app/Api/V1/Controllers/RuleController.php b/app/Api/V1/Controllers/RuleController.php index fc9b73fc5d..1ffa018fd0 100644 --- a/app/Api/V1/Controllers/RuleController.php +++ b/app/Api/V1/Controllers/RuleController.php @@ -33,12 +33,14 @@ use FireflyIII\Models\Rule; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\TransactionRules\Engine\RuleEngine; +use FireflyIII\TransactionRules\Engine\RuleEngineInterface; use FireflyIII\TransactionRules\TransactionMatcher; use FireflyIII\Transformers\RuleTransformer; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Support\Collection; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Item; @@ -215,8 +217,8 @@ class RuleController extends Controller * @param RuleTestRequest $request * @param Rule $rule * - * @throws FireflyException * @return JsonResponse + * @throws FireflyException */ public function testRule(RuleTestRequest $request, Rule $rule): JsonResponse { @@ -265,28 +267,27 @@ class RuleController extends Controller // Get parameters specified by the user $parameters = $request->getTriggerParameters(); - /** @var RuleEngine $ruleEngine */ - $ruleEngine = app(RuleEngine::class); - $ruleEngine->setUser(auth()->user()); + /** @var RuleEngineInterface $ruleEngine */ + $ruleEngine = app(RuleEngineInterface::class); + $ruleEngine->setRules(new Collection([$rule])); - $rules = [$rule->id]; - - $ruleEngine->setRulesToApply($rules); - $ruleEngine->setTriggerMode(RuleEngine::TRIGGER_STORE); - - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setAccounts($parameters['accounts']); - $collector->setRange($parameters['start_date'], $parameters['end_date']); - $journals = $collector->getExtractedJournals(); - - /** @var array $journal */ - foreach ($journals as $journal) { - Log::debug('Start of new journal.'); - $ruleEngine->processJournalArray($journal); - Log::debug('Done with all rules for this group + done with journal.'); + // overrule the rule(s) if necessary. + if (array_key_exists('start', $parameters) && null !== $parameters['start'] ) { + // add a range: + $ruleEngine->addOperator(['type' => 'date_after', 'value' => $parameters['start']->format('Y-m-d')]); } + if (array_key_exists('end', $parameters) && null !== $parameters['end']) { + // add a range: + $ruleEngine->addOperator(['type' => 'date_before', 'value' => $parameters['end']->format('Y-m-d')]); + } + if (array_key_exists('accounts', $parameters) && '' !== $parameters['accounts']) { + $ruleEngine->addOperator(['type' => 'account_id', 'value' => $parameters['accounts']]); + } + + // file the rule(s) + $ruleEngine->fire(); + return response()->json([], 204); } diff --git a/app/Api/V1/Requests/RuleTriggerRequest.php b/app/Api/V1/Requests/RuleTriggerRequest.php index e2954dadaf..20dd3028c6 100644 --- a/app/Api/V1/Requests/RuleTriggerRequest.php +++ b/app/Api/V1/Requests/RuleTriggerRequest.php @@ -28,11 +28,8 @@ namespace FireflyIII\Api\V1\Requests; use Carbon\Carbon; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; -use Illuminate\Support\Collection; -use Log; /** * Class RuleTriggerRequest @@ -40,6 +37,7 @@ use Log; class RuleTriggerRequest extends FormRequest { use ConvertsDataTypes; + /** * Authorize logged in users. * @@ -57,9 +55,9 @@ class RuleTriggerRequest extends FormRequest public function getTriggerParameters(): array { return [ - 'start_date' => $this->getDate('start_date'), - 'end_date' => $this->getDate('end_date'), - 'accounts' => $this->getAccounts(), + 'start' => $this->getDate('start'), + 'end' => $this->getDate('end'), + 'accounts' => $this->getAccounts(), ]; } @@ -69,33 +67,17 @@ class RuleTriggerRequest extends FormRequest public function rules(): array { return [ - 'start_date' => 'required|date', - 'end_date' => 'required|date|after:start_date', + 'start' => 'date', + 'end' => 'date|after:start', ]; } /** - * @return Collection + * @return string */ - private function getAccounts(): Collection + private function getAccounts(): string { - $accountList = '' === (string) $this->query('accounts') ? [] : explode(',', $this->query('accounts')); - $accounts = new Collection; - - /** @var AccountRepositoryInterface $accountRepository */ - $accountRepository = app(AccountRepositoryInterface::class); - - foreach ($accountList as $accountId) { - Log::debug(sprintf('Searching for asset account with id "%s"', $accountId)); - $account = $accountRepository->findNull((int) $accountId); - if ($this->validAccount($account)) { - /** @noinspection NullPointerExceptionInspection */ - Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name)); - $accounts->push($account); - } - } - - return $accounts; + return (string) $this->query('accounts'); } /** @@ -111,14 +93,4 @@ class RuleTriggerRequest extends FormRequest return $result; } - /** - * @param Account|null $account - * - * @return bool - */ - private function validAccount(?Account $account): bool - { - return null !== $account && AccountType::ASSET === $account->accountType->type; - } - } diff --git a/app/Factory/TagFactory.php b/app/Factory/TagFactory.php index a277211d92..258620cf0b 100644 --- a/app/Factory/TagFactory.php +++ b/app/Factory/TagFactory.php @@ -35,23 +35,7 @@ use Log; */ class TagFactory { - /** @var Collection */ - private $tags; - /** @var User */ - private $user; - - - /** - * Constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } - } + private User $user; /** * @param array $data diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index 45a6993393..24a846e3f2 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -63,6 +63,8 @@ use FireflyIII\Support\Navigation; use FireflyIII\Support\Preferences; use FireflyIII\Support\Steam; use FireflyIII\Support\Telemetry; +use FireflyIII\TransactionRules\Engine\RuleEngineInterface; +use FireflyIII\TransactionRules\Engine\SearchRuleEngine; use FireflyIII\Validation\FireflyValidator; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -192,6 +194,19 @@ class FireflyServiceProvider extends ServiceProvider } ); + $this->app->bind( + RuleEngineInterface::class, + static function (Application $app) { + /** @var SearchRuleEngine $engine */ + $engine = app(SearchRuleEngine::class); + if ($app->auth->check()) { + $engine->setUser(auth()->user()); + } + + return $engine; + } + ); + // more generators: $this->app->bind(PopupReportInterface::class, PopupReport::class); $this->app->bind(HelpInterface::class, Help::class); diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index b277f23485..398a360846 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -62,6 +62,7 @@ class OperatorQuerySearch implements SearchInterface private User $user; private ParsedQuery $query; private int $page; + private int $limit; private array $words; private array $validOperators; private GroupCollectorInterface $collector; @@ -80,6 +81,7 @@ class OperatorQuerySearch implements SearchInterface $this->operators = new Collection; $this->page = 1; $this->words = []; + $this->limit = 25; $this->validOperators = array_keys(config('firefly.search.operators')); $this->startTime = microtime(true); $this->accountRepository = app(AccountRepositoryInterface::class); @@ -154,11 +156,9 @@ class OperatorQuerySearch implements SearchInterface $parser = new QueryParser(); $this->query = $parser->parse($query); - // get limit from preferences. - $pageSize = (int) app('preferences')->getForUser($this->user, 'listPageSize', 50)->data; $this->collector = app(GroupCollectorInterface::class); $this->collector->setUser($this->user); - $this->collector->setLimit($pageSize)->setPage($this->page); + $this->collector->setLimit($this->limit)->setPage($this->page); $this->collector->withAccountInformation()->withCategoryInformation()->withBudgetInformation(); Log::debug(sprintf('Found %d node(s)', count($this->query->getNodes()))); @@ -203,6 +203,8 @@ class OperatorQuerySearch implements SearchInterface $this->billRepository->setUser($user); $this->categoryRepository->setUser($user); $this->budgetRepository->setUser($user); + + $this->setLimit((int) app('preferences')->getForUser($user, 'listPageSize', 50)->data); } /** @@ -328,9 +330,16 @@ class OperatorQuerySearch implements SearchInterface } break; case 'account_id': - $account = $this->accountRepository->findNull((int) $value); - if (null !== $account) { - $this->collector->setAccounts(new Collection([$account])); + $parts = explode(',', $value); + $collection = new Collection; + foreach ($parts as $accountId) { + $account = $this->accountRepository->findNull((int) $value); + if (null !== $account) { + $collection->push($account); + } + } + if ($collection->count() > 0) { + $this->collector->setAccounts($collection); } break; // @@ -695,4 +704,11 @@ class OperatorQuerySearch implements SearchInterface ]; } + /** + * @param int $limit + */ + public function setLimit(int $limit): void + { + $this->limit = $limit; + } } \ No newline at end of file diff --git a/app/Support/Search/SearchInterface.php b/app/Support/Search/SearchInterface.php index cd990180be..42a2d387b4 100644 --- a/app/Support/Search/SearchInterface.php +++ b/app/Support/Search/SearchInterface.php @@ -51,6 +51,11 @@ interface SearchInterface */ public function setPage(int $page): void; + /** + * @param int $limit + */ + public function setLimit(int $limit): void; + /** * @return bool */ diff --git a/app/TransactionRules/Actions/ActionInterface.php b/app/TransactionRules/Actions/ActionInterface.php index 1f1a52aeef..6c836a51b6 100644 --- a/app/TransactionRules/Actions/ActionInterface.php +++ b/app/TransactionRules/Actions/ActionInterface.php @@ -45,4 +45,12 @@ interface ActionInterface * @return bool */ public function act(TransactionJournal $journal): bool; + + /** + * Execute the action on an array. + * + * @param array $journal + * @return bool + */ + public function actOnArray(array $journal): bool; } diff --git a/app/TransactionRules/Actions/AddTag.php b/app/TransactionRules/Actions/AddTag.php index 0254a649e1..05c55a04e8 100644 --- a/app/TransactionRules/Actions/AddTag.php +++ b/app/TransactionRules/Actions/AddTag.php @@ -22,9 +22,11 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; +use DB; use FireflyIII\Factory\TagFactory; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\User; use Log; /** @@ -32,8 +34,7 @@ use Log; */ class AddTag implements ActionInterface { - /** @var RuleAction The rule action */ - private $action; + private RuleAction $action; /** * TriggerInterface constructor. @@ -46,11 +47,8 @@ class AddTag implements ActionInterface } /** - * Add a tag - * - * @param TransactionJournal $journal - * - * @return bool + * @inheritDoc + * @deprecated */ public function act(TransactionJournal $journal): bool { @@ -77,4 +75,36 @@ class AddTag implements ActionInterface return false; } + + /** + * @inheritDoc + */ + public function actOnArray(array $journal): bool + { + // journal has this tag maybe? + /** @var TagFactory $factory */ + $factory = app(TagFactory::class); + $factory->setUser(User::find($journal['user_id'])); + $tag = $factory->findOrCreate($this->action->action_value); + if (null === $tag) { + // could not find, could not create tag. + Log::error(sprintf('RuleAction AddTag. Could not find or create tag "%s"', $this->action->action_value)); + + return false; + } + $count = DB::table('tag_transaction_journal') + ->where('tag_id', $tag->id) + ->where('transaction_journal_id', $journal['transaction_journal_id']) + ->count(); + if (0 === $count) { + // add to journal: + DB::table('tag_transaction_journal')->insert(['tag_id' => $tag->id, 'transaction_journal_id' => $journal['transaction_journal_id']]); + Log::debug(sprintf('RuleAction AddTag. Added tag #%d ("%s") to journal %d.', $tag->id, $tag->tag, $journal['transaction_journal_id'])); + + return true; + } + Log::debug(sprintf('RuleAction AddTag fired but tag %d ("%s") was already added to journal %d.', $tag->id, $tag->tag, $journal['transaction_journal_id'])); + + return false; + } } diff --git a/app/TransactionRules/Engine/RuleEngineInterface.php b/app/TransactionRules/Engine/RuleEngineInterface.php index 05b52e1201..64ba96f42d 100644 --- a/app/TransactionRules/Engine/RuleEngineInterface.php +++ b/app/TransactionRules/Engine/RuleEngineInterface.php @@ -21,6 +21,7 @@ namespace FireflyIII\TransactionRules\Engine; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -28,6 +29,10 @@ use Illuminate\Support\Collection; */ interface RuleEngineInterface { + /** + * @param User $user + */ + public function setUser(User $user): void; /** * Add rules for the engine to execute. * @@ -49,4 +54,9 @@ interface RuleEngineInterface */ public function addOperator(array $operator): void; + /** + * Fire the rule engine. + */ + public function fire(): void; + } \ No newline at end of file diff --git a/app/TransactionRules/Engine/SearchRuleEngine.php b/app/TransactionRules/Engine/SearchRuleEngine.php index 6d18dd1e40..f6e28a7401 100644 --- a/app/TransactionRules/Engine/SearchRuleEngine.php +++ b/app/TransactionRules/Engine/SearchRuleEngine.php @@ -21,10 +21,179 @@ namespace FireflyIII\TransactionRules\Engine; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Rule; +use FireflyIII\Models\RuleAction; +use FireflyIII\Models\RuleTrigger; +use FireflyIII\Support\Search\SearchInterface; +use FireflyIII\TransactionRules\Factory\ActionFactory; +use FireflyIII\User; +use Illuminate\Support\Collection; +use Log; + /** * Class SearchRuleEngine */ class SearchRuleEngine implements RuleEngineInterface { + private User $user; + private Collection $rules; + private array $operators; + public function __construct() + { + $this->rules = new Collection; + } + + /** + * @inheritDoc + */ + public function setUser(User $user): void + { + $this->user = $user; + $this->operators = []; + } + + /** + * @inheritDoc + */ + public function setRules(Collection $rules): void + { + foreach ($rules as $rule) { + if ($rule instanceof Rule) { + Log::debug(sprintf('Adding a rule to the SearchRuleEngine: #%d ("%s")', $rule->id, $rule->title)); + $this->rules->push($rule); + } + } + } + + /** + * @inheritDoc + */ + public function setRuleGroups(Collection $ruleGroups): void + { + die(__METHOD__); + } + + /** + * @inheritDoc + */ + public function addOperator(array $operator): void + { + Log::debug('Add operator: ', $operator); + $this->operators[] = $operator; + } + + /** + * @inheritDoc + * @throws FireflyException + */ + public function fire(): void + { + Log::debug('SearchRuleEngine::fire()!'); + foreach ($this->rules as $rule) { + $this->fireRule($rule); + } + } + + /** + * @param Rule $rule + * @throws FireflyException + */ + private function fireRule(Rule $rule): void + { + $searchArray = []; + /** @var RuleTrigger $ruleTrigger */ + foreach ($rule->ruleTriggers as $ruleTrigger) { + $searchArray[$ruleTrigger->trigger_type] = sprintf('"%s"', $ruleTrigger->trigger_value); + } + + // add local operators: + foreach ($this->operators as $operator) { + $searchArray[$operator['type']] = sprintf('"%s"', $operator['value']); + } + $toJoin = []; + foreach ($searchArray as $type => $value) { + $toJoin[] = sprintf('%s:%s', $type, $value); + } + + $searchQuery = join(' ', $toJoin); + Log::debug(sprintf('Search query for rule #%d ("%s") = %s', $rule->id, $rule->title, $searchQuery)); + + // build and run the search engine. + $searchEngine = app(SearchInterface::class); + $searchEngine->setUser($this->user); + $searchEngine->setPage(1); + $searchEngine->setLimit(1337); + $searchEngine->parseQuery($searchQuery); + + $result = $searchEngine->searchTransactions(); + $collection = $result->getCollection(); + Log::debug(sprintf('Found %d transactions using search engine.', $collection->count())); + + $this->processResults($rule, $collection); + } + + /** + * @param Rule $rule + * @param Collection $collection + * @throws FireflyException + */ + private function processResults(Rule $rule, Collection $collection): void + { + Log::debug('Going to process results.'); + /** @var array $group */ + foreach ($collection as $group) { + $this->processTransactionGroup($rule, $group); + } + } + + /** + * @param Rule $rule + * @param array $group + * @throws FireflyException + */ + private function processTransactionGroup(Rule $rule, array $group): void + { + Log::debug(sprintf('Will now execute actions on transaction group #%d', $group['id'])); + /** @var array $transaction */ + foreach ($group['transactions'] as $transaction) { + $this->processTransactionJournal($rule, $transaction); + } + } + + /** + * @param Rule $rule + * @param array $transaction + * @throws FireflyException + */ + private function processTransactionJournal(Rule $rule, array $transaction): void + { + Log::debug(sprintf('Will now execute actions on transaction journal #%d', $transaction['transaction_journal_id'])); + /** @var RuleAction $ruleAction */ + foreach ($rule->ruleActions as $ruleAction) { + $break = $this->processRuleAction($ruleAction, $transaction); + if (true === $break) { + break; + } + } + } + + /** + * @param RuleAction $ruleAction + * @param array $transaction + * @return bool + * @throws FireflyException + */ + private function processRuleAction(RuleAction $ruleAction, array $transaction): bool + { + Log::debug(sprintf('Executing rule action "%s" with value "%s"', $ruleAction->action_type, $ruleAction->action_value)); + $actionClass = ActionFactory::getAction($ruleAction); + $actionClass->actOnArray($transaction); + if ($ruleAction->stop_processing) { + Log::debug(sprintf('Rule action "%s" asks to break, so break!', $ruleAction->action_value)); + return true; + } + return false; + } } \ No newline at end of file diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 42eaa9cb58..92d92cecb8 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -24,425 +24,455 @@ declare(strict_types=1); return [ // general stuff: - 'close' => 'Close', - 'actions' => 'Actions', - 'edit' => 'Edit', - 'delete' => 'Delete', - 'split' => 'Split', - 'single_split' => 'Split', - 'clone' => 'Clone', - 'last_seven_days' => 'Last seven days', - 'last_thirty_days' => 'Last thirty days', - 'welcome_back' => 'What\'s playing?', - 'everything' => 'Everything', - 'today' => 'today', - 'customRange' => 'Custom range', - 'apply' => 'Apply', - 'select_date' => 'Select date..', - 'cancel' => 'Cancel', - 'from' => 'From', - 'to' => 'To', - 'structure' => 'Structure', - 'help_translating' => 'This help text is not yet available in your language. Will you help translate?', - 'showEverything' => 'Show everything', - 'never' => 'Never', - 'no_results_for_empty_search' => 'Your search was empty, so nothing was found.', - 'removed_amount' => 'Removed :amount', - 'added_amount' => 'Added :amount', - 'asset_account_role_help' => 'Any extra options resulting from your choice can be set later.', - 'Opening balance' => 'Opening balance', - 'create_new_stuff' => 'Create new stuff', - 'new_withdrawal' => 'New withdrawal', - 'create_new_transaction' => 'Create new transaction', - 'sidebar_frontpage_create' => 'Create', - 'new_transaction' => 'New transaction', - 'no_rules_for_bill' => 'This bill has no rules associated to it.', - 'go_to_asset_accounts' => 'View your asset accounts', - 'go_to_budgets' => 'Go to your budgets', - 'new_clone_instructions' => 'This button will automatically clone the transaction and set the date to today. Are you sure?', - 'clones_journal_x' => 'This transaction is a clone of ":description" (#:id)', - 'go_to_categories' => 'Go to your categories', - 'go_to_bills' => 'Go to your bills', - 'go_to_expense_accounts' => 'See your expense accounts', - 'go_to_revenue_accounts' => 'See your revenue accounts', - 'go_to_piggies' => 'Go to your piggy banks', - 'new_deposit' => 'New deposit', - 'new_transfer' => 'New transfer', - 'new_transfers' => 'New transfer', - 'new_asset_account' => 'New asset account', - 'new_expense_account' => 'New expense account', - 'new_revenue_account' => 'New revenue account', - 'new_liabilities_account' => 'New liability', - 'new_budget' => 'New budget', - 'new_bill' => 'New bill', - 'block_account_logout' => 'You have been logged out. Blocked accounts cannot use this site. Did you register with a valid email address?', - 'flash_success' => 'Success!', - 'flash_info' => 'Message', - 'flash_warning' => 'Warning!', - 'flash_error' => 'Error!', - 'flash_info_multiple' => 'There is one message|There are :count messages', - 'flash_error_multiple' => 'There is one error|There are :count errors', - 'net_worth' => 'Net worth', - 'route_has_no_help' => 'There is no help for this route.', - 'help_for_this_page' => 'Help for this page', - 'no_help_could_be_found' => 'No help text could be found.', - 'no_help_title' => 'Apologies, an error occurred.', - 'two_factor_welcome' => 'Hello!', - 'two_factor_enter_code' => 'To continue, please enter your two factor authentication code. Your application can generate it for you.', - 'two_factor_code_here' => 'Enter code here', - 'two_factor_title' => 'Two factor authentication', - 'authenticate' => 'Authenticate', - 'two_factor_forgot_title' => 'Lost two factor authentication', - 'two_factor_forgot' => 'I forgot my two-factor thing.', - 'two_factor_lost_header' => 'Lost your two factor authentication?', - 'two_factor_lost_intro' => 'If you lost your backup codes as well, you have bad luck. This is not something you can fix from the web interface. You have two choices.', - 'two_factor_lost_fix_self' => 'If you run your own instance of Firefly III, read this entry in the FAQ for instructions.', - 'two_factor_lost_fix_owner' => 'Otherwise, email the site owner, :site_owner and ask them to reset your two factor authentication.', - 'mfa_backup_code' => 'You have used a backup code to login to Firefly III. It can\'t be used again, so cross it from your list.', - 'pref_two_factor_new_backup_codes' => 'Get new backup codes', - 'pref_two_factor_backup_code_count' => 'You have :count valid backup code.|You have :count valid backup codes.', - '2fa_i_have_them' => 'I stored them!', - 'warning_much_data' => ':days days of data may take a while to load.', - 'registered' => 'You have registered successfully!', - 'Default asset account' => 'Default asset account', - 'no_budget_pointer' => 'You seem to have no budgets yet. You should create some on the budgets-page. Budgets can help you keep track of expenses.', - 'no_bill_pointer' => 'You seem to have no bills yet. You should create some on the bills-page. Bills can help you keep track of expenses.', - 'Savings account' => 'Savings account', - 'Credit card' => 'Credit card', - 'source_accounts' => 'Source account|Source accounts', - 'destination_accounts' => 'Destination account|Destination accounts', - 'user_id_is' => 'Your user id is :user', - 'field_supports_markdown' => 'This field supports Markdown.', - 'need_more_help' => 'If you need more help using Firefly III, please open a ticket on Github.', - 'reenable_intro_text' => 'You can also re-enable the introduction guidance.', - 'intro_boxes_after_refresh' => 'The introduction boxes will reappear when you refresh the page.', - 'show_all_no_filter' => 'Show all transactions without grouping them by date.', - 'expenses_by_category' => 'Expenses by category', - 'expenses_by_budget' => 'Expenses by budget', - 'income_by_category' => 'Income by category', - 'expenses_by_asset_account' => 'Expenses by asset account', - 'expenses_by_expense_account' => 'Expenses by expense account', - 'cannot_redirect_to_account' => 'Firefly III cannot redirect you to the correct page. Apologies.', - 'sum_of_expenses' => 'Sum of expenses', - 'sum_of_income' => 'Sum of income', - 'liabilities' => 'Liabilities', - 'spent_in_specific_budget' => 'Spent in budget ":budget"', - 'spent_in_specific_double' => 'Spent in account ":account"', - 'earned_in_specific_double' => 'Earned in account ":account"', - 'source_account' => 'Source account', - 'source_account_reconciliation' => 'You can\'t edit the source account of a reconciliation transaction.', - 'destination_account' => 'Destination account', - 'destination_account_reconciliation' => 'You can\'t edit the destination account of a reconciliation transaction.', - 'sum_of_expenses_in_budget' => 'Spent total in budget ":budget"', - 'left_in_budget_limit' => 'Left to spend according to budgeting', - 'current_period' => 'Current period', - 'show_the_current_period_and_overview' => 'Show the current period and overview', - 'pref_languages_locale' => 'For a language other than English to work properly, your operating system must be equipped with the correct locale-information. If these are not present, currency data, dates and amounts may be formatted wrong.', - 'budget_in_period' => 'All transactions for budget ":name" between :start and :end in :currency', - 'chart_budget_in_period' => 'Chart for all transactions for budget ":name" between :start and :end in :currency', - 'chart_budget_in_period_only_currency' => 'The amount you budgeted was in :currency, so this chart will only show transactions in :currency.', - 'chart_account_in_period' => 'Chart for all transactions for account ":name" (:balance) between :start and :end', - 'chart_category_in_period' => 'Chart for all transactions for category ":name" between :start and :end', - 'chart_category_all' => 'Chart for all transactions for category ":name"', - 'clone_withdrawal' => 'Clone this withdrawal', - 'clone_deposit' => 'Clone this deposit', - 'clone_transfer' => 'Clone this transfer', - 'multi_select_no_selection' => 'None selected', - 'multi_select_select_all' => 'Select all', - 'multi_select_n_selected' => 'selected', - 'multi_select_all_selected' => 'All selected', - 'multi_select_filter_placeholder' => 'Find..', - 'intro_next_label' => 'Next', - 'intro_prev_label' => 'Previous', - 'intro_skip_label' => 'Skip', - 'intro_done_label' => 'Done', - 'between_dates_breadcrumb' => 'Between :start and :end', - 'all_journals_without_budget' => 'All transactions without a budget', - 'journals_without_budget' => 'Transactions without a budget', - 'all_journals_without_category' => 'All transactions without a category', - 'journals_without_category' => 'Transactions without a category', - 'all_journals_for_account' => 'All transactions for account :name', - 'chart_all_journals_for_account' => 'Chart of all transactions for account :name', - 'journals_in_period_for_account' => 'All transactions for account :name between :start and :end', - 'transferred' => 'Transferred', - 'all_withdrawal' => 'All expenses', - 'all_transactions' => 'All transactions', - 'title_withdrawal_between' => 'All expenses between :start and :end', - 'all_deposit' => 'All revenue', - 'title_deposit_between' => 'All revenue between :start and :end', - 'all_transfers' => 'All transfers', - 'title_transfers_between' => 'All transfers between :start and :end', - 'all_transfer' => 'All transfers', - 'all_journals_for_tag' => 'All transactions for tag ":tag"', - 'title_transfer_between' => 'All transfers between :start and :end', - 'all_journals_for_category' => 'All transactions for category :name', - 'all_journals_for_budget' => 'All transactions for budget :name', - 'chart_all_journals_for_budget' => 'Chart of all transactions for budget :name', - 'journals_in_period_for_category' => 'All transactions for category :name between :start and :end', - 'journals_in_period_for_tag' => 'All transactions for tag :tag between :start and :end', - 'not_available_demo_user' => 'The feature you try to access is not available to demo users.', - 'exchange_rate_instructions' => 'Asset account "@name" only accepts transactions in @native_currency. If you wish to use @foreign_currency instead, make sure that the amount in @native_currency is known as well:', - 'transfer_exchange_rate_instructions' => 'Source asset account "@source_name" only accepts transactions in @source_currency. Destination asset account "@dest_name" only accepts transactions in @dest_currency. You must provide the transferred amount correctly in both currencies.', - 'transaction_data' => 'Transaction data', - 'invalid_server_configuration' => 'Invalid server configuration', - 'invalid_locale_settings' => 'Firefly III is unable to format monetary amounts because your server is missing the required packages. There are instructions how to do this.', - 'quickswitch' => 'Quickswitch', - 'sign_in_to_start' => 'Sign in to start your session', - 'sign_in' => 'Sign in', - 'register_new_account' => 'Register a new account', - 'forgot_my_password' => 'I forgot my password', - 'problems_with_input' => 'There were some problems with your input.', - 'reset_password' => 'Reset your password', - 'button_reset_password' => 'Reset password', - 'reset_button' => 'Reset', - 'want_to_login' => 'I want to login', - 'login_page_title' => 'Login to Firefly III', - 'register_page_title' => 'Register at Firefly III', - 'forgot_pw_page_title' => 'Forgot your password for Firefly III', - 'reset_pw_page_title' => 'Reset your password for Firefly III', - 'cannot_reset_demo_user' => 'You cannot reset the password of the demo user.', - 'no_att_demo_user' => 'The demo user can\'t upload attachments.', - 'button_register' => 'Register', - 'authorization' => 'Authorization', - 'active_bills_only' => 'active bills only', - 'active_exp_bills_only' => 'active and expected bills only', - 'per_period_sum_1D' => 'Expected daily costs', - 'per_period_sum_1W' => 'Expected weekly costs', - 'per_period_sum_1M' => 'Expected monthly costs', - 'per_period_sum_3M' => 'Expected quarterly costs', - 'per_period_sum_6M' => 'Expected half-yearly costs', - 'per_period_sum_1Y' => 'Expected yearly costs', - 'average_per_bill' => 'average per bill', - 'expected_total' => 'expected total', - 'reconciliation_account_name' => ':name reconciliation (:currency)', - 'saved' => 'Saved', + 'close' => 'Close', + 'actions' => 'Actions', + 'edit' => 'Edit', + 'delete' => 'Delete', + 'split' => 'Split', + 'single_split' => 'Split', + 'clone' => 'Clone', + 'last_seven_days' => 'Last seven days', + 'last_thirty_days' => 'Last thirty days', + 'welcome_back' => 'What\'s playing?', + 'everything' => 'Everything', + 'today' => 'today', + 'customRange' => 'Custom range', + 'apply' => 'Apply', + 'select_date' => 'Select date..', + 'cancel' => 'Cancel', + 'from' => 'From', + 'to' => 'To', + 'structure' => 'Structure', + 'help_translating' => 'This help text is not yet available in your language. Will you help translate?', + 'showEverything' => 'Show everything', + 'never' => 'Never', + 'no_results_for_empty_search' => 'Your search was empty, so nothing was found.', + 'removed_amount' => 'Removed :amount', + 'added_amount' => 'Added :amount', + 'asset_account_role_help' => 'Any extra options resulting from your choice can be set later.', + 'Opening balance' => 'Opening balance', + 'create_new_stuff' => 'Create new stuff', + 'new_withdrawal' => 'New withdrawal', + 'create_new_transaction' => 'Create new transaction', + 'sidebar_frontpage_create' => 'Create', + 'new_transaction' => 'New transaction', + 'no_rules_for_bill' => 'This bill has no rules associated to it.', + 'go_to_asset_accounts' => 'View your asset accounts', + 'go_to_budgets' => 'Go to your budgets', + 'new_clone_instructions' => 'This button will automatically clone the transaction and set the date to today. Are you sure?', + 'clones_journal_x' => 'This transaction is a clone of ":description" (#:id)', + 'go_to_categories' => 'Go to your categories', + 'go_to_bills' => 'Go to your bills', + 'go_to_expense_accounts' => 'See your expense accounts', + 'go_to_revenue_accounts' => 'See your revenue accounts', + 'go_to_piggies' => 'Go to your piggy banks', + 'new_deposit' => 'New deposit', + 'new_transfer' => 'New transfer', + 'new_transfers' => 'New transfer', + 'new_asset_account' => 'New asset account', + 'new_expense_account' => 'New expense account', + 'new_revenue_account' => 'New revenue account', + 'new_liabilities_account' => 'New liability', + 'new_budget' => 'New budget', + 'new_bill' => 'New bill', + 'block_account_logout' => 'You have been logged out. Blocked accounts cannot use this site. Did you register with a valid email address?', + 'flash_success' => 'Success!', + 'flash_info' => 'Message', + 'flash_warning' => 'Warning!', + 'flash_error' => 'Error!', + 'flash_info_multiple' => 'There is one message|There are :count messages', + 'flash_error_multiple' => 'There is one error|There are :count errors', + 'net_worth' => 'Net worth', + 'route_has_no_help' => 'There is no help for this route.', + 'help_for_this_page' => 'Help for this page', + 'no_help_could_be_found' => 'No help text could be found.', + 'no_help_title' => 'Apologies, an error occurred.', + 'two_factor_welcome' => 'Hello!', + 'two_factor_enter_code' => 'To continue, please enter your two factor authentication code. Your application can generate it for you.', + 'two_factor_code_here' => 'Enter code here', + 'two_factor_title' => 'Two factor authentication', + 'authenticate' => 'Authenticate', + 'two_factor_forgot_title' => 'Lost two factor authentication', + 'two_factor_forgot' => 'I forgot my two-factor thing.', + 'two_factor_lost_header' => 'Lost your two factor authentication?', + 'two_factor_lost_intro' => 'If you lost your backup codes as well, you have bad luck. This is not something you can fix from the web interface. You have two choices.', + 'two_factor_lost_fix_self' => 'If you run your own instance of Firefly III, read this entry in the FAQ for instructions.', + 'two_factor_lost_fix_owner' => 'Otherwise, email the site owner, :site_owner and ask them to reset your two factor authentication.', + 'mfa_backup_code' => 'You have used a backup code to login to Firefly III. It can\'t be used again, so cross it from your list.', + 'pref_two_factor_new_backup_codes' => 'Get new backup codes', + 'pref_two_factor_backup_code_count' => 'You have :count valid backup code.|You have :count valid backup codes.', + '2fa_i_have_them' => 'I stored them!', + 'warning_much_data' => ':days days of data may take a while to load.', + 'registered' => 'You have registered successfully!', + 'Default asset account' => 'Default asset account', + 'no_budget_pointer' => 'You seem to have no budgets yet. You should create some on the budgets-page. Budgets can help you keep track of expenses.', + 'no_bill_pointer' => 'You seem to have no bills yet. You should create some on the bills-page. Bills can help you keep track of expenses.', + 'Savings account' => 'Savings account', + 'Credit card' => 'Credit card', + 'source_accounts' => 'Source account|Source accounts', + 'destination_accounts' => 'Destination account|Destination accounts', + 'user_id_is' => 'Your user id is :user', + 'field_supports_markdown' => 'This field supports Markdown.', + 'need_more_help' => 'If you need more help using Firefly III, please open a ticket on Github.', + 'reenable_intro_text' => 'You can also re-enable the introduction guidance.', + 'intro_boxes_after_refresh' => 'The introduction boxes will reappear when you refresh the page.', + 'show_all_no_filter' => 'Show all transactions without grouping them by date.', + 'expenses_by_category' => 'Expenses by category', + 'expenses_by_budget' => 'Expenses by budget', + 'income_by_category' => 'Income by category', + 'expenses_by_asset_account' => 'Expenses by asset account', + 'expenses_by_expense_account' => 'Expenses by expense account', + 'cannot_redirect_to_account' => 'Firefly III cannot redirect you to the correct page. Apologies.', + 'sum_of_expenses' => 'Sum of expenses', + 'sum_of_income' => 'Sum of income', + 'liabilities' => 'Liabilities', + 'spent_in_specific_budget' => 'Spent in budget ":budget"', + 'spent_in_specific_double' => 'Spent in account ":account"', + 'earned_in_specific_double' => 'Earned in account ":account"', + 'source_account' => 'Source account', + 'source_account_reconciliation' => 'You can\'t edit the source account of a reconciliation transaction.', + 'destination_account' => 'Destination account', + 'destination_account_reconciliation' => 'You can\'t edit the destination account of a reconciliation transaction.', + 'sum_of_expenses_in_budget' => 'Spent total in budget ":budget"', + 'left_in_budget_limit' => 'Left to spend according to budgeting', + 'current_period' => 'Current period', + 'show_the_current_period_and_overview' => 'Show the current period and overview', + 'pref_languages_locale' => 'For a language other than English to work properly, your operating system must be equipped with the correct locale-information. If these are not present, currency data, dates and amounts may be formatted wrong.', + 'budget_in_period' => 'All transactions for budget ":name" between :start and :end in :currency', + 'chart_budget_in_period' => 'Chart for all transactions for budget ":name" between :start and :end in :currency', + 'chart_budget_in_period_only_currency' => 'The amount you budgeted was in :currency, so this chart will only show transactions in :currency.', + 'chart_account_in_period' => 'Chart for all transactions for account ":name" (:balance) between :start and :end', + 'chart_category_in_period' => 'Chart for all transactions for category ":name" between :start and :end', + 'chart_category_all' => 'Chart for all transactions for category ":name"', + 'clone_withdrawal' => 'Clone this withdrawal', + 'clone_deposit' => 'Clone this deposit', + 'clone_transfer' => 'Clone this transfer', + 'multi_select_no_selection' => 'None selected', + 'multi_select_select_all' => 'Select all', + 'multi_select_n_selected' => 'selected', + 'multi_select_all_selected' => 'All selected', + 'multi_select_filter_placeholder' => 'Find..', + 'intro_next_label' => 'Next', + 'intro_prev_label' => 'Previous', + 'intro_skip_label' => 'Skip', + 'intro_done_label' => 'Done', + 'between_dates_breadcrumb' => 'Between :start and :end', + 'all_journals_without_budget' => 'All transactions without a budget', + 'journals_without_budget' => 'Transactions without a budget', + 'all_journals_without_category' => 'All transactions without a category', + 'journals_without_category' => 'Transactions without a category', + 'all_journals_for_account' => 'All transactions for account :name', + 'chart_all_journals_for_account' => 'Chart of all transactions for account :name', + 'journals_in_period_for_account' => 'All transactions for account :name between :start and :end', + 'transferred' => 'Transferred', + 'all_withdrawal' => 'All expenses', + 'all_transactions' => 'All transactions', + 'title_withdrawal_between' => 'All expenses between :start and :end', + 'all_deposit' => 'All revenue', + 'title_deposit_between' => 'All revenue between :start and :end', + 'all_transfers' => 'All transfers', + 'title_transfers_between' => 'All transfers between :start and :end', + 'all_transfer' => 'All transfers', + 'all_journals_for_tag' => 'All transactions for tag ":tag"', + 'title_transfer_between' => 'All transfers between :start and :end', + 'all_journals_for_category' => 'All transactions for category :name', + 'all_journals_for_budget' => 'All transactions for budget :name', + 'chart_all_journals_for_budget' => 'Chart of all transactions for budget :name', + 'journals_in_period_for_category' => 'All transactions for category :name between :start and :end', + 'journals_in_period_for_tag' => 'All transactions for tag :tag between :start and :end', + 'not_available_demo_user' => 'The feature you try to access is not available to demo users.', + 'exchange_rate_instructions' => 'Asset account "@name" only accepts transactions in @native_currency. If you wish to use @foreign_currency instead, make sure that the amount in @native_currency is known as well:', + 'transfer_exchange_rate_instructions' => 'Source asset account "@source_name" only accepts transactions in @source_currency. Destination asset account "@dest_name" only accepts transactions in @dest_currency. You must provide the transferred amount correctly in both currencies.', + 'transaction_data' => 'Transaction data', + 'invalid_server_configuration' => 'Invalid server configuration', + 'invalid_locale_settings' => 'Firefly III is unable to format monetary amounts because your server is missing the required packages. There are instructions how to do this.', + 'quickswitch' => 'Quickswitch', + 'sign_in_to_start' => 'Sign in to start your session', + 'sign_in' => 'Sign in', + 'register_new_account' => 'Register a new account', + 'forgot_my_password' => 'I forgot my password', + 'problems_with_input' => 'There were some problems with your input.', + 'reset_password' => 'Reset your password', + 'button_reset_password' => 'Reset password', + 'reset_button' => 'Reset', + 'want_to_login' => 'I want to login', + 'login_page_title' => 'Login to Firefly III', + 'register_page_title' => 'Register at Firefly III', + 'forgot_pw_page_title' => 'Forgot your password for Firefly III', + 'reset_pw_page_title' => 'Reset your password for Firefly III', + 'cannot_reset_demo_user' => 'You cannot reset the password of the demo user.', + 'no_att_demo_user' => 'The demo user can\'t upload attachments.', + 'button_register' => 'Register', + 'authorization' => 'Authorization', + 'active_bills_only' => 'active bills only', + 'active_exp_bills_only' => 'active and expected bills only', + 'per_period_sum_1D' => 'Expected daily costs', + 'per_period_sum_1W' => 'Expected weekly costs', + 'per_period_sum_1M' => 'Expected monthly costs', + 'per_period_sum_3M' => 'Expected quarterly costs', + 'per_period_sum_6M' => 'Expected half-yearly costs', + 'per_period_sum_1Y' => 'Expected yearly costs', + 'average_per_bill' => 'average per bill', + 'expected_total' => 'expected total', + 'reconciliation_account_name' => ':name reconciliation (:currency)', + 'saved' => 'Saved', // API access - 'authorization_request' => 'Firefly III v:version Authorization Request', - 'authorization_request_intro' => ':client is requesting permission to access your financial administration. Would you like to authorize :client to access these records?', - 'scopes_will_be_able' => 'This application will be able to:', - 'button_authorize' => 'Authorize', - 'none_in_select_list' => '(none)', - 'no_piggy_bank' => '(no piggy bank)', - 'name_in_currency' => ':name in :currency', - 'paid_in_currency' => 'Paid in :currency', - 'unpaid_in_currency' => 'Unpaid in :currency', - 'is_alpha_warning' => 'You are running an ALPHA version. Be wary of bugs and issues.', - 'is_beta_warning' => 'You are running an BETA version. Be wary of bugs and issues.', - 'all_destination_accounts' => 'Destination accounts', - 'all_source_accounts' => 'Source accounts', - 'back_to_index' => 'Back to the index', + 'authorization_request' => 'Firefly III v:version Authorization Request', + 'authorization_request_intro' => ':client is requesting permission to access your financial administration. Would you like to authorize :client to access these records?', + 'scopes_will_be_able' => 'This application will be able to:', + 'button_authorize' => 'Authorize', + 'none_in_select_list' => '(none)', + 'no_piggy_bank' => '(no piggy bank)', + 'name_in_currency' => ':name in :currency', + 'paid_in_currency' => 'Paid in :currency', + 'unpaid_in_currency' => 'Unpaid in :currency', + 'is_alpha_warning' => 'You are running an ALPHA version. Be wary of bugs and issues.', + 'is_beta_warning' => 'You are running an BETA version. Be wary of bugs and issues.', + 'all_destination_accounts' => 'Destination accounts', + 'all_source_accounts' => 'Source accounts', + 'back_to_index' => 'Back to the index', // check for updates: - 'update_check_title' => 'Check for updates', - 'admin_update_check_title' => 'Automatically check for update', - 'admin_update_check_explain' => 'Firefly III can check for updates automatically. When you enable this setting, it will contact the Firefly III update server to see if a new version of Firefly III is available. When it is, you will get a notification. You can test this notification using the button on the right. Please indicate below if you want Firefly III to check for updates.', - 'check_for_updates_permission' => 'Firefly III can check for updates, but it needs your permission to do so. Please go to the administration to indicate if you would like this feature to be enabled.', - 'updates_ask_me_later' => 'Ask me later', - 'updates_do_not_check' => 'Do not check for updates', - 'updates_enable_check' => 'Enable the check for updates', - 'admin_update_check_now_title' => 'Check for updates now', - 'admin_update_check_now_explain' => 'If you press the button, Firefly III will see if your current version is the latest.', - 'check_for_updates_button' => 'Check now!', - 'update_new_version_alert' => 'A new version of Firefly III is available. You are running :your_version, the latest version is :new_version which was released on :date.', - 'update_version_beta' => 'This version is a BETA version. You may run into issues.', - 'update_version_alpha' => 'This version is a ALPHA version. You may run into issues.', - 'update_current_version_alert' => 'You are running :version, which is the latest available release.', - 'update_newer_version_alert' => 'You are running :your_version, which is newer than the latest release, :new_version.', - 'update_check_error' => 'An error occurred while checking for updates: :error', - 'unknown_error' => 'Unknown error. Sorry about that.', - 'just_new_release' => 'A new version is available! Version :version was released :date. This release is very fresh. Wait a few days for the new release to stabilize.', - 'disabled_but_check' => 'You disabled update checking. So don\'t forget to check for updates yourself every now and then. Thank you!', - 'admin_update_channel_title' => 'Update channel', - 'admin_update_channel_explain' => 'Firefly III has three update "channels" which determine how ahead of the curve you are in terms of features, enhancements and bugs. Use the "beta" channel if you\'re adventurous and the "alpha" when you like to live life dangerously.', - 'update_channel_stable' => 'Stable. Everything should work as expected.', - 'update_channel_beta' => 'Beta. New features but things may be broken.', - 'update_channel_alpha' => 'Alpha. We throw stuff in, and use whatever sticks.', + 'update_check_title' => 'Check for updates', + 'admin_update_check_title' => 'Automatically check for update', + 'admin_update_check_explain' => 'Firefly III can check for updates automatically. When you enable this setting, it will contact the Firefly III update server to see if a new version of Firefly III is available. When it is, you will get a notification. You can test this notification using the button on the right. Please indicate below if you want Firefly III to check for updates.', + 'check_for_updates_permission' => 'Firefly III can check for updates, but it needs your permission to do so. Please go to the administration to indicate if you would like this feature to be enabled.', + 'updates_ask_me_later' => 'Ask me later', + 'updates_do_not_check' => 'Do not check for updates', + 'updates_enable_check' => 'Enable the check for updates', + 'admin_update_check_now_title' => 'Check for updates now', + 'admin_update_check_now_explain' => 'If you press the button, Firefly III will see if your current version is the latest.', + 'check_for_updates_button' => 'Check now!', + 'update_new_version_alert' => 'A new version of Firefly III is available. You are running :your_version, the latest version is :new_version which was released on :date.', + 'update_version_beta' => 'This version is a BETA version. You may run into issues.', + 'update_version_alpha' => 'This version is a ALPHA version. You may run into issues.', + 'update_current_version_alert' => 'You are running :version, which is the latest available release.', + 'update_newer_version_alert' => 'You are running :your_version, which is newer than the latest release, :new_version.', + 'update_check_error' => 'An error occurred while checking for updates: :error', + 'unknown_error' => 'Unknown error. Sorry about that.', + 'just_new_release' => 'A new version is available! Version :version was released :date. This release is very fresh. Wait a few days for the new release to stabilize.', + 'disabled_but_check' => 'You disabled update checking. So don\'t forget to check for updates yourself every now and then. Thank you!', + 'admin_update_channel_title' => 'Update channel', + 'admin_update_channel_explain' => 'Firefly III has three update "channels" which determine how ahead of the curve you are in terms of features, enhancements and bugs. Use the "beta" channel if you\'re adventurous and the "alpha" when you like to live life dangerously.', + 'update_channel_stable' => 'Stable. Everything should work as expected.', + 'update_channel_beta' => 'Beta. New features but things may be broken.', + 'update_channel_alpha' => 'Alpha. We throw stuff in, and use whatever sticks.', // search - 'search' => 'Search', - 'search_query' => 'Query', - 'search_found_transactions' => 'Firefly III found :count transaction in :time seconds.|Firefly III found :count transactions in :time seconds.', - 'search_found_more_transactions' => 'Firefly III found more than :count transactions in :time seconds.', - 'search_for_query' => 'Firefly III is searching for transactions with all of these words in them: :query', - 'search_modifier_amount_is' => 'Amount is exactly :value', - 'search_modifier_amount' => 'Amount is exactly :value', - 'search_modifier_amount_max' => 'Amount is at most :value', - 'search_modifier_amount_min' => 'Amount is at least :value', - 'search_modifier_amount_less' => 'Amount is less than :value', - 'search_modifier_amount_more' => 'Amount is more than :value', - 'search_modifier_source' => 'Source account is :value', - 'search_modifier_from' => 'Source account is :value', - 'search_modifier_destination' => 'Destination account is :value', - 'search_modifier_to' => 'Destination account is :value', - 'search_modifier_tag' => 'Tag is ":value"', - 'search_modifier_category' => 'Category is ":value"', - 'search_modifier_budget' => 'Budget is ":value"', - 'search_modifier_bill' => 'Bill is ":value"', - 'search_modifier_type' => 'Transaction type is :value', - 'search_modifier_date_is' => 'Transaction date is :value', - 'search_modifier_date_before' => 'Transaction date is before or on :value', - 'search_modifier_date_after' => 'Transaction date is after or on :value', - 'search_modifier_on' => 'Transaction date is :value', - 'search_modifier_before' => 'Transaction date is before :value', - 'search_modifier_after' => 'Transaction date is after :value', - 'search_modifier_created_on' => 'Transaction was created on :value', - 'search_modifier_updated_on' => 'Transaction was last updated on :value', - 'search_modifier_external_id' => 'External ID is ":value"', - 'search_modifier_internal_reference' => 'Internal reference is ":value"', - 'modifiers_applies_are' => 'The following modifiers are applied to the search as well:', - 'general_search_error' => 'An error occurred while searching. Please check the log files for more information.', - 'search_box' => 'Search', - 'search_box_intro' => 'Welcome to the search function of Firefly III. Enter your search query in the box. Make sure you check out the help file because the search is pretty advanced.', - 'search_error' => 'Error while searching', - 'search_searching' => 'Searching ...', - 'search_results' => 'Search results', + 'search' => 'Search', + 'search_query' => 'Query', + 'search_found_transactions' => 'Firefly III found :count transaction in :time seconds.|Firefly III found :count transactions in :time seconds.', + 'search_found_more_transactions' => 'Firefly III found more than :count transactions in :time seconds.', + 'search_for_query' => 'Firefly III is searching for transactions with all of these words in them: :query', + 'search_modifier_date_is' => 'Transaction date is ":value"', + 'search_modifier_date_before' => 'Transaction date is before or on ":value"', + 'search_modifier_date_after' => 'Transaction date is after or on ":value"', + 'search_modifier_created_on' => 'Transaction was created on ":value"', + 'search_modifier_updated_on' => 'Transaction was last updated on ":value"', + 'search_modifier_external_id' => 'External ID is ":value"', + 'search_modifier_internal_reference' => 'Internal reference is ":value"', + 'search_modifier_description_starts' => 'Description is ":value"', + 'search_modifier_description_ends' => 'Description ends with ":value"', + 'search_modifier_description_contains' => 'Description contains ":value"', + 'search_modifier_description_is' => 'Description is exactly ":value"', + 'search_modifier_currency_is' => 'Transaction (foreign) currency is ":value"', + 'search_modifier_foreign_currency_is' => 'Transaction foreign currency is ":value"', + 'search_modifier_has_attachments' => 'The transaction must have an attachment', + 'search_modifier_has_no_category' => 'The transaction must have no category', + 'search_modifier_has_any_category' => 'The transaction must have a (any) category', + 'search_modifier_has_no_budget' => 'The transaction must have no budget', + 'search_modifier_has_any_budget' => 'The transaction must have a (any) budget', + 'search_modifier_has_no_tag' => 'The transaction must have no tags', + 'search_modifier_has_any_tag' => 'The transaction must have a (any) tag', + 'search_modifier_notes_contain' => 'The transaction notes contain ":value"', + 'search_modifier_notes_start' => 'The transaction notes start with ":value"', + 'search_modifier_notes_end' => 'The transaction notes end with ":value"', + 'search_modifier_notes_are' => 'The transaction notes are exactly ":value"', + 'search_modifier_no_notes' => 'The transaction has no notes', + 'search_modifier_any_notes' => 'The transaction must have notes', + 'search_modifier_amount_exactly' => 'Amount is exactly :value', + 'search_modifier_amount_less' => 'Amount is less than or equal to :value', + 'search_modifier_amount_more' => 'Amount is more than or equal to :value', + 'search_modifier_source_account_is' => 'Source account name is exactly ":value"', + 'search_modifier_source_account_contains' => 'Source account name contains ":value"', + 'search_modifier_source_account_starts' => 'Source account name starts with ":value"', + 'search_modifier_source_account_ends' => 'Source account name ends with ":value"', + 'search_modifier_source_account_id' => 'Source account ID is :value', + 'search_modifier_source_account_nr_is' => 'Source account number (IBAN) is ":value"', + 'search_modifier_source_account_nr_contains' => 'Source account number (IBAN) contains ":value"', + 'search_modifier_source_account_nr_starts' => 'Source account number (IBAN) starts with ":value"', + 'search_modifier_source_account_nr_ends' => 'Source account number (IBAN) ends with ":value"', + 'search_modifier_destination_account_is' => 'Destination account name is exactly ":value"', + 'search_modifier_destination_account_contains' => 'Destination account name contains ":value"', + 'search_modifier_destination_account_starts' => 'Destination account name starts with ":value"', + 'search_modifier_destination_account_ends' => 'Destination account name ends with ":value"', + 'search_modifier_destination_account_id' => 'Destination account ID is :value', + 'search_modifier_destination_account_nr_is' => 'Destination account number (IBAN) is ":value"', + 'search_modifier_destination_account_nr_contains' => 'Destination account number (IBAN) contains ":value"', + 'search_modifier_destination_account_nr_starts' => 'Destination account number (IBAN) starts with ":value"', + 'search_modifier_destination_account_nr_ends' => 'Destination account number (IBAN) ends with ":value"', + 'search_modifier_account_id' => 'Source or destination account ID\'s is/are: :value', + 'search_modifier_category_is' => 'Category is ":value"', + 'search_modifier_budget_is' => 'Budget is ":value"', + 'search_modifier_bill_is' => 'Bill is ":value"', + 'search_modifier_transaction_type' => 'Transaction type is ":value"', + 'search_modifier_tag_is' => 'Tag is ":value"', + + // END + 'modifiers_applies_are' => 'The following modifiers are applied to the search as well:', + 'general_search_error' => 'An error occurred while searching. Please check the log files for more information.', + 'search_box' => 'Search', + 'search_box_intro' => 'Welcome to the search function of Firefly III. Enter your search query in the box. Make sure you check out the help file because the search is pretty advanced.', + 'search_error' => 'Error while searching', + 'search_searching' => 'Searching ...', + 'search_results' => 'Search results', // repeat frequencies: - 'repeat_freq_yearly' => 'yearly', - 'repeat_freq_half-year' => 'every half-year', - 'repeat_freq_quarterly' => 'quarterly', - 'repeat_freq_monthly' => 'monthly', - 'repeat_freq_weekly' => 'weekly', - 'weekly' => 'weekly', - 'quarterly' => 'quarterly', - 'half-year' => 'every half year', - 'yearly' => 'yearly', + 'repeat_freq_yearly' => 'yearly', + 'repeat_freq_half-year' => 'every half-year', + 'repeat_freq_quarterly' => 'quarterly', + 'repeat_freq_monthly' => 'monthly', + 'repeat_freq_weekly' => 'weekly', + 'weekly' => 'weekly', + 'quarterly' => 'quarterly', + 'half-year' => 'every half year', + 'yearly' => 'yearly', // rules - 'cannot_fire_inactive_rules' => 'You cannot execute inactive rules.', - 'rules' => 'Rules', - 'rule_name' => 'Name of rule', - 'rule_triggers' => 'Rule triggers when', - 'rule_actions' => 'Rule will', - 'new_rule' => 'New rule', - 'new_rule_group' => 'New rule group', - 'rule_priority_up' => 'Give rule more priority', - 'rule_priority_down' => 'Give rule less priority', - 'make_new_rule_group' => 'Make new rule group', - 'store_new_rule_group' => 'Store new rule group', - 'created_new_rule_group' => 'New rule group ":title" stored!', - 'updated_rule_group' => 'Successfully updated rule group ":title".', - 'edit_rule_group' => 'Edit rule group ":title"', - 'duplicate_rule' => 'Duplicate rule ":title"', - 'rule_copy_of' => 'Copy of ":title"', - 'duplicated_rule' => 'Duplicated rule ":title" into ":newTitle"', - 'delete_rule_group' => 'Delete rule group ":title"', - 'deleted_rule_group' => 'Deleted rule group ":title"', - 'update_rule_group' => 'Update rule group', - 'no_rules_in_group' => 'There are no rules in this group', - 'move_rule_group_up' => 'Move rule group up', - 'move_rule_group_down' => 'Move rule group down', - 'save_rules_by_moving' => 'Save this rule by moving it to another rule group:|Save these rules by moving them to another rule group:', - 'make_new_rule' => 'Make a new rule in rule group ":title"', - 'make_new_rule_no_group' => 'Make a new rule', - 'instructions_rule_from_bill' => 'In order to match transactions to your new bill ":name", Firefly III can create a rule that will automatically be checked against any transactions you store. Please verify the details below and store the rule to have Firefly III automatically match transactions to your new bill.', - 'instructions_rule_from_journal' => 'Create a rule based on one of your transactions. Complement or submit the form below.', - 'rule_is_strict' => 'strict rule', - 'rule_is_not_strict' => 'non-strict rule', - 'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.', - 'rule_help_strict' => 'In strict rules ALL triggers must fire for the action(s) to be executed. In non-strict rules, ANY trigger is enough for the action(s) to be executed.', - 'rule_help_active' => 'Inactive rules will never fire.', - 'stored_new_rule' => 'Stored new rule with title ":title"', - 'deleted_rule' => 'Deleted rule with title ":title"', - 'store_new_rule' => 'Store new rule', - 'updated_rule' => 'Updated rule with title ":title"', - 'default_rule_group_name' => 'Default rules', - 'default_rule_group_description' => 'All your rules not in a particular group.', - 'default_rule_name' => 'Your first default rule', - 'default_rule_description' => 'This rule is an example. You can safely delete it.', - 'default_rule_trigger_description' => 'The Man Who Sold the World', - 'default_rule_trigger_from_account' => 'David Bowie', - 'default_rule_action_prepend' => 'Bought the world from ', - 'default_rule_action_set_category' => 'Large expenses', - 'trigger' => 'Trigger', - 'trigger_value' => 'Trigger on value', - 'stop_processing_other_triggers' => 'Stop processing other triggers', - 'add_rule_trigger' => 'Add new trigger', - 'action' => 'Action', - 'action_value' => 'Action value', - 'stop_executing_other_actions' => 'Stop executing other actions', - 'add_rule_action' => 'Add new action', - 'edit_rule' => 'Edit rule ":title"', - 'delete_rule' => 'Delete rule ":title"', - 'update_rule' => 'Update rule', - 'test_rule_triggers' => 'See matching transactions', - 'warning_transaction_subset' => 'For performance reasons this list is limited to :max_num_transactions and may only show a subset of matching transactions', - 'warning_no_matching_transactions' => 'No matching transactions found. Please note that for performance reasons, only the last :num_transactions transactions have been checked.', - 'warning_no_valid_triggers' => 'No valid triggers provided.', - 'apply_rule_selection' => 'Apply rule ":title" to a selection of your transactions', - 'apply_rule_selection_intro' => 'Rules like ":title" are normally only applied to new or updated transactions, but you can tell Firefly III to run it on a selection of your existing transactions. This can be useful when you have updated a rule and you need the changes to be applied to all of your other transactions.', - 'include_transactions_from_accounts' => 'Include transactions from these accounts', - 'applied_rule_selection' => 'Rule ":title" has been applied to your selection.', - 'execute' => 'Execute', - 'apply_rule_group_selection' => 'Apply rule group ":title" to a selection of your transactions', - 'apply_rule_group_selection_intro' => 'Rule groups like ":title" are normally only applied to new or updated transactions, but you can tell Firefly III to run all the rules in this group on a selection of your existing transactions. This can be useful when you have updated a group of rules and you need the changes to be applied to all of your other transactions.', - 'applied_rule_group_selection' => 'Rule group ":title" has been applied to your selection.', + 'cannot_fire_inactive_rules' => 'You cannot execute inactive rules.', + 'rules' => 'Rules', + 'rule_name' => 'Name of rule', + 'rule_triggers' => 'Rule triggers when', + 'rule_actions' => 'Rule will', + 'new_rule' => 'New rule', + 'new_rule_group' => 'New rule group', + 'rule_priority_up' => 'Give rule more priority', + 'rule_priority_down' => 'Give rule less priority', + 'make_new_rule_group' => 'Make new rule group', + 'store_new_rule_group' => 'Store new rule group', + 'created_new_rule_group' => 'New rule group ":title" stored!', + 'updated_rule_group' => 'Successfully updated rule group ":title".', + 'edit_rule_group' => 'Edit rule group ":title"', + 'duplicate_rule' => 'Duplicate rule ":title"', + 'rule_copy_of' => 'Copy of ":title"', + 'duplicated_rule' => 'Duplicated rule ":title" into ":newTitle"', + 'delete_rule_group' => 'Delete rule group ":title"', + 'deleted_rule_group' => 'Deleted rule group ":title"', + 'update_rule_group' => 'Update rule group', + 'no_rules_in_group' => 'There are no rules in this group', + 'move_rule_group_up' => 'Move rule group up', + 'move_rule_group_down' => 'Move rule group down', + 'save_rules_by_moving' => 'Save this rule by moving it to another rule group:|Save these rules by moving them to another rule group:', + 'make_new_rule' => 'Make a new rule in rule group ":title"', + 'make_new_rule_no_group' => 'Make a new rule', + 'instructions_rule_from_bill' => 'In order to match transactions to your new bill ":name", Firefly III can create a rule that will automatically be checked against any transactions you store. Please verify the details below and store the rule to have Firefly III automatically match transactions to your new bill.', + 'instructions_rule_from_journal' => 'Create a rule based on one of your transactions. Complement or submit the form below.', + 'rule_is_strict' => 'strict rule', + 'rule_is_not_strict' => 'non-strict rule', + 'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.', + 'rule_help_strict' => 'In strict rules ALL triggers must fire for the action(s) to be executed. In non-strict rules, ANY trigger is enough for the action(s) to be executed.', + 'rule_help_active' => 'Inactive rules will never fire.', + 'stored_new_rule' => 'Stored new rule with title ":title"', + 'deleted_rule' => 'Deleted rule with title ":title"', + 'store_new_rule' => 'Store new rule', + 'updated_rule' => 'Updated rule with title ":title"', + 'default_rule_group_name' => 'Default rules', + 'default_rule_group_description' => 'All your rules not in a particular group.', + 'default_rule_name' => 'Your first default rule', + 'default_rule_description' => 'This rule is an example. You can safely delete it.', + 'default_rule_trigger_description' => 'The Man Who Sold the World', + 'default_rule_trigger_from_account' => 'David Bowie', + 'default_rule_action_prepend' => 'Bought the world from ', + 'default_rule_action_set_category' => 'Large expenses', + 'trigger' => 'Trigger', + 'trigger_value' => 'Trigger on value', + 'stop_processing_other_triggers' => 'Stop processing other triggers', + 'add_rule_trigger' => 'Add new trigger', + 'action' => 'Action', + 'action_value' => 'Action value', + 'stop_executing_other_actions' => 'Stop executing other actions', + 'add_rule_action' => 'Add new action', + 'edit_rule' => 'Edit rule ":title"', + 'delete_rule' => 'Delete rule ":title"', + 'update_rule' => 'Update rule', + 'test_rule_triggers' => 'See matching transactions', + 'warning_transaction_subset' => 'For performance reasons this list is limited to :max_num_transactions and may only show a subset of matching transactions', + 'warning_no_matching_transactions' => 'No matching transactions found. Please note that for performance reasons, only the last :num_transactions transactions have been checked.', + 'warning_no_valid_triggers' => 'No valid triggers provided.', + 'apply_rule_selection' => 'Apply rule ":title" to a selection of your transactions', + 'apply_rule_selection_intro' => 'Rules like ":title" are normally only applied to new or updated transactions, but you can tell Firefly III to run it on a selection of your existing transactions. This can be useful when you have updated a rule and you need the changes to be applied to all of your other transactions.', + 'include_transactions_from_accounts' => 'Include transactions from these accounts', + 'applied_rule_selection' => 'Rule ":title" has been applied to your selection.', + 'execute' => 'Execute', + 'apply_rule_group_selection' => 'Apply rule group ":title" to a selection of your transactions', + 'apply_rule_group_selection_intro' => 'Rule groups like ":title" are normally only applied to new or updated transactions, but you can tell Firefly III to run all the rules in this group on a selection of your existing transactions. This can be useful when you have updated a group of rules and you need the changes to be applied to all of your other transactions.', + 'applied_rule_group_selection' => 'Rule group ":title" has been applied to your selection.', // actions and triggers - 'rule_trigger_user_action' => 'User action is ":trigger_value"', + 'rule_trigger_user_action' => 'User action is ":trigger_value"', - 'rule_trigger_from_account_starts_choice' => 'Source account name starts with..', - 'rule_trigger_from_account_starts' => 'Source account name starts with ":trigger_value"', - 'rule_trigger_from_account_ends_choice' => 'Source account name ends with..', - 'rule_trigger_from_account_ends' => 'Source account name ends with ":trigger_value"', - 'rule_trigger_from_account_is_choice' => 'Source account name is..', - 'rule_trigger_from_account_is' => 'Source account name is ":trigger_value"', - 'rule_trigger_from_account_contains_choice' => 'Source account name contains..', - 'rule_trigger_from_account_contains' => 'Source account name contains ":trigger_value"', + 'rule_trigger_from_account_starts_choice' => 'Source account name starts with..', + 'rule_trigger_from_account_starts' => 'Source account name starts with ":trigger_value"', + 'rule_trigger_from_account_ends_choice' => 'Source account name ends with..', + 'rule_trigger_from_account_ends' => 'Source account name ends with ":trigger_value"', + 'rule_trigger_from_account_is_choice' => 'Source account name is..', + 'rule_trigger_from_account_is' => 'Source account name is ":trigger_value"', + 'rule_trigger_from_account_contains_choice' => 'Source account name contains..', + 'rule_trigger_from_account_contains' => 'Source account name contains ":trigger_value"', - 'rule_trigger_from_account_nr_starts_choice' => 'Source account number / IBAN starts with..', - 'rule_trigger_from_account_nr_starts' => 'Source account number / IBAN starts with ":trigger_value"', - 'rule_trigger_from_account_nr_ends_choice' => 'Source account number / IBAN ends with..', - 'rule_trigger_from_account_nr_ends' => 'Source account number / IBAN ends with ":trigger_value"', - 'rule_trigger_from_account_nr_is_choice' => 'Source account number / IBAN is..', - 'rule_trigger_from_account_nr_is' => 'Source account number / IBAN is ":trigger_value"', - 'rule_trigger_from_account_nr_contains_choice' => 'Source account number / IBAN contains..', - 'rule_trigger_from_account_nr_contains' => 'Source account number / IBAN contains ":trigger_value"', + 'rule_trigger_from_account_nr_starts_choice' => 'Source account number / IBAN starts with..', + 'rule_trigger_from_account_nr_starts' => 'Source account number / IBAN starts with ":trigger_value"', + 'rule_trigger_from_account_nr_ends_choice' => 'Source account number / IBAN ends with..', + 'rule_trigger_from_account_nr_ends' => 'Source account number / IBAN ends with ":trigger_value"', + 'rule_trigger_from_account_nr_is_choice' => 'Source account number / IBAN is..', + 'rule_trigger_from_account_nr_is' => 'Source account number / IBAN is ":trigger_value"', + 'rule_trigger_from_account_nr_contains_choice' => 'Source account number / IBAN contains..', + 'rule_trigger_from_account_nr_contains' => 'Source account number / IBAN contains ":trigger_value"', - 'rule_trigger_to_account_starts_choice' => 'Destination account name starts with..', - 'rule_trigger_to_account_starts' => 'Destination account name starts with ":trigger_value"', - 'rule_trigger_to_account_ends_choice' => 'Destination account name ends with..', - 'rule_trigger_to_account_ends' => 'Destination account name ends with ":trigger_value"', - 'rule_trigger_to_account_is_choice' => 'Destination account name is..', - 'rule_trigger_to_account_is' => 'Destination account name is ":trigger_value"', - 'rule_trigger_to_account_contains_choice' => 'Destination account name contains..', - 'rule_trigger_to_account_contains' => 'Destination account name contains ":trigger_value"', + 'rule_trigger_to_account_starts_choice' => 'Destination account name starts with..', + 'rule_trigger_to_account_starts' => 'Destination account name starts with ":trigger_value"', + 'rule_trigger_to_account_ends_choice' => 'Destination account name ends with..', + 'rule_trigger_to_account_ends' => 'Destination account name ends with ":trigger_value"', + 'rule_trigger_to_account_is_choice' => 'Destination account name is..', + 'rule_trigger_to_account_is' => 'Destination account name is ":trigger_value"', + 'rule_trigger_to_account_contains_choice' => 'Destination account name contains..', + 'rule_trigger_to_account_contains' => 'Destination account name contains ":trigger_value"', - 'rule_trigger_to_account_nr_starts_choice' => 'Destination account number / IBAN starts with..', - 'rule_trigger_to_account_nr_starts' => 'Destination account number / IBAN starts with ":trigger_value"', - 'rule_trigger_to_account_nr_ends_choice' => 'Destination account number / IBAN ends with..', - 'rule_trigger_to_account_nr_ends' => 'Destination account number / IBAN ends with ":trigger_value"', - 'rule_trigger_to_account_nr_is_choice' => 'Destination account number / IBAN is..', - 'rule_trigger_to_account_nr_is' => 'Destination account number / IBAN is ":trigger_value"', - 'rule_trigger_to_account_nr_contains_choice' => 'Destination account number / IBAN contains..', - 'rule_trigger_to_account_nr_contains' => 'Destination account number / IBAN contains ":trigger_value"', + 'rule_trigger_to_account_nr_starts_choice' => 'Destination account number / IBAN starts with..', + 'rule_trigger_to_account_nr_starts' => 'Destination account number / IBAN starts with ":trigger_value"', + 'rule_trigger_to_account_nr_ends_choice' => 'Destination account number / IBAN ends with..', + 'rule_trigger_to_account_nr_ends' => 'Destination account number / IBAN ends with ":trigger_value"', + 'rule_trigger_to_account_nr_is_choice' => 'Destination account number / IBAN is..', + 'rule_trigger_to_account_nr_is' => 'Destination account number / IBAN is ":trigger_value"', + 'rule_trigger_to_account_nr_contains_choice' => 'Destination account number / IBAN contains..', + 'rule_trigger_to_account_nr_contains' => 'Destination account number / IBAN contains ":trigger_value"', - 'rule_trigger_transaction_type_choice' => 'Transaction is of type..', - 'rule_trigger_transaction_type' => 'Transaction is of type ":trigger_value"', - 'rule_trigger_category_is_choice' => 'Category is..', - 'rule_trigger_category_is' => 'Category is ":trigger_value"', - 'rule_trigger_amount_less_choice' => 'Amount is less than..', - 'rule_trigger_amount_less' => 'Amount is less than :trigger_value', - 'rule_trigger_amount_exactly_choice' => 'Amount is..', - 'rule_trigger_amount_exactly' => 'Amount is :trigger_value', - 'rule_trigger_amount_more_choice' => 'Amount is more than..', - 'rule_trigger_amount_more' => 'Amount is more than :trigger_value', - 'rule_trigger_description_starts_choice' => 'Description starts with..', - 'rule_trigger_description_starts' => 'Description starts with ":trigger_value"', - 'rule_trigger_description_ends_choice' => 'Description ends with..', - 'rule_trigger_description_ends' => 'Description ends with ":trigger_value"', - 'rule_trigger_description_contains_choice' => 'Description contains..', - 'rule_trigger_description_contains' => 'Description contains ":trigger_value"', - 'rule_trigger_description_is_choice' => 'Description is..', - 'rule_trigger_description_is' => 'Description is ":trigger_value"', + 'rule_trigger_transaction_type_choice' => 'Transaction is of type..', + 'rule_trigger_transaction_type' => 'Transaction is of type ":trigger_value"', + 'rule_trigger_category_is_choice' => 'Category is..', + 'rule_trigger_category_is' => 'Category is ":trigger_value"', + 'rule_trigger_amount_less_choice' => 'Amount is less than..', + 'rule_trigger_amount_less' => 'Amount is less than :trigger_value', + 'rule_trigger_amount_exactly_choice' => 'Amount is..', + 'rule_trigger_amount_exactly' => 'Amount is :trigger_value', + 'rule_trigger_amount_more_choice' => 'Amount is more than..', + 'rule_trigger_amount_more' => 'Amount is more than :trigger_value', + 'rule_trigger_description_starts_choice' => 'Description starts with..', + 'rule_trigger_description_starts' => 'Description starts with ":trigger_value"', + 'rule_trigger_description_ends_choice' => 'Description ends with..', + 'rule_trigger_description_ends' => 'Description ends with ":trigger_value"', + 'rule_trigger_description_contains_choice' => 'Description contains..', + 'rule_trigger_description_contains' => 'Description contains ":trigger_value"', + 'rule_trigger_description_is_choice' => 'Description is..', + 'rule_trigger_description_is' => 'Description is ":trigger_value"', - 'rule_trigger_date_is_choice' => 'Transaction date is..', - 'rule_trigger_date_is' => 'Transaction date is ":trigger_value"', - 'rule_trigger_date_before_choice' => 'Transaction date is before..', - 'rule_trigger_date_before' => 'Transaction date is before ":trigger_value"', - 'rule_trigger_date_after_choice' => 'Transaction date is after..', - 'rule_trigger_date_after' => 'Transaction date is after ":trigger_value"', + 'rule_trigger_date_is_choice' => 'Transaction date is..', + 'rule_trigger_date_is' => 'Transaction date is ":trigger_value"', + 'rule_trigger_date_before_choice' => 'Transaction date is before..', + 'rule_trigger_date_before' => 'Transaction date is before ":trigger_value"', + 'rule_trigger_date_after_choice' => 'Transaction date is after..', + 'rule_trigger_date_after' => 'Transaction date is after ":trigger_value"', 'rule_trigger_budget_is_choice' => 'Budget is..', 'rule_trigger_budget_is' => 'Budget is ":trigger_value"', @@ -536,26 +566,26 @@ return [ 'new_rule_for_bill_title' => 'Rule for bill ":name"', 'new_rule_for_bill_description' => 'This rule marks transactions for bill ":name".', - 'new_rule_for_journal_title' => 'Rule based on transaction ":description"', - 'new_rule_for_journal_description' => 'This rule is based on transaction ":description". It will match transactions that are exactly the same.', + 'new_rule_for_journal_title' => 'Rule based on transaction ":description"', + 'new_rule_for_journal_description' => 'This rule is based on transaction ":description". It will match transactions that are exactly the same.', // tags - 'store_new_tag' => 'Store new tag', - 'update_tag' => 'Update tag', - 'no_location_set' => 'No location set.', - 'meta_data' => 'Meta data', - 'location' => 'Location', - 'without_date' => 'Without date', - 'result' => 'Result', - 'sums_apply_to_range' => 'All sums apply to the selected range', - 'mapbox_api_key' => 'To use map, get an API key from Mapbox. Open your .env file and enter this code after MAPBOX_API_KEY=.', - 'press_object_location' => 'Right click or long press to set the object\'s location.', - 'clear_location' => 'Clear location', - 'delete_all_selected_tags' => 'Delete all selected tags', - 'select_tags_to_delete' => 'Don\'t forget to select some tags.', - 'deleted_x_tags' => 'Deleted :count tag.|Deleted :count tags.', - 'create_rule_from_transaction' => 'Create rule based on transaction', - 'create_recurring_from_transaction' => 'Create recurring transaction based on transaction', + 'store_new_tag' => 'Store new tag', + 'update_tag' => 'Update tag', + 'no_location_set' => 'No location set.', + 'meta_data' => 'Meta data', + 'location' => 'Location', + 'without_date' => 'Without date', + 'result' => 'Result', + 'sums_apply_to_range' => 'All sums apply to the selected range', + 'mapbox_api_key' => 'To use map, get an API key from Mapbox. Open your .env file and enter this code after MAPBOX_API_KEY=.', + 'press_object_location' => 'Right click or long press to set the object\'s location.', + 'clear_location' => 'Clear location', + 'delete_all_selected_tags' => 'Delete all selected tags', + 'select_tags_to_delete' => 'Don\'t forget to select some tags.', + 'deleted_x_tags' => 'Deleted :count tag.|Deleted :count tags.', + 'create_rule_from_transaction' => 'Create rule based on transaction', + 'create_recurring_from_transaction' => 'Create recurring transaction based on transaction', // preferences @@ -606,86 +636,86 @@ return [ 'list_page_size_help' => 'Any list of things (accounts, transactions, etc) shows at most this many per page.', 'list_page_size_label' => 'Page size', 'between_dates' => '(:start and :end)', - 'pref_optional_fields_transaction' => 'Optional fields for transactions', - 'pref_optional_fields_transaction_help' => 'By default not all fields are enabled when creating a new transaction (because of the clutter). Below, you can enable these fields if you think they could be useful for you. Of course, any field that is disabled, but already filled in, will be visible regardless of the setting.', - 'optional_tj_date_fields' => 'Date fields', - 'optional_tj_business_fields' => 'Business fields', - 'optional_tj_attachment_fields' => 'Attachment fields', - 'pref_optional_tj_interest_date' => 'Interest date', - 'pref_optional_tj_book_date' => 'Book date', - 'pref_optional_tj_process_date' => 'Processing date', - 'pref_optional_tj_due_date' => 'Due date', - 'pref_optional_tj_payment_date' => 'Payment date', - 'pref_optional_tj_invoice_date' => 'Invoice date', - 'pref_optional_tj_internal_reference' => 'Internal reference', - 'pref_optional_tj_notes' => 'Notes', - 'pref_optional_tj_attachments' => 'Attachments', - 'pref_optional_tj_external_uri' => 'External URI', - 'optional_field_meta_dates' => 'Dates', - 'optional_field_meta_business' => 'Business', - 'optional_field_attachments' => 'Attachments', - 'optional_field_meta_data' => 'Optional meta data', - 'external_uri' => 'External URI', + 'pref_optional_fields_transaction' => 'Optional fields for transactions', + 'pref_optional_fields_transaction_help' => 'By default not all fields are enabled when creating a new transaction (because of the clutter). Below, you can enable these fields if you think they could be useful for you. Of course, any field that is disabled, but already filled in, will be visible regardless of the setting.', + 'optional_tj_date_fields' => 'Date fields', + 'optional_tj_business_fields' => 'Business fields', + 'optional_tj_attachment_fields' => 'Attachment fields', + 'pref_optional_tj_interest_date' => 'Interest date', + 'pref_optional_tj_book_date' => 'Book date', + 'pref_optional_tj_process_date' => 'Processing date', + 'pref_optional_tj_due_date' => 'Due date', + 'pref_optional_tj_payment_date' => 'Payment date', + 'pref_optional_tj_invoice_date' => 'Invoice date', + 'pref_optional_tj_internal_reference' => 'Internal reference', + 'pref_optional_tj_notes' => 'Notes', + 'pref_optional_tj_attachments' => 'Attachments', + 'pref_optional_tj_external_uri' => 'External URI', + 'optional_field_meta_dates' => 'Dates', + 'optional_field_meta_business' => 'Business', + 'optional_field_attachments' => 'Attachments', + 'optional_field_meta_data' => 'Optional meta data', + 'external_uri' => 'External URI', // profile: - 'delete_stuff_header' => 'Delete data from Firefly III', - 'permanent_delete_stuff' => 'Be careful with these buttons. Deleting stuff is permanent.', - 'other_sessions_logged_out' => 'All your other sessions have been logged out.', - 'delete_all_budgets' => 'Delete ALL your budgets', - 'delete_all_categories' => 'Delete ALL your categories', - 'delete_all_tags' => 'Delete ALL your tags', - 'delete_all_bills' => 'Delete ALL your bills', - 'delete_all_piggy_banks' => 'Delete ALL your piggy banks', - 'delete_all_rules' => 'Delete ALL your rules', - 'delete_all_recurring' => 'Delete ALL your recurring transactions', - 'delete_all_object_groups' => 'Delete ALL your object groups', - 'delete_all_accounts' => 'Delete ALL your accounts', - 'delete_all_asset_accounts' => 'Delete ALL your asset accounts', - 'delete_all_expense_accounts' => 'Delete ALL your expense accounts', - 'delete_all_revenue_accounts' => 'Delete ALL your revenue accounts', - 'delete_all_liabilities' => 'Delete ALL your liabilities', - 'delete_all_transactions' => 'Delete ALL your transactions', - 'delete_all_withdrawals' => 'Delete ALL your withdrawals', - 'delete_all_deposits' => 'Delete ALL your deposits', - 'delete_all_transfers' => 'Delete ALL your transfers', - 'also_delete_transactions' => 'Deleting accounts will also delete ALL associated withdrawals, deposits and transfers!', - 'deleted_all_budgets' => 'All budgets have been deleted', - 'deleted_all_categories' => 'All categories have been deleted', - 'deleted_all_tags' => 'All tags have been deleted', - 'deleted_all_bills' => 'All bills have been deleted', - 'deleted_all_piggy_banks' => 'All piggy banks have been deleted', - 'deleted_all_rules' => 'All rules and rule groups have been deleted', - 'deleted_all_object_groups' => 'All groups have been deleted', - 'deleted_all_accounts' => 'All accounts have been deleted', - 'deleted_all_asset_accounts' => 'All asset accounts have been deleted', - 'deleted_all_expense_accounts' => 'All expense accounts have been deleted', - 'deleted_all_revenue_accounts' => 'All revenue accounts have been deleted', - 'deleted_all_liabilities' => 'All liabilities have been deleted', - 'deleted_all_transactions' => 'All transactions have been deleted', - 'deleted_all_withdrawals' => 'All withdrawals have been deleted', - 'deleted_all_deposits' => 'All deposits have been deleted', - 'deleted_all_transfers' => 'All transfers have been deleted', - 'deleted_all_recurring' => 'All recurring transactions have been deleted', - 'change_your_password' => 'Change your password', - 'delete_account' => 'Delete account', - 'current_password' => 'Current password', - 'new_password' => 'New password', - 'new_password_again' => 'New password (again)', - 'delete_your_account' => 'Delete your account', - 'delete_your_account_help' => 'Deleting your account will also delete any accounts, transactions, anything you might have saved into Firefly III. It\'ll be GONE.', - 'delete_your_account_password' => 'Enter your password to continue.', - 'password' => 'Password', - 'are_you_sure' => 'Are you sure? You cannot undo this.', - 'delete_account_button' => 'DELETE your account', - 'invalid_current_password' => 'Invalid current password!', - 'password_changed' => 'Password changed!', - 'should_change' => 'The idea is to change your password.', - 'invalid_password' => 'Invalid password!', - 'what_is_pw_security' => 'What is "verify password security"?', - 'secure_pw_title' => 'How to choose a secure password', - 'secure_pw_history' => 'Not a week goes by that you read in the news about a site losing the passwords of its users. Hackers and thieves use these passwords to try to steal your private information. This information is valuable.', - 'secure_pw_ff' => 'Do you use the same password all over the internet? If one site loses your password, hackers have access to all your data. Firefly III relies on you to choose a strong and unique password to protect your financial records.', - 'secure_pw_check_box' => 'To help you do that Firefly III can check if the password you want to use has been stolen in the past. If this is the case, Firefly III advises you NOT to use that password.', + 'delete_stuff_header' => 'Delete data from Firefly III', + 'permanent_delete_stuff' => 'Be careful with these buttons. Deleting stuff is permanent.', + 'other_sessions_logged_out' => 'All your other sessions have been logged out.', + 'delete_all_budgets' => 'Delete ALL your budgets', + 'delete_all_categories' => 'Delete ALL your categories', + 'delete_all_tags' => 'Delete ALL your tags', + 'delete_all_bills' => 'Delete ALL your bills', + 'delete_all_piggy_banks' => 'Delete ALL your piggy banks', + 'delete_all_rules' => 'Delete ALL your rules', + 'delete_all_recurring' => 'Delete ALL your recurring transactions', + 'delete_all_object_groups' => 'Delete ALL your object groups', + 'delete_all_accounts' => 'Delete ALL your accounts', + 'delete_all_asset_accounts' => 'Delete ALL your asset accounts', + 'delete_all_expense_accounts' => 'Delete ALL your expense accounts', + 'delete_all_revenue_accounts' => 'Delete ALL your revenue accounts', + 'delete_all_liabilities' => 'Delete ALL your liabilities', + 'delete_all_transactions' => 'Delete ALL your transactions', + 'delete_all_withdrawals' => 'Delete ALL your withdrawals', + 'delete_all_deposits' => 'Delete ALL your deposits', + 'delete_all_transfers' => 'Delete ALL your transfers', + 'also_delete_transactions' => 'Deleting accounts will also delete ALL associated withdrawals, deposits and transfers!', + 'deleted_all_budgets' => 'All budgets have been deleted', + 'deleted_all_categories' => 'All categories have been deleted', + 'deleted_all_tags' => 'All tags have been deleted', + 'deleted_all_bills' => 'All bills have been deleted', + 'deleted_all_piggy_banks' => 'All piggy banks have been deleted', + 'deleted_all_rules' => 'All rules and rule groups have been deleted', + 'deleted_all_object_groups' => 'All groups have been deleted', + 'deleted_all_accounts' => 'All accounts have been deleted', + 'deleted_all_asset_accounts' => 'All asset accounts have been deleted', + 'deleted_all_expense_accounts' => 'All expense accounts have been deleted', + 'deleted_all_revenue_accounts' => 'All revenue accounts have been deleted', + 'deleted_all_liabilities' => 'All liabilities have been deleted', + 'deleted_all_transactions' => 'All transactions have been deleted', + 'deleted_all_withdrawals' => 'All withdrawals have been deleted', + 'deleted_all_deposits' => 'All deposits have been deleted', + 'deleted_all_transfers' => 'All transfers have been deleted', + 'deleted_all_recurring' => 'All recurring transactions have been deleted', + 'change_your_password' => 'Change your password', + 'delete_account' => 'Delete account', + 'current_password' => 'Current password', + 'new_password' => 'New password', + 'new_password_again' => 'New password (again)', + 'delete_your_account' => 'Delete your account', + 'delete_your_account_help' => 'Deleting your account will also delete any accounts, transactions, anything you might have saved into Firefly III. It\'ll be GONE.', + 'delete_your_account_password' => 'Enter your password to continue.', + 'password' => 'Password', + 'are_you_sure' => 'Are you sure? You cannot undo this.', + 'delete_account_button' => 'DELETE your account', + 'invalid_current_password' => 'Invalid current password!', + 'password_changed' => 'Password changed!', + 'should_change' => 'The idea is to change your password.', + 'invalid_password' => 'Invalid password!', + 'what_is_pw_security' => 'What is "verify password security"?', + 'secure_pw_title' => 'How to choose a secure password', + 'secure_pw_history' => 'Not a week goes by that you read in the news about a site losing the passwords of its users. Hackers and thieves use these passwords to try to steal your private information. This information is valuable.', + 'secure_pw_ff' => 'Do you use the same password all over the internet? If one site loses your password, hackers have access to all your data. Firefly III relies on you to choose a strong and unique password to protect your financial records.', + 'secure_pw_check_box' => 'To help you do that Firefly III can check if the password you want to use has been stolen in the past. If this is the case, Firefly III advises you NOT to use that password.', 'secure_pw_working_title' => 'How does it work?', 'secure_pw_working' => 'By checking the box, Firefly III will send the first five characters of the SHA1 hash of your password to the website of Troy Hunt to see if it is on the list. This will stop you from using unsafe passwords as is recommended in the latest NIST Special Publication on this subject.', 'secure_pw_should' => 'Should I check the box?', @@ -1753,7 +1783,6 @@ return [ 'object_group' => 'Group', - // ]; diff --git a/tests/Unit/Support/Search/OperatorQuerySearchTest.php b/tests/Unit/Support/Search/OperatorQuerySearchTest.php index 7b5fa8152b..b13d008faf 100644 --- a/tests/Unit/Support/Search/OperatorQuerySearchTest.php +++ b/tests/Unit/Support/Search/OperatorQuerySearchTest.php @@ -483,7 +483,7 @@ class OperatorQuerySearchTest extends TestCase $this->assertCount(0, $object->getWords()); // operator is assumed to be included. - $this->assertCount(1, $object->getOperators()); + $this->assertCount(2, $object->getOperators()); $result = ['transactions' => []]; // execute search should work: