Group
Extension

Serge-Sync-Plugin-TranslationService-weblate/lib/Serge/Sync/Plugin/TranslationService/weblate.pm

# ABSTRACT: Weblate (https://weblate.org/) synchronization plugin for Serge

package Serge::Sync::Plugin::TranslationService::weblate;
use parent Serge::Sync::Plugin::Base::TranslationService, Serge::Interface::SysCmdRunner;

use strict;

use File::Find qw(find);
use File::Spec::Functions qw(catfile abs2rel);
use JSON -support_by_pp; # -support_by_pp is used to make Perl on Mac happy
use Serge::Util qw(subst_macros);
use version;
use Scalar::Util qw(reftype);
use File::Path qw(make_path);
use File::Basename;

our $VERSION = qv('0.900.6');

sub name {
    return 'Weblate translation software (https://weblate.org/) synchronization plugin';
}

sub init {
    my $self = shift;

    $self->SUPER::init(@_);

    $self->{optimizations} = 1; # set to undef to disable optimizations

    $self->merge_schema({
        root_directory   => 'STRING',
        project          => 'STRING',
        config_file      => 'STRING',
        languages        => 'ARRAY'
    });
}

sub validate_data {
    my ($self) = @_;

    $self->SUPER::validate_data;

    $self->{data}->{root_directory} = subst_macros($self->{data}->{root_directory});
    $self->{data}->{project} = subst_macros($self->{data}->{project});
    $self->{data}->{config_file} = subst_macros($self->{data}->{config_file});
    $self->{data}->{languages} = subst_macros($self->{data}->{languages});

    die "'root_directory', which is set to '$self->{data}->{root_directory}', does not point to a valid folder." unless -d $self->{data}->{root_directory};
    die "'config_file' not defined" unless defined $self->{data}->{config_file};
    die "'config_file', which is set to '$self->{data}->{config_file}', does not point to a valid file.\n" unless -f $self->{data}->{config_file};
    die "'project' not defined" unless defined $self->{data}->{project};

    if (!exists $self->{data}->{languages} or scalar(@{$self->{data}->{languages}}) == 0) {
        die "the list of destination languages is empty";
    }
}

sub pull_ts {
    my ($self, $langs) = @_;

    my $langs_to_push = $self->get_all_langs($langs);
    my %files = $self->translation_files($langs_to_push);

    foreach my $key (sort keys %files) {
        my $file = $files{$key};
        my $full_path = catfile($self->{data}->{root_directory}, $file);

        my ($file_name,$folder_path,$file_suffix) = fileparse($full_path);

        if (!(-d $folder_path)) {
            make_path($folder_path);
        }

        my $cli_return = $self->run_weblate_cli('download --output "'.$file.'" '.$key, 0);

        if ($cli_return != 0) {
            return $cli_return;
        }
    }

    return 0;
}

sub push_ts {
    my ($self, $langs) = @_;

    my $langs_to_push = $self->get_all_langs($langs);
    my %files = $self->translation_files($langs_to_push);

    foreach my $key (sort keys %files) {
        my $file = $files{$key};
        my $full_path = catfile($self->{data}->{root_directory}, $file);

        if (-f $full_path) {
            my $command = 'upload --overwrite --input "' . $file . '"';
            $command .= ' --method replace';
            if ($self->{data}->{fuzzy}) {
                $command .= ' --fuzzy '.$self->{data}->{fuzzy};
            }
            $command .= ' '.$key;

            my $cli_return = $self->run_weblate_cli($command, 0);

            if ($cli_return != 0) {
                return $cli_return;
            }
        }
    }

    return 0;
}

sub translation_files {
    my ($self, $langs) = @_;

    my $json_components = $self->run_weblate_cli('--format json list-components '.$self->{data}->{project}, 1);

    my $json_components_tree = $self->parse_json($json_components);
    my $json_components_list = $self->parse_list($json_components_tree);
    my @components = map { $_->{slug} } @$json_components_list;

    my %translations = ();

    my %langs_hash = map { $_ => 1 } @$langs;

    foreach my $component (@components) {
        my $json_translations = $self->run_weblate_cli('--format json list-translations '.$self->{data}->{project}.'/'.$component, 1);

        my $json_translations_tree = $self->parse_json($json_translations);
        my $json_translations_list = $self->parse_list($json_translations_tree);

        foreach my $translation (@$json_translations_list) {
            my $language = $translation->{language}->{code};
            my $filename = $translation->{filename};
            my $language_code = $translation->{language_code};

            if (exists $langs_hash{$language_code}) {
                $translations{$self->{data}->{project}.'/'.$component.'/'.$language} = $filename;
            }
        }
    }

    return %translations;
}

sub get_all_langs {
    my ($self, $langs) = @_;

    if (!$langs) {
        $langs = $self->{data}->{languages};
    }

    my @all_langs = ();

    push @all_langs, @$langs;

    return \@all_langs;
}

sub run_weblate_cli {
    my ($self, $action, $capture) = @_;

    my $cli_return = 0;

    my $command = 'wlc --config '.$self->{data}->{config_file}.' '.$action;

    print "Running $action ...\n";

    my $json_response = $self->run_in($self->{data}->{root_directory}, $command, 1);

    if ($capture) {
        return $json_response;
    }

    if ($json_response == '') {
        return 0;
    }

    return $self->parse_result($json_response);
}

sub parse_result {
    my ($self, $json_response) = @_;

    my $json_tree = $self->parse_json($json_response);

    if (reftype($json_tree) == 'ARRAY') {
        return 0;
    }

    my $result =  $json_tree->{result};

    if ($result == 'true') {
        return 0;
    }

    return 1;
}

sub parse_list {
    my ($self, $json_tree) = @_;

    if (reftype($json_tree) == 'ARRAY') {
        return $json_tree;
    }

    return $json_tree->{results};
}

sub find_lang_files {
    my ($self, $directory) = @_;

    my @files = ();

    find(sub {
        push @files, abs2rel($File::Find::name, $directory) if(-f $_);
    }, $directory);

    return @files;
}

sub parse_json {
    my ($self, $json) = @_;

    my $tree;
    eval {
        ($tree) = from_json($json, {relaxed => 1});
    };
    if ($@ || !$tree) {
        my $error_text = $@;
        if ($error_text) {
            $error_text =~ s/\t/ /g;
            $error_text =~ s/^\s+//s;
        } else {
            $error_text = "from_json() returned empty data structure";
        }

        die $error_text;
    }

    return $tree;
}

sub get_langs {
    my ($self, $langs) = @_;

    if (!$langs) {
        $langs = $self->{data}->{languages};
    }

    return $langs;
}

1;

Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.