formatted source code
This commit is contained in:
parent
6f221c681c
commit
8f17a6aa49
|
@ -41,6 +41,7 @@ public final class Logging {
|
|||
|
||||
/**
|
||||
* Add a console log handler writing to the applications stderr
|
||||
*
|
||||
* @param level the level to set the handler to
|
||||
*/
|
||||
private static void createConsoleLogger(Level level) {
|
||||
|
@ -54,6 +55,7 @@ public final class Logging {
|
|||
* The file will be located in a folder directly besides the application
|
||||
* with the name LOG_FOLDER_NAME. The name will follow the format DATE_TIME_FORMAT plus "log" as
|
||||
* file extension.
|
||||
*
|
||||
* @param level the log level to set the handler to
|
||||
*/
|
||||
private static void createFileLogger(Level level) {
|
||||
|
|
|
@ -8,7 +8,7 @@ import me.teridax.jcash.lang.Locales;
|
|||
|
||||
import javax.swing.*;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static javax.swing.JOptionPane.ERROR_MESSAGE;
|
||||
import static javax.swing.JOptionPane.showMessageDialog;
|
||||
|
@ -87,6 +87,7 @@ public final class Main {
|
|||
|
||||
/**
|
||||
* Get the main singleton instance of this program
|
||||
*
|
||||
* @return the singleton instance of this class
|
||||
*/
|
||||
public static Main getInstance() {
|
||||
|
@ -96,6 +97,7 @@ public final class Main {
|
|||
/**
|
||||
* Attempts to create a new instance of the singleton class Main.
|
||||
* This method throws an exception if the class is already instantiated.
|
||||
*
|
||||
* @throws IllegalStateException if the class is already instantiated
|
||||
*/
|
||||
public static void instance() {
|
||||
|
|
|
@ -34,6 +34,23 @@ public final class Bank {
|
|||
this.accounts = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given BLZ. If the blz is not valid an exception is thrown.
|
||||
*
|
||||
* @param maybeBlz a string to be tested for being a blz
|
||||
* @return returns the correctly formatted blz
|
||||
* @throws IllegalArgumentException if the supplied argument is not a valid blz
|
||||
*/
|
||||
public static String validateBlz(String maybeBlz) throws IllegalArgumentException {
|
||||
var pattern = Pattern.compile("[\\w-_]+");
|
||||
var matcher = pattern.matcher(maybeBlz);
|
||||
|
||||
if (matcher.find())
|
||||
return matcher.group();
|
||||
|
||||
throw new IllegalArgumentException("not a valid BLZ: " + maybeBlz);
|
||||
}
|
||||
|
||||
public String getBlz() {
|
||||
return blz;
|
||||
}
|
||||
|
@ -46,7 +63,8 @@ public final class Bank {
|
|||
* Add a new account and its associated owner to this bank.
|
||||
* If the owner is already present the account is added to the existing owner.
|
||||
* If the account is already present for the owner the old account gets overwritten.
|
||||
* @param owner the owner of the account
|
||||
*
|
||||
* @param owner the owner of the account
|
||||
* @param account the account of the owner
|
||||
*/
|
||||
public void addAccount(Owner owner, Account account) {
|
||||
|
@ -70,6 +88,7 @@ public final class Bank {
|
|||
|
||||
/**
|
||||
* Retrieve all accounts owned by the specific owner.
|
||||
*
|
||||
* @param owner the owner for which accounts are to be retrieved
|
||||
* @return all accounts owned by the owner and managed by this bank
|
||||
*/
|
||||
|
@ -77,24 +96,9 @@ public final class Bank {
|
|||
return accounts.get(owner).toArray(Account[]::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given BLZ. If the blz is not valid an exception is thrown.
|
||||
* @param maybeBlz a string to be tested for being a blz
|
||||
* @return returns the correctly formatted blz
|
||||
* @throws IllegalArgumentException if the supplied argument is not a valid blz
|
||||
*/
|
||||
public static String validateBlz(String maybeBlz) throws IllegalArgumentException {
|
||||
var pattern = Pattern.compile("[\\w-_]+");
|
||||
var matcher = pattern.matcher(maybeBlz);
|
||||
|
||||
if (matcher.find())
|
||||
return matcher.group();
|
||||
|
||||
throw new IllegalArgumentException("not a valid BLZ: " + maybeBlz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a profile of the specified international bank account number.
|
||||
*
|
||||
* @param iban the number to create a profile for
|
||||
* @return the profile for the iban account
|
||||
*/
|
||||
|
|
|
@ -34,10 +34,11 @@ public abstract class Account {
|
|||
/**
|
||||
* Parses a row of a fixed amount of columns into an account.
|
||||
* This function will attempt to create an instance of two classes which inherit from Account.
|
||||
*
|
||||
* @param columns array of 6 strings
|
||||
* @return either an instance of {@link SavingsAccount} or an instance of {@link CurrentAccount}
|
||||
* @throws IllegalArgumentException if the account type cannot be determined or the provided data is invalid
|
||||
* @throws NullPointerException if columns is null
|
||||
* @throws NullPointerException if columns is null
|
||||
*/
|
||||
public static Account fromColumns(String[] columns) throws IllegalArgumentException, NullPointerException {
|
||||
Objects.requireNonNull(columns);
|
||||
|
@ -90,13 +91,14 @@ public abstract class Account {
|
|||
/**
|
||||
* Returns true if the parameter is an instance of class {@link Account} and both their
|
||||
* ibans are equal.
|
||||
*
|
||||
* @param obj the obj to compare to
|
||||
* @return true if the parameter is an instance of class {@link Account} and their ibans match
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof Account)
|
||||
return iban == ((Account)obj).iban;
|
||||
return iban == ((Account) obj).iban;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -105,6 +107,7 @@ public abstract class Account {
|
|||
* Returns a description of the account in form a string.
|
||||
* This method is not equal to {@link #toString()} but is intended to be
|
||||
* a method used to retrieve a formatted representation for guis.
|
||||
*
|
||||
* @return a basic description of the account in form a string
|
||||
*/
|
||||
public String getDescription() {
|
||||
|
@ -113,6 +116,7 @@ public abstract class Account {
|
|||
|
||||
/**
|
||||
* Add a non-negative value onto the balance of this account.
|
||||
*
|
||||
* @param amount the amount of value to add
|
||||
* @throws IllegalArgumentException if amount is negative
|
||||
*/
|
||||
|
@ -133,6 +137,7 @@ public abstract class Account {
|
|||
/**
|
||||
* Takeoff a certain amount of money from this accounts balance.
|
||||
* Saturates the result if the value to subtract is greater than the balance present.
|
||||
*
|
||||
* @param amount the amount of money to subtract from the accounts balance
|
||||
* @throws IllegalArgumentException if amount is greater than the balance present
|
||||
*/
|
||||
|
|
|
@ -25,6 +25,7 @@ public final class CurrentAccount extends Account {
|
|||
/**
|
||||
* Takeoff a certain amount of money from this accounts balance.
|
||||
* Saturates the result if the value to subtract is greater than the balance present.
|
||||
*
|
||||
* @param amount the amount of money to subtract from the accounts balance
|
||||
* @throws IllegalArgumentException if amount is smaller than 0 or the overflow is greater than the overdraft.
|
||||
*/
|
||||
|
|
|
@ -34,10 +34,11 @@ public final class Owner {
|
|||
|
||||
/**
|
||||
* Create a new instance of this class parsed from the columns.
|
||||
*
|
||||
* @param columns the fields of this class as strings
|
||||
* @return an instance of this class
|
||||
* @throws IllegalArgumentException if the supplied columns is invalid
|
||||
* @throws NullPointerException if columns is null
|
||||
* @throws NullPointerException if columns is null
|
||||
*/
|
||||
public static Owner fromColumns(String... columns) throws IllegalArgumentException, NullPointerException {
|
||||
Objects.requireNonNull(columns);
|
||||
|
|
|
@ -36,6 +36,7 @@ public final class BankingManagementSystem {
|
|||
/**
|
||||
* Utility method for retrieving the tail of a string array.
|
||||
* This method return all items which follow the n-th index denoted by index.
|
||||
*
|
||||
* @param array the array to take the tail of
|
||||
* @param index the amount trailing indices to skip
|
||||
* @return an array containing the last elements of the supplied array
|
||||
|
@ -66,9 +67,10 @@ public final class BankingManagementSystem {
|
|||
* </ol>
|
||||
* The file can contain a header line which gets skipped when reading.
|
||||
* Note that the first line is always skipped and the name of every column is not checked in any way.
|
||||
*
|
||||
* @param file the file to parse
|
||||
* @throws IllegalArgumentException if the file cannot be read or the containing data is invalid
|
||||
* @return a valid BMS
|
||||
* @throws IllegalArgumentException if the file cannot be read or the containing data is invalid
|
||||
*/
|
||||
public static BankingManagementSystem loadFromCsv(Path file) throws IllegalArgumentException {
|
||||
LOGGER.fine("parsing banking management system from file: " + Objects.toString(file, "null"));
|
||||
|
@ -123,6 +125,7 @@ public final class BankingManagementSystem {
|
|||
|
||||
/**
|
||||
* Return a bank with the given blz.
|
||||
*
|
||||
* @param blz the blz to search bank of
|
||||
* @return the bank with this blz or none
|
||||
*/
|
||||
|
|
|
@ -23,14 +23,14 @@ public class Profile {
|
|||
* The bank that manages every account referenced by this profile
|
||||
*/
|
||||
private final Bank bank;
|
||||
/**
|
||||
* Primary or currently selected account.
|
||||
*/
|
||||
private Account primaryAccount;
|
||||
/**
|
||||
* All other account registered at a specific bank for the specified owner
|
||||
*/
|
||||
private final Account[] accounts;
|
||||
/**
|
||||
* Primary or currently selected account.
|
||||
*/
|
||||
private Account primaryAccount;
|
||||
|
||||
public Profile(Owner owner, Bank bank, Account account, Account[] accounts) {
|
||||
if (!Arrays.asList(accounts).contains(account)) {
|
||||
|
@ -48,21 +48,10 @@ public class Profile {
|
|||
return primaryAccount;
|
||||
}
|
||||
|
||||
public Owner getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public Bank getBank() {
|
||||
return bank;
|
||||
}
|
||||
|
||||
public Account[] getAccounts() {
|
||||
return accounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the primary account of this profile based on a descriptive text.
|
||||
* This method may not change anything if no account can be found with a matching description
|
||||
*
|
||||
* @param description the description to find a matching account for
|
||||
*/
|
||||
public void setPrimaryAccount(String description) {
|
||||
|
@ -74,4 +63,16 @@ public class Profile {
|
|||
}
|
||||
Logging.LOGGER.warning("Account " + description + " not found in associated account list");
|
||||
}
|
||||
|
||||
public Owner getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public Bank getBank() {
|
||||
return bank;
|
||||
}
|
||||
|
||||
public Account[] getAccounts() {
|
||||
return accounts;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ public class DecodeTests {
|
|||
|
||||
assertEquals(decodeCurrency("1,3€"), 1.3d);
|
||||
assertEquals(decodeCurrency("‘0567€"), 567d);
|
||||
assertEquals(decodeCurrency("145,34"), 145,34d);
|
||||
assertEquals(decodeCurrency("145,34"), 145, 34d);
|
||||
assertEquals(decodeCurrency("0,45 €"), 0.45d);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,10 +21,11 @@ public class StringDecoder {
|
|||
* Attempts to convert the given string into a double value representing a percentage.
|
||||
* The output value will be in the range [0, 100]. Strings formatted without a percentage
|
||||
* symbol will be assumed to be normalized and thus multiplied by 100 to retrieve the result in percent.
|
||||
*
|
||||
* @param number the string to convert
|
||||
* @return the double value
|
||||
* @throws IllegalArgumentException when the format is invalid
|
||||
* @throws NullPointerException when the argument is null
|
||||
* @throws NullPointerException when the argument is null
|
||||
*/
|
||||
public static double decodePercent(String number) throws IllegalArgumentException, NullPointerException {
|
||||
Objects.requireNonNull(number);
|
||||
|
@ -55,10 +56,11 @@ public class StringDecoder {
|
|||
|
||||
/**
|
||||
* Attempts to convert the given string into a currency value.
|
||||
*
|
||||
* @param currency the string to convert
|
||||
* @return the double value
|
||||
* @throws IllegalArgumentException when the format is invalid
|
||||
* @throws NullPointerException when the argument is null
|
||||
* @throws NullPointerException when the argument is null
|
||||
*/
|
||||
public static double decodeCurrency(String currency) throws IllegalArgumentException, NullPointerException {
|
||||
Objects.requireNonNull(currency);
|
||||
|
@ -76,10 +78,11 @@ public class StringDecoder {
|
|||
/**
|
||||
* Attempts to convert the given string into universally unique number.
|
||||
* This function does not check for duplicates. The number must be a positive integer.
|
||||
*
|
||||
* @param number the string to convert
|
||||
* @return the integer serial number
|
||||
* @throws IllegalArgumentException when the format is invalid
|
||||
* @throws NullPointerException when the argument is null
|
||||
* @throws NullPointerException when the argument is null
|
||||
*/
|
||||
public static int decodeUniqueIdentificationNumber(String number) throws IllegalArgumentException, NullPointerException {
|
||||
Objects.requireNonNull(number);
|
||||
|
@ -103,10 +106,11 @@ public class StringDecoder {
|
|||
/**
|
||||
* Attempts to convert the given string into a name.
|
||||
* This method performs validation and trimming.
|
||||
*
|
||||
* @param name the string to convert
|
||||
* @return the qualified name
|
||||
* @throws IllegalArgumentException when the format is invalid
|
||||
* @throws NullPointerException when the argument is null
|
||||
* @throws NullPointerException when the argument is null
|
||||
*/
|
||||
public static String decodeName(String name) throws IllegalArgumentException, NullPointerException {
|
||||
Objects.requireNonNull(name);
|
||||
|
@ -124,10 +128,11 @@ public class StringDecoder {
|
|||
|
||||
/**
|
||||
* Attempts to convert the given string into a street and an optional house address.
|
||||
*
|
||||
* @param street the string to convert
|
||||
* @return the address name
|
||||
* @throws IllegalArgumentException when the format is invalid
|
||||
* @throws NullPointerException when the argument is null
|
||||
* @throws NullPointerException when the argument is null
|
||||
*/
|
||||
public static String decodeStreet(String street) throws IllegalArgumentException, NullPointerException {
|
||||
Objects.requireNonNull(street);
|
||||
|
|
|
@ -20,11 +20,13 @@ public class Loader {
|
|||
*/
|
||||
private static final FileNameExtensionFilter FILE_FILTER = new FileNameExtensionFilter(translate("Comma separated value spreadsheet"), "csv", "CSV");
|
||||
|
||||
private Loader() {}
|
||||
private Loader() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a BMS from a csv file. Opens up a dialog which prompts the user to select a single file.
|
||||
* Once the file is selected this function will try to parse the contents to a BMS and return the instance.
|
||||
*
|
||||
* @return a valid BMS instance loaded from a file
|
||||
* @throws IllegalStateException When either no file is selected or the selected files content is invalid
|
||||
*/
|
||||
|
|
|
@ -12,11 +12,12 @@ public class Utils {
|
|||
/**
|
||||
* Add a new row of components to the specified target component.
|
||||
* This will add label to the right side of the next row and the specified component to the left.
|
||||
*
|
||||
* @param constraints the constraint to use. Must be non-null
|
||||
* @param target the base component to add a row to
|
||||
* @param comp the component to add to the left side
|
||||
* @param row the row to add the components to
|
||||
* @param name the labels text to add to the left side
|
||||
* @param target the base component to add a row to
|
||||
* @param comp the component to add to the left side
|
||||
* @param row the row to add the components to
|
||||
* @param name the labels text to add to the left side
|
||||
*/
|
||||
public static void addGridBagRow(GridBagConstraints constraints, JComponent target, JComponent comp, int row, String name) {
|
||||
constraints.gridwidth = 1;
|
||||
|
@ -36,11 +37,12 @@ public class Utils {
|
|||
/**
|
||||
* Add a new row of components to the specified target component.
|
||||
* This will add label to the right side of the next row and the specified component to the left.
|
||||
*
|
||||
* @param constraints the constraint to use. Must be non-null
|
||||
* @param target the base component to add a row to
|
||||
* @param comp the component to add to the left side
|
||||
* @param row the row to add the components to
|
||||
* @param right the component to place on the left side
|
||||
* @param target the base component to add a row to
|
||||
* @param comp the component to add to the left side
|
||||
* @param row the row to add the components to
|
||||
* @param right the component to place on the left side
|
||||
*/
|
||||
public static void addGridBagRow(GridBagConstraints constraints, JComponent target, JComponent comp, int row, Component right) {
|
||||
constraints.gridwidth = 1;
|
||||
|
|
|
@ -34,7 +34,7 @@ public class AccountView extends JPanel {
|
|||
createComponents();
|
||||
createLayout();
|
||||
|
||||
setBorder(BorderFactory.createEmptyBorder(8,8,8,8));
|
||||
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
||||
}
|
||||
|
||||
public void setProfile(Profile profile) {
|
||||
|
@ -55,10 +55,10 @@ public class AccountView extends JPanel {
|
|||
this.type.setText(translate(account.getClass().getSimpleName()));
|
||||
if (account instanceof CurrentAccount) {
|
||||
this.typeSpecialLabel.setText(translate("Overdraft"));
|
||||
this.typeSpecialProperty.setText( StringDecoder.getNumberFormat().format(((CurrentAccount) account).getOverdraft()) + " €");
|
||||
this.typeSpecialProperty.setText(StringDecoder.getNumberFormat().format(((CurrentAccount) account).getOverdraft()) + " €");
|
||||
} else if (account instanceof SavingsAccount) {
|
||||
this.typeSpecialLabel.setText(translate("Interest rate"));
|
||||
this.typeSpecialProperty.setText( ((SavingsAccount) account).getInterest() + " %" );
|
||||
this.typeSpecialProperty.setText(((SavingsAccount) account).getInterest() + " %");
|
||||
} else {
|
||||
Logging.LOGGER.severe("Type of new primary account cannot be determined: " + account.getClass().getName());
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ public class AccountView extends JPanel {
|
|||
var constraints = new GridBagConstraints();
|
||||
|
||||
constraints.gridwidth = 4;
|
||||
constraints.insets = new Insets(12,12,12,12);
|
||||
constraints.insets = new Insets(12, 12, 12, 12);
|
||||
|
||||
var accountSelectionPanel = new JPanel(new BorderLayout(12, 0));
|
||||
accountSelectionPanel.add(iban, BorderLayout.CENTER);
|
||||
|
|
|
@ -11,6 +11,7 @@ public interface AccountSelectionListener {
|
|||
/**
|
||||
* Run when a new account is selected.
|
||||
* The selected account is set as the primary account of the profile
|
||||
*
|
||||
* @param account the profile for the selected account
|
||||
*/
|
||||
void onAccountSelected(Profile account);
|
||||
|
|
|
@ -19,9 +19,10 @@ public class LoginData {
|
|||
|
||||
/**
|
||||
* authenticate the specified account with the provided pin.
|
||||
* @param blz the bank identifier
|
||||
*
|
||||
* @param blz the bank identifier
|
||||
* @param iban the account identifier
|
||||
* @param pin the pin for the account to authenticate with
|
||||
* @param pin the pin for the account to authenticate with
|
||||
* @return an optional wrapping the specified account if authentication was successful
|
||||
*/
|
||||
public Optional<Profile> authenticateAccount(String blz, int iban, int pin) {
|
||||
|
|
|
@ -14,8 +14,9 @@ public class TransferData {
|
|||
|
||||
/**
|
||||
* Transfers a certain amount of money to the specified account of the specified bank
|
||||
* @param amount the amount to transfer
|
||||
* @param blz the bank that manages the account
|
||||
*
|
||||
* @param amount the amount to transfer
|
||||
* @param blz the bank that manages the account
|
||||
* @param ibanString the internal bank account number to transfer money to
|
||||
* @throws IllegalArgumentException if the bank or the account do not exist
|
||||
*/
|
||||
|
@ -30,7 +31,7 @@ public class TransferData {
|
|||
// validate iban of target account
|
||||
var iban = 0;
|
||||
try {
|
||||
iban = StringDecoder.decodeUniqueIdentificationNumber(ibanString);
|
||||
iban = StringDecoder.decodeUniqueIdentificationNumber(ibanString);
|
||||
} catch (Exception ex) {
|
||||
Logging.LOGGER.warning("IBAN has invalid format: " + ibanString + " because: " + ex.getMessage());
|
||||
throw new IllegalArgumentException("IBAN has invalid format: " + ibanString);
|
||||
|
|
|
@ -16,8 +16,9 @@ public class Locales {
|
|||
* Sets the default locale to use for the application instance.
|
||||
* This will instruct the translator to use the default locale as well as
|
||||
* Java swing components.
|
||||
*
|
||||
* @param language the locale to use for language
|
||||
* @param country the locale to use for country
|
||||
* @param country the locale to use for country
|
||||
*/
|
||||
public static void setDefaultLocale(String language, String country) {
|
||||
var locale = language + "_" + country;
|
||||
|
|
|
@ -8,21 +8,20 @@ import java.util.stream.Collectors;
|
|||
|
||||
public final class Translator {
|
||||
|
||||
/**
|
||||
* Precomputed index of the locale used to statically translate a phrase
|
||||
*/
|
||||
private static int localeTranslationIndex = 0;
|
||||
|
||||
/**
|
||||
* List of all supported locales in the format: language_country. Examples: en_EN, fr_FR
|
||||
* The index inside the list is directly related to the index of the translation array inside the list translations.
|
||||
*/
|
||||
private static final List<String> languages = new ArrayList<>();
|
||||
/**
|
||||
* Mapping of a default english phrase of the code en_EN which is associated with a list of possible translations.
|
||||
* Index 0 of the translation is equivalent to the key itself since locale 0 is always en_EN.
|
||||
* Mapping of a default english phrase of the code en_EN which is associated with a list of possible translations.
|
||||
* Index 0 of the translation is equivalent to the key itself since locale 0 is always en_EN.
|
||||
*/
|
||||
private static final Map<String, String[]> translations = new HashMap<>();
|
||||
/**
|
||||
* Precomputed index of the locale used to statically translate a phrase
|
||||
*/
|
||||
private static int localeTranslationIndex = 0;
|
||||
|
||||
static {
|
||||
// read language file and parse
|
||||
|
@ -64,6 +63,7 @@ public final class Translator {
|
|||
/**
|
||||
* Translates the given phrase into the corresponding phrase of the selected locale of the translator.
|
||||
* If no translation is found or no locale is defined for the translator this function will return the given phrase.
|
||||
*
|
||||
* @param phrase the text to translate
|
||||
* @return the translated phrase, or the phrase itself in case no translation can be found
|
||||
*/
|
||||
|
@ -81,6 +81,7 @@ public final class Translator {
|
|||
|
||||
/**
|
||||
* Returns an array of all available locales for this translator
|
||||
*
|
||||
* @return an array of all available locales for this translator
|
||||
*/
|
||||
public static String[] availableLocales() {
|
||||
|
@ -90,6 +91,7 @@ public final class Translator {
|
|||
/**
|
||||
* Map the given locale string to an index indicating which array location to choose when fetching a result from
|
||||
* the translation map.
|
||||
*
|
||||
* @param locale the locale string
|
||||
* @return a matching index of the locale
|
||||
* @throws IllegalArgumentException if the given locale is not part of the available locales
|
||||
|
@ -107,6 +109,7 @@ public final class Translator {
|
|||
/**
|
||||
* Sets the default locale to use when translating.
|
||||
* The locale must have the format language_COUNTRY like en_EN
|
||||
*
|
||||
* @param locale the locale to use when translating
|
||||
* @return if the specified locale can be used by the translator
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue