Subida del módulo y tema de PrestaShop

This commit is contained in:
Kaloyan
2026-04-09 18:31:51 +02:00
parent 12c253296f
commit 16b3ff9424
39262 changed files with 7418797 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class LocalizationException {
message: string;
name: string;
constructor(message: string) {
this.message = message;
this.name = 'LocalizationException';
}
}
export default LocalizationException;

View File

@@ -0,0 +1,15 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
import NumberFormatter from '@app/cldr/number-formatter';
import NumberSymbol from '@app/cldr/number-symbol';
import PriceSpecification from '@app/cldr/specifications/price';
import NumberSpecification from '@app/cldr/specifications/number';
export {
PriceSpecification,
NumberSpecification,
NumberFormatter,
NumberSymbol,
};

View File

@@ -0,0 +1,322 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* These placeholders are used in CLDR number formatting templates.
* They are meant to be replaced by the correct localized symbols in the number formatting process.
*/
import NumberSymbol from '@app/cldr/number-symbol';
import PriceSpecification from '@app/cldr/specifications/price';
import NumberSpecification from '@app/cldr/specifications/number';
// eslint-disable-next-line
const escapeRE = require('lodash.escaperegexp');
const CURRENCY_SYMBOL_PLACEHOLDER = '¤';
const DECIMAL_SEPARATOR_PLACEHOLDER = '.';
const GROUP_SEPARATOR_PLACEHOLDER = ',';
const MINUS_SIGN_PLACEHOLDER = '-';
const PERCENT_SYMBOL_PLACEHOLDER = '%';
const PLUS_SIGN_PLACEHOLDER = '+';
class NumberFormatter {
numberSpecification: Record<string, any>;
/**
* @param NumberSpecification specification Number specification to be used
* (can be a number spec, a price spec, a percentage spec)
*/
constructor(specification: Record<string, any>) {
this.numberSpecification = specification;
}
/**
* Formats the passed number according to specifications.
*
* @param int|float|string number The number to format
* @param NumberSpecification specification Number specification to be used
* (can be a number spec, a price spec, a percentage spec)
*
* @return string The formatted number
* You should use this this value for display, without modifying it
*/
format(number: number, specification?: Record<string, any>): string {
if (specification !== undefined) {
this.numberSpecification = specification;
}
/*
* We need to work on the absolute value first.
* Then the CLDR pattern will add the sign if relevant (at the end).
*/
const num = Math.abs(number).toFixed(
this.numberSpecification.getMaxFractionDigits(),
);
let [majorDigits, minorDigits] = this.extractMajorMinorDigits(num);
majorDigits = <string> this.splitMajorGroups(majorDigits);
minorDigits = this.adjustMinorDigitsZeroes(minorDigits);
// Assemble the final number
let formattedNumber = majorDigits;
if (minorDigits) {
formattedNumber += DECIMAL_SEPARATOR_PLACEHOLDER + minorDigits;
}
// Get the good CLDR formatting pattern. Sign is important here !
const pattern = this.getCldrPattern(number < 0);
formattedNumber = this.addPlaceholders(formattedNumber, pattern);
formattedNumber = this.replaceSymbols(formattedNumber);
formattedNumber = this.performSpecificReplacements(formattedNumber);
return formattedNumber;
}
/**
* Get number's major and minor digits.
*
* Major digits are the "integer" part (before decimal separator),
* minor digits are the fractional part
* Result will be an array of exactly 2 items: [majorDigits, minorDigits]
*
* Usage example:
* list(majorDigits, minorDigits) = this.getMajorMinorDigits(decimalNumber);
*
* @param DecimalNumber number
*
* @return string[]
*/
extractMajorMinorDigits(number: string): Array<string> {
// Get the number's major and minor digits.
const result = number.toString().split('.');
const majorDigits = result[0];
const minorDigits = result[1] === undefined ? '' : result[1];
return [majorDigits, minorDigits];
}
/**
* Splits major digits into groups.
*
* e.g.: Given the major digits "1234567", and major group size
* configured to 3 digits, the result would be "1 234 567"
*
* @param string majorDigits The major digits to be grouped
*
* @return string The grouped major digits
*/
splitMajorGroups(digit: string): Array<string> | string {
if (!this.numberSpecification.isGroupingUsed()) {
return digit;
}
// Reverse the major digits, since they are grouped from the right.
const majorDigits = digit.split('').reverse();
// Group the major digits.
let groups = [];
groups.push(
majorDigits.splice(0, this.numberSpecification.getPrimaryGroupSize()),
);
while (majorDigits.length) {
groups.push(
majorDigits.splice(0, this.numberSpecification.getSecondaryGroupSize()),
);
}
// Reverse back the digits and the groups
groups = groups.reverse();
const newGroups: Array<string> = [];
groups.forEach((group) => {
newGroups.push(group.reverse().join(''));
});
// Reconstruct the major digits.
return newGroups.join(GROUP_SEPARATOR_PLACEHOLDER);
}
/**
* Adds or remove trailing zeroes, depending on specified min and max fraction digits numbers.
*
* @param string minorDigits Digits to be adjusted with (trimmed or padded) zeroes
*
* @return string The adjusted minor digits
*/
adjustMinorDigitsZeroes(minorDigits: string): string {
let digit = minorDigits;
if (digit.length > this.numberSpecification.getMaxFractionDigits()) {
// Strip any trailing zeroes.
digit = digit.replace(/0+$/, '');
}
if (digit.length < this.numberSpecification.getMinFractionDigits()) {
// Re-add needed zeroes
digit = digit.padEnd(
this.numberSpecification.getMinFractionDigits(),
'0',
);
}
return digit;
}
/**
* Get the CLDR formatting pattern.
*
* @see http://cldr.unicode.org/translation/number-patterns
*
* @param bool isNegative If true, the negative pattern
* will be returned instead of the positive one
*
* @return string The CLDR formatting pattern
*/
getCldrPattern(isNegative: boolean): string {
if (isNegative) {
return this.numberSpecification.getNegativePattern();
}
return this.numberSpecification.getPositivePattern();
}
/**
* Replace placeholder number symbols with relevant numbering system's symbols.
*
* @param string number
* The number to process
*
* @return string
* The number with replaced symbols
*/
replaceSymbols(number: string): string {
const symbols = this.numberSpecification.getSymbol();
const map: Record<string, any> = {};
map[DECIMAL_SEPARATOR_PLACEHOLDER] = symbols.getDecimal();
map[GROUP_SEPARATOR_PLACEHOLDER] = symbols.getGroup();
map[MINUS_SIGN_PLACEHOLDER] = symbols.getMinusSign();
map[PERCENT_SYMBOL_PLACEHOLDER] = symbols.getPercentSign();
map[PLUS_SIGN_PLACEHOLDER] = symbols.getPlusSign();
return this.strtr(number, map);
}
/**
* strtr() for JavaScript
* Translate characters or replace substrings
*
* @param str
* String to parse
* @param pairs
* Hash of ('from' => 'to', ...).
*
* @return string
*/
strtr(str: string, pairs: Record<string, any>): string {
const substrs = Object.keys(pairs).map(escapeRE);
return str
.split(RegExp(`(${substrs.join('|')})`))
.map((part: string) => pairs[part] || part)
.join('');
}
/**
* Add missing placeholders to the number using the passed CLDR pattern.
*
* Missing placeholders can be the percent sign, currency symbol, etc.
*
* e.g. with a currency CLDR pattern:
* - Passed number (partially formatted): 1,234.567
* - Returned number: 1,234.567 ¤
* ("¤" symbol is the currency symbol placeholder)
*
* @see http://cldr.unicode.org/translation/number-patterns
*
* @param formattedNumber
* Number to process
* @param pattern
* CLDR formatting pattern to use
*
* @return string
*/
addPlaceholders(formattedNumber: string, pattern: string): string {
/*
* Regex groups explanation:
* # : literal "#" character. Once.
* (,#+)* : any other "#" characters group, separated by ",". Zero to infinity times.
* 0 : literal "0" character. Once.
* (\.[0#]+)* : any combination of "0" and "#" characters groups, separated by '.'.
* Zero to infinity times.
*/
return pattern.replace(/#?(,#+)*0(\.[0#]+)*/, formattedNumber);
}
/**
* Perform some more specific replacements.
*
* Specific replacements are needed when number specification is extended.
* For instance, prices have an extended number specification in order to
* add currency symbol to the formatted number.
*
* @param string formattedNumber
*
* @return mixed
*/
performSpecificReplacements(formattedNumber: string): string {
if (this.numberSpecification instanceof PriceSpecification) {
return formattedNumber
.split(CURRENCY_SYMBOL_PLACEHOLDER)
.join(this.numberSpecification.getCurrencySymbol());
}
return formattedNumber;
}
static build(specifications: Record<string, any>): NumberFormatter {
let symbol;
if (undefined !== specifications.numberSymbols) {
// @ts-ignore-next-line
symbol = new NumberSymbol(...specifications.numberSymbols);
} else {
// @ts-ignore-next-line
symbol = new NumberSymbol(...specifications.symbol);
}
let specification;
if (specifications.currencySymbol) {
specification = new PriceSpecification(
specifications.positivePattern,
specifications.negativePattern,
symbol,
parseInt(specifications.maxFractionDigits, 10),
parseInt(specifications.minFractionDigits, 10),
specifications.groupingUsed,
specifications.primaryGroupSize,
specifications.secondaryGroupSize,
specifications.currencySymbol,
specifications.currencyCode,
);
} else {
specification = new NumberSpecification(
specifications.positivePattern,
specifications.negativePattern,
symbol,
parseInt(specifications.maxFractionDigits, 10),
parseInt(specifications.minFractionDigits, 10),
specifications.groupingUsed,
specifications.primaryGroupSize,
specifications.secondaryGroupSize,
);
}
return new NumberFormatter(specification);
}
}
export default NumberFormatter;

View File

@@ -0,0 +1,230 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
import LocalizationException from '@app/cldr/exception/localization';
class NumberSymbol {
decimal: string;
group: string;
list: string;
percentSign: string;
minusSign: string;
plusSign: string;
exponential: string;
superscriptingExponent: string;
perMille: string;
infinity: string;
nan: string;
/**
* NumberSymbolList constructor.
*
* @param string decimal Decimal separator character
* @param string group Digits group separator character
* @param string list List elements separator character
* @param string percentSign Percent sign character
* @param string minusSign Minus sign character
* @param string plusSign Plus sign character
* @param string exponential Exponential character
* @param string superscriptingExponent Superscripting exponent character
* @param string perMille Permille sign character
* @param string infinity The infinity sign. Corresponds to the IEEE infinity bit pattern.
* @param string nan The NaN (Not A Number) sign. Corresponds to the IEEE NaN bit pattern.
*
* @throws LocalizationException
*/
constructor(
decimal: string,
group: string,
list: string,
percentSign: string,
minusSign: string,
plusSign: string,
exponential: string,
superscriptingExponent: string,
perMille: string,
infinity: string,
nan: string,
) {
this.decimal = decimal;
this.group = group;
this.list = list;
this.percentSign = percentSign;
this.minusSign = minusSign;
this.plusSign = plusSign;
this.exponential = exponential;
this.superscriptingExponent = superscriptingExponent;
this.perMille = perMille;
this.infinity = infinity;
this.nan = nan;
this.validateData();
}
/**
* Get the decimal separator.
*
* @return string
*/
getDecimal(): string {
return this.decimal;
}
/**
* Get the digit groups separator.
*
* @return string
*/
getGroup(): string {
return this.group;
}
/**
* Get the list elements separator.
*
* @return string
*/
getList(): string {
return this.list;
}
/**
* Get the percent sign.
*
* @return string
*/
getPercentSign(): string {
return this.percentSign;
}
/**
* Get the minus sign.
*
* @return string
*/
getMinusSign(): string {
return this.minusSign;
}
/**
* Get the plus sign.
*
* @return string
*/
getPlusSign(): string {
return this.plusSign;
}
/**
* Get the exponential character.
*
* @return string
*/
getExponential(): string {
return this.exponential;
}
/**
* Get the exponent character.
*
* @return string
*/
getSuperscriptingExponent(): string {
return this.superscriptingExponent;
}
/**
* Gert the per mille symbol (often "‰").
*
* @see https://en.wikipedia.org/wiki/Per_mille
*
* @return string
*/
getPerMille(): string {
return this.perMille;
}
/**
* Get the infinity symbol (often "∞").
*
* @see https://en.wikipedia.org/wiki/Infinity_symbol
*
* @return string
*/
getInfinity(): string {
return this.infinity;
}
/**
* Get the NaN (not a number) sign.
*
* @return string
*/
getNan(): string {
return this.nan;
}
/**
* Symbols list validation.
*
* @throws LocalizationException
*/
validateData(): void {
if (!this.decimal || typeof this.decimal !== 'string') {
throw new LocalizationException('Invalid decimal');
}
if (!this.group || typeof this.group !== 'string') {
throw new LocalizationException('Invalid group');
}
if (!this.list || typeof this.list !== 'string') {
throw new LocalizationException('Invalid symbol list');
}
if (!this.percentSign || typeof this.percentSign !== 'string') {
throw new LocalizationException('Invalid percentSign');
}
if (!this.minusSign || typeof this.minusSign !== 'string') {
throw new LocalizationException('Invalid minusSign');
}
if (!this.plusSign || typeof this.plusSign !== 'string') {
throw new LocalizationException('Invalid plusSign');
}
if (!this.exponential || typeof this.exponential !== 'string') {
throw new LocalizationException('Invalid exponential');
}
if (!this.superscriptingExponent || typeof this.superscriptingExponent !== 'string') {
throw new LocalizationException('Invalid superscriptingExponent');
}
if (!this.perMille || typeof this.perMille !== 'string') {
throw new LocalizationException('Invalid perMille');
}
if (!this.infinity || typeof this.infinity !== 'string') {
throw new LocalizationException('Invalid infinity');
}
if (!this.nan || typeof this.nan !== 'string') {
throw new LocalizationException('Invalid nan');
}
}
}
export default NumberSymbol;

View File

@@ -0,0 +1,175 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
import LocalizationException from '@app/cldr/exception/localization';
import NumberSymbol from '@app/cldr/number-symbol';
class NumberSpecification {
positivePattern: string;
negativePattern: string;
symbol: NumberSymbol;
maxFractionDigits: number;
minFractionDigits: number;
groupingUsed: boolean;
primaryGroupSize: number;
secondaryGroupSize: number;
/**
* Number specification constructor.
*
* @param string positivePattern CLDR formatting pattern for positive amounts
* @param string negativePattern CLDR formatting pattern for negative amounts
* @param NumberSymbol symbol Number symbol
* @param int maxFractionDigits Maximum number of digits after decimal separator
* @param int minFractionDigits Minimum number of digits after decimal separator
* @param bool groupingUsed Is digits grouping used ?
* @param int primaryGroupSize Size of primary digits group in the number
* @param int secondaryGroupSize Size of secondary digits group in the number
*
* @throws LocalizationException
*/
constructor(
positivePattern: string,
negativePattern: string,
symbol: NumberSymbol,
maxFractionDigits: number,
minFractionDigits: number,
groupingUsed: boolean,
primaryGroupSize: number,
secondaryGroupSize: number,
) {
this.positivePattern = positivePattern;
this.negativePattern = negativePattern;
this.symbol = symbol;
this.maxFractionDigits = maxFractionDigits;
// eslint-disable-next-line
this.minFractionDigits =
maxFractionDigits < minFractionDigits
? maxFractionDigits
: minFractionDigits;
this.groupingUsed = groupingUsed;
this.primaryGroupSize = primaryGroupSize;
this.secondaryGroupSize = secondaryGroupSize;
if (!this.positivePattern || typeof this.positivePattern !== 'string') {
throw new LocalizationException('Invalid positivePattern');
}
if (!this.negativePattern || typeof this.negativePattern !== 'string') {
throw new LocalizationException('Invalid negativePattern');
}
if (!this.symbol || !(this.symbol instanceof NumberSymbol)) {
throw new LocalizationException('Invalid symbol');
}
if (typeof this.maxFractionDigits !== 'number') {
throw new LocalizationException('Invalid maxFractionDigits');
}
if (typeof this.minFractionDigits !== 'number') {
throw new LocalizationException('Invalid minFractionDigits');
}
if (typeof this.groupingUsed !== 'boolean') {
throw new LocalizationException('Invalid groupingUsed');
}
if (typeof this.primaryGroupSize !== 'number') {
throw new LocalizationException('Invalid primaryGroupSize');
}
if (typeof this.secondaryGroupSize !== 'number') {
throw new LocalizationException('Invalid secondaryGroupSize');
}
}
/**
* Get symbol.
*
* @return NumberSymbol
*/
getSymbol(): NumberSymbol {
return this.symbol;
}
/**
* Get the formatting rules for this number (when positive).
*
* This pattern uses the Unicode CLDR number pattern syntax
*
* @return string
*/
getPositivePattern(): string {
return this.positivePattern;
}
/**
* Get the formatting rules for this number (when negative).
*
* This pattern uses the Unicode CLDR number pattern syntax
*
* @return string
*/
getNegativePattern(): string {
return this.negativePattern;
}
/**
* Get the maximum number of digits after decimal separator (rounding if needed).
*
* @return int
*/
getMaxFractionDigits(): number {
return this.maxFractionDigits;
}
/**
* Get the minimum number of digits after decimal separator (fill with "0" if needed).
*
* @return int
*/
getMinFractionDigits(): number {
return this.minFractionDigits;
}
/**
* Get the "grouping" flag. This flag defines if digits
* grouping should be used when formatting this number.
*
* @return bool
*/
isGroupingUsed(): boolean {
return this.groupingUsed;
}
/**
* Get the size of primary digits group in the number.
*
* @return int
*/
getPrimaryGroupSize(): number {
return this.primaryGroupSize;
}
/**
* Get the size of secondary digits groups in the number.
*
* @return int
*/
getSecondaryGroupSize(): number {
return this.secondaryGroupSize;
}
}
export default NumberSpecification;

View File

@@ -0,0 +1,99 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
import LocalizationException from '@app/cldr/exception/localization';
import NumberSpecification from '@app/cldr/specifications/number';
import NumberSymbol from '@app/cldr/number-symbol';
/**
* Currency display option: symbol notation.
*/
const CURRENCY_DISPLAY_SYMBOL = 'symbol';
class PriceSpecification extends NumberSpecification {
currencySymbol: string;
currencyCode: string;
/**
* Price specification constructor.
*
* @param string positivePattern CLDR formatting pattern for positive amounts
* @param string negativePattern CLDR formatting pattern for negative amounts
* @param NumberSymbol symbol Number symbol
* @param int maxFractionDigits Maximum number of digits after decimal separator
* @param int minFractionDigits Minimum number of digits after decimal separator
* @param bool groupingUsed Is digits grouping used ?
* @param int primaryGroupSize Size of primary digits group in the number
* @param int secondaryGroupSize Size of secondary digits group in the number
* @param string currencySymbol Currency symbol of this price (eg. : €)
* @param currencyCode Currency code of this price (e.g.: EUR)
*
* @throws LocalizationException
*/
constructor(
positivePattern: string,
negativePattern: string,
symbol: NumberSymbol,
maxFractionDigits: number,
minFractionDigits: number,
groupingUsed: boolean,
primaryGroupSize: number,
secondaryGroupSize: number,
currencySymbol: string,
currencyCode: string,
) {
super(
positivePattern,
negativePattern,
symbol,
maxFractionDigits,
minFractionDigits,
groupingUsed,
primaryGroupSize,
secondaryGroupSize,
);
this.currencySymbol = currencySymbol;
this.currencyCode = currencyCode;
if (!this.currencySymbol || typeof this.currencySymbol !== 'string') {
throw new LocalizationException('Invalid currencySymbol');
}
if (!this.currencyCode || typeof this.currencyCode !== 'string') {
throw new LocalizationException('Invalid currencyCode');
}
}
/**
* Get type of display for currency symbol.
*
* @return string
*/
static getCurrencyDisplay(): string {
return CURRENCY_DISPLAY_SYMBOL;
}
/**
* Get the currency symbol
* e.g.: €.
*
* @return string
*/
getCurrencySymbol(): string {
return this.currencySymbol;
}
/**
* Get the currency ISO code
* e.g.: EUR.
*
* @return string
*/
getCurrencyCode(): string {
return this.currencyCode;
}
}
export default PriceSpecification;