pascalx.lang.locale.pas

Переключить прокрутку окна
Загрузить этот исходный код

{
    pascalx.lang.locale — поддержка локализаций.

    Copyright © 2021, 2026 Малик Разработчик

    Это свободная программа: вы можете перераспространять её и/или изменять
    её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она будет полезной,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <https://www.gnu.org/licenses/>.
}

unit pascalx.lang.locale;

    {$MODE DELPHI}

interface

    {%region} uses
        pascalx.lang,
        pascalx.io.charset.basiclatin;
    {%endregion}

    {$TYPEINFO ON}
    {$CALLING REGISTER}

    const UNIT_NAME = 'pascalx.lang.locale';

    {%region} type
        Locale = class;

        Locale = class(&Object)
        private
            class var instance: Locale;
        public
            class procedure register(inst: Locale); static;
            class function getInstance(): Locale; static;
        private
            fldLocaleId: AnsiString;
            fldAliasIds: AnsiString_Array1d;
        public
            constructor create(const localeId: AnsiString; const aliasIds: AnsiString_Array1d);
            procedure aliasIdsCopyInto(const dst: AnsiString_Array1d; offset: int = 0);
            function isDigit(character: uchar): boolean; virtual;
            function isLowerCase(character: uchar): boolean; virtual;
            function isUpperCase(character: uchar): boolean; virtual;
            function toLowerCase(character: uchar): uchar; virtual;
            function toUpperCase(character: uchar): uchar; virtual;
            function orderOf(character: uchar): int; virtual;
            function compare(const str1, str2: UnicodeString): int; virtual;
            function isLocaleId(const localeId: AnsiString): boolean;
            function aliasIds(): AnsiString_Array1d;
        published
            property localeId: AnsiString read fldLocaleId;
        end;
    {%endregion}

implementation

    {$CALLING REGISTER}

    {%region  Locale }
        class procedure Locale.register(inst: Locale);
        begin
            if inst = nil then begin
                raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('inst') ]));
            end;
            if instance <> nil then begin
                raise SecurityException.create(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'security'));
            end;
            instance := inst;
        end;

        class function Locale.getInstance(): Locale;
        begin
            if instance = nil then begin
                instance := Locale.create('default', nil);
            end;
            result := instance;
        end;

        constructor Locale.create(const localeId: AnsiString; const aliasIds: AnsiString_Array1d);
        label
            continue_label0;
        var
            ali: int;
            alj: int;
            sourcesLength: int;
            aliasesLength: int;
            aliasInstance: AnsiString;
            aliasesData: AnsiString_Array1d;
            aliasesCopy: AnsiString_Array1d;
        begin
            inherited create();
            aliasesLength := length(aliasIds);
            aliasesData := &Array.newAnsiString1d(aliasesLength);
            sourcesLength := aliasesLength;
            aliasesLength := 0;
            for ali := 0 to sourcesLength - 1 do begin
                aliasInstance := aliasIds[ali];
                if (aliasInstance.length <= 0) or (aliasInstance = localeId) then goto continue_label0;
                for alj := 0 to aliasesLength - 1 do if aliasInstance = aliasesData[alj] then goto continue_label0;
                aliasesData[aliasesLength] := aliasInstance;
                inc(aliasesLength);
                continue_label0:
            end;
            if aliasesLength <> length(aliasesData) then begin
                aliasesCopy := &Array.newAnsiString1d(aliasesLength);
                &Array.copyStrings(aliasesData, 0, aliasesCopy, 0, aliasesLength);
                aliasesData := aliasesCopy;
            end;
            fldLocaleId := localeId;
            fldAliasIds := aliasesData;
        end;

        procedure Locale.aliasIdsCopyInto(const dst: AnsiString_Array1d; offset: int);
        var
            aliasIds: AnsiString_Array1d;
        begin
            aliasIds := fldAliasIds;
            &Array.copyStrings(aliasIds, 0, dst, offset, length(aliasIds));
        end;

        function Locale.isDigit(character: uchar): boolean;
        begin
            result := BasicLatin.isDigit(character);
        end;

        function Locale.isLowerCase(character: uchar): boolean;
        begin
            result := BasicLatin.isLowerCase(character);
        end;

        function Locale.isUpperCase(character: uchar): boolean;
        begin
            result := BasicLatin.isUpperCase(character);
        end;

        function Locale.toLowerCase(character: uchar): uchar;
        begin
            result := BasicLatin.toLowerCase(character);
        end;

        function Locale.toUpperCase(character: uchar): uchar;
        begin
            result := BasicLatin.toUpperCase(character);
        end;

        function Locale.orderOf(character: uchar): int;
        begin
            result := BasicLatin.orderOf(character);
        end;

        function Locale.compare(const str1, str2: UnicodeString): int;
        var
            index: int;
            order1: int;
            order2: int;
            length1: int;
            length2: int;
        begin
            length1 := str1.length;
            length2 := str2.length;
            for index := 1 to CoInt.min(length1, length2) do begin
                order1 := orderOf(str1[index]);
                order2 := orderOf(str2[index]);
                if order1 <> order2 then begin
                    result := order1 - order2;
                    exit;
                end;
            end;
            result := length1 - length2;
        end;

        function Locale.isLocaleId(const localeId: AnsiString): boolean;
        var
            index: int;
            aliasIds: AnsiString_Array1d;
        begin
            if fldLocaleId = localeId then begin
                result := true;
                exit;
            end;
            aliasIds := fldAliasIds;
            for index := 0 to length(aliasIds) - 1 do if aliasIds[index] = localeId then begin
                result := true;
                exit;
            end;
            result := false;
        end;

        function Locale.aliasIds(): AnsiString_Array1d;
        var
            length: int;
            aliasIdsData: AnsiString_Array1d;
            aliasIdsCopy: AnsiString_Array1d;
        begin
            aliasIdsData := fldAliasIds;
            length := system.length(aliasIdsData);
            aliasIdsCopy := &Array.newAnsiString1d(length);
            &Array.copyStrings(aliasIdsData, 0, aliasIdsCopy, 0, length);
            result := aliasIdsCopy;
        end;
    {%endregion}

    {%region} finalization
        Locale.instance.free();
    {%endregion}

end.