Unverified Commit 171e797e authored by Birte Kristina Friesel's avatar Birte Kristina Friesel
Browse files

Add localization support.

Right now, only two languages are supported, and only a fraction of strings
are already translated. There's also quite a bunch of todos left where
strings are assembled in the Model, which has no access to localization
functions. But that's something for iterative refinement over the next
months, and (especially when it comes to adding languages and translation
strings to templates) merge requests.

Squashed commit of the following:

commit 67d756f3
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Fri Jul 18 19:53:56 2025 +0200

    more translations

commit 8cb0d65e
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Fri Jul 18 18:54:12 2025 +0200

    sme more translations

commit ff12f010
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Fri Jul 18 18:53:31 2025 +0200

    Add language selection to account page

commit 9bf27132
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Fri Jul 18 16:42:28 2025 +0200

    Translate footer components

commit 90c2c650
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Fri Jul 18 16:16:50 2025 +0200

    Use Accept-Language header if user has no preferred languages

commit 814cb4a4
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Fri Jul 18 16:11:19 2025 +0200

    Add list of preferred languages to user settings

commit 731b7898
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Fri Jul 18 15:33:42 2025 +0200

    Localization with Locale::Maketext

    WiP, no suitable foundation for merge requests yet.

    Still todo:
    * override Accept-Language header via account settings
    * Adjust all the templates and frontend javascript

    Related to #223
parent e081fd25
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -9,6 +9,8 @@ requires 'GIS::Distance::Fast';
requires 'IO::Socket::Socks', '>= 0.64';
requires 'IO::Socket::SSL',   '>= 2.009';
requires 'List::UtilsBy';
requires 'Locale::Maketext';
requires 'Locale::Maketext::Lexicon';
requires 'Math::Polygon';
requires 'MIME::Entity';
requires 'Mojolicious';
+45 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ use Travelynx::Helper::DBRIS;
use Travelynx::Helper::EFA;
use Travelynx::Helper::HAFAS;
use Travelynx::Helper::IRIS;
use Travelynx::Helper::Locales;
use Travelynx::Helper::MOTIS;
use Travelynx::Helper::Sendmail;
use Travelynx::Helper::Traewelling;
@@ -157,6 +158,14 @@ sub startup {
		}
	);

	$self->hook(
		'before_render' => sub {
			my ($self) = @_;

			$self->stash( loc_handle => $self->loc_handle );
		}
	);

	$self->attr(
		cache_iris_main => sub {
			my ($self) = @_;
@@ -411,6 +420,40 @@ sub startup {
		}
	);

	$self->helper(
		loc_handle => sub {
			my ($self) = @_;

			my @languages;
			if ( $self->is_user_authenticated
				and @{ $self->current_user->{languages} } )
			{
				@languages = @{ $self->current_user->{languages} };
			}
			elsif ( my $languages = $self->req->headers->accept_language ) {
				for my $lang ( split( qr{ \s* , \s* }x, $languages ) ) {
					if ( $lang =~ m{ ^ de }x ) {
						push( @languages, 'de-DE' );
					}
					elsif ( $lang =~ m{ ^ en }x ) {
						push( @languages, 'en-GB' );
					}
				}
			}

			# de-DE is our fall-back language and thus always appended
			return Travelynx::Helper::Locales->get_handle( @languages,
				'de-DE' );
		}
	);

	$self->helper(
		'L' => sub {
			my ( $self, @args ) = @_;
			$self->stash('loc_handle')->maketext(@args);
		}
	);

	$self->helper(
		'now' => sub {
			return DateTime->now( time_zone => 'Europe/Berlin' );
@@ -3067,6 +3110,7 @@ sub startup {
	$authed_r->get('/account/hooks')->to('account#webhook');
	$authed_r->get('/account/traewelling')->to('traewelling#settings');
	$authed_r->get('/account/insight')->to('account#insight');
	$authed_r->get('/account/language')->to('account#change_language');
	$authed_r->get('/ajax/status_card.html')->to('traveling#status_card');
	$authed_r->get( '/cancelled' => [ format => [ 'html', 'json' ] ] )
	  ->to( 'traveling#cancelled', format => undef );
@@ -3097,6 +3141,7 @@ sub startup {
	$authed_r->post('/account/hooks')->to('account#webhook');
	$authed_r->post('/account/traewelling')->to('traewelling#settings');
	$authed_r->post('/account/insight')->to('account#insight');
	$authed_r->post('/account/language')->to('account#change_language');
	$authed_r->post('/account/select_backend')->to('account#change_backend');
	$authed_r->post('/checkin/add')->to('traveling#add_intransit_form');
	$authed_r->post('/journey/add')->to('traveling#add_journey_form');
+22 −0
Original line number Diff line number Diff line
@@ -3359,6 +3359,28 @@ qq{select distinct checkout_station_id from in_transit where backend_id = 0;}
			}
		);
	},

	# v66 -> v67
	# Add language settings to profile
	sub {
		my ($db) = @_;
		$db->query(
			qq{
				drop view users_with_backend;
				alter table users add column language varchar(128);
				update schema_version set version = 67;
				create view users_with_backend as select
					users.id as id, users.name as name, status, public_level,
					language, email, password, registered_at, last_seen,
					deletion_requested, deletion_notified, use_history,
					accept_follows, notifications, profile, backend_id, iris,
					hafas, efa, dbris, motis, backend.name as backend_name
					from users
					left join backends as backend on users.backend_id = backend.id
					;
			}
		);
	},
);

sub sync_stations {
+29 −0
Original line number Diff line number Diff line
@@ -874,6 +874,35 @@ sub webhook {
	$self->render( 'webhooks', hook => $hook );
}

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

	my $action   = $self->req->param('action');
	my $language = $self->req->param('language');

	if ( $action and $action eq 'save' ) {
		if ( $self->validation->csrf_protect->has_error('csrf_token') ) {
			$self->render(
				'bad_request',
				csrf   => 1,
				status => 400
			);
			return;
		}
		$self->users->set_language(
			uid      => $self->current_user->{id},
			language => $language,
		);
		$self->flash( success => 'language' );
		$self->redirect_to('account');
	}
	else {
		my @languages = @{ $self->current_user->{languages} };
		$self->param( language => $languages[0] // q{} );
		$self->render('language');
	}
}

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

+22 −0
Original line number Diff line number Diff line
package Travelynx::Helper::Locales;

use strict;
use warnings;

use base qw(Locale::Maketext);

our %lexicon = (
	_AUTO => 1,
);

use Locale::Maketext::Lexicon {
	_decode => 1,
	'*'     => [ Gettext => 'share/locales/*.po' ],
};

sub init {
	my ($self) = @_;
	return $self->SUPER::init( @_[ 1 .. $#_ ] );
}

1;
Loading