-opendir DIR, "$bindir" or die "$!";
-my @progfiles = grep { /\.pl$/ && !/(_custom|^\.)/ } readdir DIR;
-seekdir DIR, 0;
-my @customfiles = grep /_custom/, readdir DIR;
-closedir DIR;
+sub find_files {
+ my ($top_dir_name) = @_;
+
+ my (@files, $finder);
+
+ $finder = sub {
+ my ($dir_name) = @_;
+
+ tie my %dir_h, 'IO::Dir', $dir_name;
+
+ push @files, grep { -f } map { "${dir_name}/${_}" } keys %dir_h;
+ my @sub_dirs = grep { -d } map { "${dir_name}/${_}" } grep { ! m/^\.\.?$/ } keys %dir_h;
+
+ $finder->($_) for @sub_dirs;
+ };
+
+ $finder->($top_dir_name);
+
+ return @files;
+}
+
+sub merge_texts {
+# overwrite existing entries with the ones from 'missing'
+ $self->{texts}->{$_} = $missing->{$_} for grep { $missing->{$_} } keys %alllocales;
+
+ # try to set missing entries from lost ones
+ my %lost_by_text = map { ($_->{text} => $_->{translation}) } @lost;
+ $self->{texts}->{$_} = $lost_by_text{$_} for grep { !$self->{texts}{$_} } keys %alllocales;
+}
+
+my @bindir_files = find_files($bindir);
+my @progfiles = map { m:^(.+)/([^/]+)$:; [ $2, $1 ] } grep { /\.pl$/ && !/_custom/ } @bindir_files;
+my @customfiles = grep /_custom/, @bindir_files;
+
+push @progfiles, map { m:^(.+)/([^/]+)$:; [ $2, $1 ] } grep { /\.pm$/ } map { find_files($_) } @progdirs;