summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md186
-rw-r--r--doc/contributing.md33
-rw-r--r--doc/setup.md136
-rw-r--r--doc/usage.md34
-rwxr-xr-xlib/Travelynx.pm29
-rw-r--r--lib/Travelynx/Command/translation.pm99
-rwxr-xr-xlib/Travelynx/Controller/Profile.pm9
-rwxr-xr-xlib/Travelynx/Controller/Traveling.pm10
-rwxr-xr-xlib/Travelynx/Model/Journeys.pm91
-rw-r--r--share/locales/de_DE.po20
-rw-r--r--share/locales/en_GB.po26
-rw-r--r--share/locales/reference.md1416
-rw-r--r--share/locales/template.pot54
-rw-r--r--t/r-negative-delay.t2
-rw-r--r--templates/account.html.ep6
-rw-r--r--templates/changelog.html.ep16
-rw-r--r--templates/journey.html.ep10
17 files changed, 1941 insertions, 236 deletions
diff --git a/README.md b/README.md
index a8e5bc7..aaf37b0 100644
--- a/README.md
+++ b/README.md
@@ -13,183 +13,13 @@ Instances](https://finalrewind.org/projects/Travel-Status-DE-HAFAS/). Support
for EFA instances and bahn.de is under way.
You can use the public instance on [travelynx.de](https://travelynx.de) or
-host your own. See the Installation and Setup notes below.
+host your own. Further reading:
-Dependencies
----
-
- * perl ≥ 5.20
- * carton
- * build-essential
- * libpq-dev
- * git
-
-Installation
----
-
-travelynx depends on a set of Perl modules which are documented in `cpanfile`.
-After installing the dependencies mentioned above, you can use carton to
-install Perl depenencies locally. You may alsobe able to use cpanminus;
-however this method is untested.
-
-In the project root directory (where `cpanfile` resides), run
-
-```
-carton install --deployment
-```
-
-and set `PERL5LIB=.../local/lib/perl5` before executing any travelynx
-commands (see configs in the examples directory) or wrap them with `carton
-exec`, e.g. `carton exec hypnotoad index.pl`
-
-Setup
----
-
-First, you need to set up a PostgreSQL database so that travelynx can store
-user accounts and journeys. It must be at least version 9.4 and must use a
-UTF-8 locale. The following steps describe setup on a Debian 9 system;
-setup on other distributions should be similar.
-
-* Write down a strong random password
-* Create a postgres user for travelynx: `sudo -u postgres createuser -P travelynx`
- (enter password when prompted)
-* Create the database: `sudo -u postgres createdb -O travelynx travelynx`
-* Copy `examples/travelynx.conf` to the application root directory
- (the one in which `index.pl` resides) and edit it. Make sure to configure
- db, cache, mail, and secrets.
-* Initialize the database: `carton exec perl index.pl database migrate`
- or `PERL5LIB=local/lib/perl5 perl index.pl database migrate`
+* [Contributing](doc/contributing.md) to travelynx development
+* [Setup](doc/setup.md) for hosting your own instance
+* [Usage](doc/usage.md) primer (what is this whole “checking in” about?)
-Your server also needs to be able to send mail. Set up your MTA of choice and
-make sure that the sendmail binary can be used for outgoing mails. Mail
-reception on the server is not required.
-
-Finally, configure the web service:
-
-* Set up a travelynx service using the service supervisor of your choice
- (see `examples/travelynx.service` for a systemd unit file)
-* Configure your web server to reverse-provy requests to the travelynx
- instance. See `examples/nginx-site` for an nginx config.
-* Install a `timeout 5m perl index.pl work -m production` cronjob. It is used
- to update realtime data and perform automatic checkout and should run
- every three minutes or so, see `examples/cron`.
-
-You can now start the travelynx service, navigate to the website and register
-your first account. There is no admin account, all management is performed
-via cron or (in non-standard cases) on the command line.
-
-Please open an issue on <https://github.com/derf/travelynx/issues> or send a
-mail to derf+travelynx@finalrewind.org if there is anything missing or
-ambiguous in this setup manual.
-
-Note that Deutsche Bahn have put parts of their API behind an IP reputation
-filter. In general, checkins with the bahn.de backend will only be possible if
-travelynx is accessing it from a residential (non-server) IP range. See the
-dbris bahn.de proxy / proxies setting in `example/travelynx.conf` for
-workarounds.
-
-Updating
----
-
-It is recommended to run travelynx directly from the git repository. When
-updating, the workflow depends on whether schema updates need to be applied
-or not.
-
-```
-git pull
-carton install --deployment # if you are using carton: update dependencies
-chmod -R a+rX . # only needed if travelynx is running under a different user
-if perl index.pl database has-current-schema; then
- systemctl reload travelynx
-else
- systemctl stop travelynx
- perl index.pl database migrate
- systemctl start travelynx
-fi
-```
-
-Note that this is subject to change -- the application may perform schema
-updates automatically in the future. If you used carton for installation,
-use `carton exec perl ...` in the snippet above; otherwise, export
-`PERL5LIB=.../local/lib/perl5`.
-
-Setup with Docker
----
-
-Note that travelynx Docker support is experimental and, in its current form,
-far from best practices. Pull requests are appreciated.
-
-First, you need to set up a PostgreSQL database so that travelynx can store
-user accounts and journeys. It must be at least version 9.4 and must use a
-UTF-8 locale. See above (or `examples/docker/postgres-init.sh`) for database
-initialization. You do not need to perform the `database migrate` step.
-
-Next, you need to prepare three files that will be mounted into the travelynx
-container: travelynx configuration, e-mail configuration, and imprint and
-privacy policy. For the sake of this readme, we assume that you are using the
-`local/` directory to store these
-
-* `mkdir local`
-* copy examples/travelynx.conf to local/travelynx.conf and configure it.
-* copy examples/docker/email-transport.sh to local/email-transport.sh and configure it.
- The travelynx container does not contain a mail server, so it needs a
- separate SMTP server to send mail. It does not receive mail.
-* create local/imprint.html.ep and enter imprint as well as privacy policy data.
-* create local/terms-of-service.html.ep and enter your terms of service.
-* Configure your web server to reverse-provy requests to the travelynx
- instance. See `examples/nginx-site` for an nginx config.
-
-travelynx consists of two runtimes: the web application and a background
-worker. Your service supervisor (or docker compose / docker stack / kubernetes
-setup) should orchestrate them somewhere along these lines.
-
-* `docker pull derfnull/travelynx:latest`
-* Start web application: `docker run -p 8093:8093 -v ${PWD}/local:/local:ro travelynx:latest`
-* Wait until localhost:8093 responds to requests
-* Start worker: `docker run -v ${PWD}/local:/local:ro travelynx:latest worker`
-
-To install an update: stop worker and web application, update the travelynx
-image, and start them again. Database migrations will be performed
-automatically. Note that downgrades are not supported.
-
-Usage
----
-
-For the sake of this manual, we will assume your travelynx instance is running
-on `travelynx.de`
-
-travelynx journey logging is based on checkin and checkout actions: You check
-into a train when boarding it, select a destination, and are automatically
-checked out when you arrive. Real-time data is saved on both occasions and
-continuously updated while in transit, providing an accurate overview of both
-scheduled and actual journey times.
-
-## Checking in
-
-You can check into a train up to 30 minutes before its scheduled departure and
-up to two hours after its actual departure (including delays).
-
-First, you need to select the station you want to check in from.
-Navigate to `travelynx.de` or click/tap on the travelynx text in the navigation
-bar. You will see a list of the five stations closest to your current location
-(as reported by your browser). Select the station you're at or enter its
-name or DS100 code manually.
-
-As soon as you select a train, you will be checked in and travelynx will switch
-to the journey / checkout view. If you already know where you're headed, you
-should click/tap on the destination station in the station list now. You can
-change the destination by selecting a new one anytime.
-
-## Checking out
-
-You are automatically checked out a few minutes after arrival at your
-destination. If the train has already arrived when you select a destination and
-its arrival was less than two hours ago, you are checked out immediately. If
-it's more than two hours, you need to perform a manual checkout (without
-arrival data) using the link at the bottom of the checkin menu's station list.
-
-Testing
----
+## Testing
The test scripts assume that travelynx.conf contains a valid database
connection. They will create a test-specific schema, perform all operations in
@@ -202,8 +32,7 @@ Please use a separate development database instead.
Run the tests by executing `prove`. Use `prove -v` for debug output and
`DBI_TRACE=SQL prove -v` to monitor SQL queries.
-Licensing
----
+## Licensing
The copyright of individual files is documented in the file's header or in
.reuse/dep5. The referenced licenses are stored in the LICENSES directory.
@@ -223,8 +52,7 @@ The easiest way of making changes available is by maintaining a public fork of
the Git repository. A tarball is also acceptable. Please change the `source`
ref in travelynx.conf if you are using a fork with custom changes.
-References
----
+## References
Mirrors of the travelynx repository are maintained at the following locations:
diff --git a/doc/contributing.md b/doc/contributing.md
new file mode 100644
index 0000000..a31ed4f
--- /dev/null
+++ b/doc/contributing.md
@@ -0,0 +1,33 @@
+# Contributing to travelynx Development
+
+First, a note upfront: travelynx is a hobby project.
+While I appreciate suggestions, bug reports, and merge requests / patches, I want to make sure that it remains a hobby project and does not turn into a chore.
+As such, please do not expect a timely response to anything you submit.
+I typically only address issues and merge requests when I have the capacity for them _and_ when doing so does not feel like a chore.
+
+That being said, I do appreciate bug reports, feature requests, and (simple!) patches, even if I may take quite a while to address or review them.
+If you are planning a more involved patch set, please get in touch first.
+
+## Translations
+
+This is probably the easiest way to improve the life of any travelynx users who are not native German speakers.
+Note that travelynx does _not_ use Weblate.
+
+### Updating or Extending Translations
+
+* Look at the [translation reference](../share/locales/reference.md)
+* Pick a language that you'd like to fix / update / extend
+* Adjust the corresponding `share/locales/ab-CD.po` file
+* Open a merge request, either on [Codeberg](https://codeberg.org/derf/travelynx/pulls) or [GitHub](https://github.com/derf/travelynx/pulls)
+
+### Adding a new Language
+
+* Copy `share/locales/template.pot` to `share/locales/ab-CD.po`, replacing ab-CD with the appropriate language code
+* Add the language / locale to `$self->helper(loc_handle …` in `lib/Travelynx.pm`
+* Add the language / locale to `templates/language.html.ep`
+* Provide as many translations as you feel comfortable with – partial translation files are fine; any entry left as `msgstr ""` will cause travelynx to fall back to English or German.
+* Open a merge request, either on [Codeberg](https://codeberg.org/derf/travelynx/pulls) or [GitHub](https://github.com/derf/travelynx/pulls)
+
+## Bug Reports
+
+You may report bugs and request features either on [Codeberg](https://codeberg.org/derf/travelynx/issues) or [GitHub](https://github.com/derf/travelynx/issues).
diff --git a/doc/setup.md b/doc/setup.md
new file mode 100644
index 0000000..82a2348
--- /dev/null
+++ b/doc/setup.md
@@ -0,0 +1,136 @@
+# Hosting your own travelynx
+
+This document describes how to host your own travelynx instance.
+
+## Dependencies
+
+ * perl ≥ 5.20
+ * carton
+ * build-essential
+ * libpq-dev
+ * git
+
+## Installation
+
+travelynx depends on a set of Perl modules which are documented in `cpanfile`.
+After installing the dependencies mentioned above, you can use carton to
+install Perl depenencies locally. You may alsobe able to use cpanminus;
+however this method is untested.
+
+In the project root directory (where `cpanfile` resides), run
+
+```
+carton install --deployment
+```
+
+and set `PERL5LIB=.../local/lib/perl5` before executing any travelynx
+commands (see configs in the examples directory) or wrap them with `carton
+exec`, e.g. `carton exec hypnotoad index.pl`
+
+## Setup
+
+First, you need to set up a PostgreSQL database so that travelynx can store
+user accounts and journeys. It must be at least version 9.4 and must use a
+UTF-8 locale. The following steps describe setup on a Debian 9 system;
+setup on other distributions should be similar.
+
+* Write down a strong random password
+* Create a postgres user for travelynx: `sudo -u postgres createuser -P travelynx`
+ (enter password when prompted)
+* Create the database: `sudo -u postgres createdb -O travelynx travelynx`
+* Copy `examples/travelynx.conf` to the application root directory
+ (the one in which `index.pl` resides) and edit it. Make sure to configure
+ db, cache, mail, and secrets.
+* Initialize the database: `carton exec perl index.pl database migrate`
+ or `PERL5LIB=local/lib/perl5 perl index.pl database migrate`
+
+Your server also needs to be able to send mail. Set up your MTA of choice and
+make sure that the sendmail binary can be used for outgoing mails. Mail
+reception on the server is not required.
+
+Finally, configure the web service:
+
+* Set up a travelynx service using the service supervisor of your choice
+ (see `examples/travelynx.service` for a systemd unit file)
+* Configure your web server to reverse-provy requests to the travelynx
+ instance. See `examples/nginx-site` for an nginx config.
+* Install a `timeout 5m perl index.pl work -m production` cronjob. It is used
+ to update realtime data and perform automatic checkout and should run
+ every three minutes or so, see `examples/cron`.
+
+You can now start the travelynx service, navigate to the website and register
+your first account. There is no admin account, all management is performed
+via cron or (in non-standard cases) on the command line.
+
+Please open an issue on <https://github.com/derf/travelynx/issues> or send a
+mail to derf+travelynx@finalrewind.org if there is anything missing or
+ambiguous in this setup manual.
+
+Note that Deutsche Bahn have put parts of their API behind an IP reputation
+filter. In general, checkins with the bahn.de backend will only be possible if
+travelynx is accessing it from a residential (non-server) IP range. See the
+dbris bahn.de proxy / proxies setting in `example/travelynx.conf` for
+workarounds.
+
+## Updating
+
+It is recommended to run travelynx directly from the git repository. When
+updating, the workflow depends on whether schema updates need to be applied
+or not.
+
+```
+git pull
+carton install --deployment # if you are using carton: update dependencies
+chmod -R a+rX . # only needed if travelynx is running under a different user
+if perl index.pl database has-current-schema; then
+ systemctl reload travelynx
+else
+ systemctl stop travelynx
+ perl index.pl database migrate
+ systemctl start travelynx
+fi
+```
+
+Note that this is subject to change -- the application may perform schema
+updates automatically in the future. If you used carton for installation,
+use `carton exec perl ...` in the snippet above; otherwise, export
+`PERL5LIB=.../local/lib/perl5`.
+
+## Setup with Docker
+---
+
+Note that travelynx Docker support is experimental and, in its current form,
+far from best practices. Pull requests are appreciated.
+
+First, you need to set up a PostgreSQL database so that travelynx can store
+user accounts and journeys. It must be at least version 9.4 and must use a
+UTF-8 locale. See above (or `examples/docker/postgres-init.sh`) for database
+initialization. You do not need to perform the `database migrate` step.
+
+Next, you need to prepare three files that will be mounted into the travelynx
+container: travelynx configuration, e-mail configuration, and imprint and
+privacy policy. For the sake of this readme, we assume that you are using the
+`local/` directory to store these
+
+* `mkdir local`
+* copy examples/travelynx.conf to local/travelynx.conf and configure it.
+* copy examples/docker/email-transport.sh to local/email-transport.sh and configure it.
+ The travelynx container does not contain a mail server, so it needs a
+ separate SMTP server to send mail. It does not receive mail.
+* create local/imprint.html.ep and enter imprint as well as privacy policy data.
+* create local/terms-of-service.html.ep and enter your terms of service.
+* Configure your web server to reverse-provy requests to the travelynx
+ instance. See `examples/nginx-site` for an nginx config.
+
+travelynx consists of two runtimes: the web application and a background
+worker. Your service supervisor (or docker compose / docker stack / kubernetes
+setup) should orchestrate them somewhere along these lines.
+
+* `docker pull derfnull/travelynx:latest`
+* Start web application: `docker run -p 8093:8093 -v ${PWD}/local:/local:ro travelynx:latest`
+* Wait until localhost:8093 responds to requests
+* Start worker: `docker run -v ${PWD}/local:/local:ro travelynx:latest worker`
+
+To install an update: stop worker and web application, update the travelynx
+image, and start them again. Database migrations will be performed
+automatically. Note that downgrades are not supported.
diff --git a/doc/usage.md b/doc/usage.md
new file mode 100644
index 0000000..2d8bb42
--- /dev/null
+++ b/doc/usage.md
@@ -0,0 +1,34 @@
+# travelynx primer
+
+For the sake of this manual, we will assume your travelynx instance is running
+on `travelynx.de`
+
+travelynx journey logging is based on checkin and checkout actions: You check
+into a train when boarding it, select a destination, and are automatically
+checked out when you arrive. Real-time data is saved on both occasions and
+continuously updated while in transit, providing an accurate overview of both
+scheduled and actual journey times.
+
+## Checking in
+
+You can check into a train at nearly any point in time, though it's usually a
+good idea to do it within a 30-minute window befor/after its departure. The
+precise constraints depend on the selected backend (i.e., data provider).
+
+First, you need to select the stop you want to check in from. Navigate to
+`travelynx.de` or click/tap on the travelynx text in the navigation bar. You
+will see a list of the five stops closest to your current location (as reported
+by your browser). Select the stop you're at or enter its name manually.
+
+As soon as you select a train, you will be checked in and travelynx will switch
+to the journey / checkout view. If you already know where you're headed, you
+should click/tap on the destination stop in the stop list now. You can change
+the destination by selecting a new one anytime.
+
+## Checking out
+
+You are automatically checked out a few minutes after arrival at your
+destination. If the train has already arrived when you select a destination and
+its arrival was less than two hours ago, you are checked out immediately. If
+it's more than two hours, you need to perform a manual checkout (without
+arrival data) using the link at the bottom of the checkin menu's stop list.
diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm
index cec4a92..607b153 100755
--- a/lib/Travelynx.pm
+++ b/lib/Travelynx.pm
@@ -2793,13 +2793,16 @@ sub startup {
my @extra_station_coordinates
= map { [ $_->{latlon}, $_->{name} ] } @extra_stations;
- my @now_coordinates = map {
- [
- $_->{now_latlon},
- $_->{train_type} . ' '
- . ( $_->{train_line} // $_->{train_no} )
- ]
- } @journeys;
+ my @now_coordinates;
+ if ( $opt{with_now_markers} ) {
+ @now_coordinates = map {
+ [
+ $_->{now_latlon},
+ $_->{train_type} . ' '
+ . ( $_->{train_line} // $_->{train_no} )
+ ]
+ } @journeys;
+ }
my @station_pairs;
my @polylines;
@@ -2826,10 +2829,14 @@ sub startup {
my $from_eva = $journey->{from_eva} // $journey->{dep_eva};
my $to_eva = $journey->{to_eva} // $journey->{arr_eva};
- my $from_index
- = first_index { $_->[2] and $_->[2] == $from_eva } @polyline;
- my $to_index
- = first_index { $_->[2] and $_->[2] == $to_eva } @polyline;
+ # poly_dep_index, poly_arr_index are only available for
+ # journeys that were processed by get_travel_distance
+ # beforehand. However, they are much less error-prone than this
+ # first_index / last_index kludge when it comes to ring lines.
+ my $from_index = $journey->{poly_dep_index}
+ // first_index { $_->[2] and $_->[2] == $from_eva } @polyline;
+ my $to_index = $journey->{poly_arr_index}
+ // first_index { $_->[2] and $_->[2] == $to_eva } @polyline;
# Work around inconsistencies caused by a multiple EVA IDs mapping to the same station name
if ( $from_index == -1 ) {
diff --git a/lib/Travelynx/Command/translation.pm b/lib/Travelynx/Command/translation.pm
new file mode 100644
index 0000000..cc3a5ac
--- /dev/null
+++ b/lib/Travelynx/Command/translation.pm
@@ -0,0 +1,99 @@
+package Travelynx::Command::translation;
+
+# Copyright (C) 2025 Birte Kristina Friesel
+#
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+use Mojo::Base 'Mojolicious::Command';
+use Travelynx::Helper::Locales;
+
+has description => 'Export translation status';
+
+has usage => sub { shift->extract_usage };
+
+sub run {
+ my ( $self, $command ) = @_;
+
+ my @locales = (qw(de-DE en-GB fr-FR hu-HU pl-PL));
+
+ my %count;
+ my %handle;
+ for my $locale (@locales) {
+ $handle{$locale} = Travelynx::Helper::Locales->get_handle($locale);
+ $handle{$locale}->fail_with('failure_handler_auto');
+ $count{$locale} = 0;
+ }
+
+ binmode( STDOUT, ':encoding(utf-8)' );
+
+ if ( not $command ) {
+ $self->help;
+ }
+ elsif ( $command eq 'update-ref' ) {
+ my @buf;
+
+ open( my $fh, '<:encoding(utf-8)', 'share/locales/de_DE.po' );
+ my $comment;
+ for my $line (<$fh>) {
+ chomp $line;
+ if ( $line =~ m{ ^ [#] \s+ (.*) $ }x ) {
+ push( @buf, "## $1\n" );
+ }
+ elsif ( $line =~ m{ ^ [#] , \s+ (.*) $ }x ) {
+ $comment = $1;
+ }
+ elsif ( $line =~ m{ ^ msgid \s+ " (.*) " $ }x ) {
+ my $id = $1;
+ push( @buf, "### ${id}\n" );
+ if ($comment) {
+ push( @buf, '*' . $comment . "*\n" );
+ $comment = undef;
+ }
+ for my $locale (@locales) {
+ my $translation = $handle{$locale}->maketext($id);
+ if ( $translation ne $id ) {
+ push( @buf, "* ${locale}: ${translation}" );
+ $count{$locale} += 1;
+ }
+ else {
+ push( @buf, "* ${locale} *missing*" );
+ }
+ }
+ push( @buf, q{} );
+ }
+ }
+ close($fh);
+
+ open( $fh, '>:encoding(utf-8)', 'share/locales/reference.md' );
+ say $fh '# Translation Status';
+ say $fh q{};
+ for my $locale (@locales) {
+ say $fh sprintf(
+ '* %s: %.1f%% complete (%d missing)',
+ $locale,
+ $count{$locale} * 100 / $count{'de-DE'},
+ $count{'de-DE'} - $count{$locale},
+ );
+ }
+ say $fh q{};
+ for my $line (@buf) {
+ say $fh $line;
+ }
+ close($fh);
+ }
+ else {
+ $self->help;
+ }
+}
+
+1;
+
+__END__
+
+=head1 SYNOPSIS
+
+ Usage: index.pl translation <command>
+
+ Supported commands:
+
+ * update-ref: update share/locales/reference.md
diff --git a/lib/Travelynx/Controller/Profile.pm b/lib/Travelynx/Controller/Profile.pm
index db30d36..978e3f8 100755
--- a/lib/Travelynx/Controller/Profile.pm
+++ b/lib/Travelynx/Controller/Profile.pm
@@ -114,7 +114,8 @@ sub profile {
my $map_data = {};
if ( $status->{checked_in} ) {
$map_data = $self->journeys_to_map_data(
- journeys => [$status],
+ journeys => [$status],
+ with_now_markers => 1,
);
}
@@ -506,7 +507,8 @@ sub user_status {
my $map_data = {};
if ( $status->{checked_in} ) {
$map_data = $self->journeys_to_map_data(
- journeys => [$status],
+ journeys => [$status],
+ with_now_markers => 1,
);
}
@@ -600,7 +602,8 @@ sub status_card {
if ( $status->{checked_in} ) {
$map_data = $self->journeys_to_map_data(
- journeys => [$status],
+ journeys => [$status],
+ with_now_markers => 1,
);
}
diff --git a/lib/Travelynx/Controller/Traveling.pm b/lib/Travelynx/Controller/Traveling.pm
index a821f3a..e23301e 100755
--- a/lib/Travelynx/Controller/Traveling.pm
+++ b/lib/Travelynx/Controller/Traveling.pm
@@ -370,8 +370,9 @@ sub homepage {
my $map_data = {};
if ( $status->{arr_name} ) {
$map_data = $self->journeys_to_map_data(
- journeys => [$status],
- show_full_route => 1,
+ journeys => [$status],
+ show_full_route => 1,
+ with_now_markers => 1,
);
}
my $journey_visibility
@@ -461,8 +462,9 @@ sub status_card {
my $map_data = {};
if ( $status->{arr_name} ) {
$map_data = $self->journeys_to_map_data(
- journeys => [$status],
- show_full_route => 1,
+ journeys => [$status],
+ show_full_route => 1,
+ with_now_markers => 1,
);
}
my $journey_visibility
diff --git a/lib/Travelynx/Model/Journeys.pm b/lib/Travelynx/Model/Journeys.pm
index 5e6195f..9efa365 100755
--- a/lib/Travelynx/Model/Journeys.pm
+++ b/lib/Travelynx/Model/Journeys.pm
@@ -13,7 +13,7 @@ use DateTime;
use DateTime::Format::Strptime;
use GIS::Distance;
use JSON;
-use List::MoreUtils qw(after_incl before_incl);
+use List::MoreUtils qw(after_incl before_incl first_index last_index);
my %visibility_itoa = (
100 => 'public',
@@ -704,7 +704,7 @@ sub get {
for my $stop ( @{ $ref->{route} } ) {
for my $k (qw(rt_arr rt_dep sched_arr sched_dep)) {
if ( $stop->[2]{$k} ) {
- $stop->[2]{$k} = epoch_to_dt( $stop->[2]{$k} );
+ $stop->[2]{"${k}_dt"} = epoch_to_dt( $stop->[2]{$k} );
}
}
}
@@ -1190,9 +1190,11 @@ sub get_travel_distance {
my $from = $journey->{from_name};
my $from_eva = $journey->{from_eva};
my $from_latlon = $journey->{from_latlon};
+ my $from_ts = $journey->{sched_dep_ts} // $journey->{rt_dep_ts};
my $to = $journey->{to_name};
my $to_eva = $journey->{to_eva};
my $to_latlon = $journey->{to_latlon};
+ my $to_ts = $journey->{sched_arr_ts} // $journey->{rt_arr_ts};
my $route_ref = $journey->{route};
my $polyline_ref = $journey->{polyline};
@@ -1246,31 +1248,82 @@ sub get_travel_distance {
my $geo = GIS::Distance->new();
my $distance_beeline
= $geo->distance_metal( @{$from_latlon}, @{$to_latlon} );
- my @route
- = after_incl { ( $_->[1] and $_->[1] == $from_eva ) or $_->[0] eq $from }
+
+ # A trip may pass the same stop multiple times.
+ # Thus, two criteria must be met to select the start/end of the actual route:
+ # * stop name or ID matches, and
+ # * one of:
+ # - arrival/departure time at the stop matches, or
+ # - the stop does not have arrival/departure time
+ # In the latter case, we still face the risk of selecting the wrong
+ # start/end stop. However, we have no way of finding the right one. As the
+ # majority of trips do not pass the same stop multiple times, it's better
+ # to risk having a few inaccurate distances than not calculating the
+ # distance for any journey that lacks sched_dep/rt_dep or
+ # sched_from/rt_from.
+
+ my $route_start = first_index {
+ (
+ ( $_->[1] and $_->[1] == $from_eva or $_->[0] eq $from )
+ and ( not( defined $_->[2]{sched_dep} or defined $_->[2]{rt_dep} )
+ or ( $_->[2]{sched_dep} // $_->[2]{rt_dep} ) == $from_ts )
+ )
+ }
@{$route_ref};
- @route
- = before_incl { ( $_->[1] and $_->[1] == $to_eva ) or $_->[0] eq $to }
- @route;
- if (
- @route < 2
- or ( $route[-1][0] ne $to
- and ( not $route[-1][1] or $route[-1][1] != $to_eva ) )
- )
- {
+ # Here, we need to use last_index. In case of ring lines, the first index
+ # will not have sched_arr/rt_arr set, but we should not select it as route
+ # end...
+ my $route_end = last_index {
+ (
+ ( $_->[1] and $_->[1] == $to_eva or $_->[0] eq $to )
+ and ( not( defined $_->[2]{sched_arr} or defined $_->[2]{rt_arr} )
+ or ( $_->[2]{sched_arr} // $_->[2]{rt_arr} ) == $to_ts )
+ )
+ }
+ @{$route_ref};
- # I AM ERROR
+ if ( not defined $route_start and defined $route_end ) {
return ( 0, 0, $distance_beeline );
}
- my @polyline = after_incl { $_->[2] and $_->[2] == $from_eva }
+ my %seen;
+ for my $stop ( @{$route_ref} ) {
+ $seen{ $stop->[1] } //= 1;
+ $stop->[2]{n} = $seen{ $stop->[1] };
+ $seen{ $stop->[1] } += 1;
+ }
+
+ # Assumption: polyline entries are always [lat, lon] or [lat, lon, stop ID]
+ %seen = ();
+ for my $entry ( @{ $polyline_ref // [] } ) {
+ if ( $entry->[2] ) {
+ $seen{ $entry->[2] } //= 1;
+ $entry->[3] = $seen{ $entry->[2] };
+ $seen{ $entry->[2] } += 1;
+ }
+ }
+
+ $journey->{route_dep_index} = $route_start;
+ $journey->{route_arr_index} = $route_end;
+
+ my @route = @{$route_ref}[ $route_start .. $route_end ];
+
+ # Just like the route, the polyline may contain the same stop more than
+ # once. So we need to select based on the seen counter.
+ my $poly_start = first_index {
+ $_->[2] and $_->[2] == $from_eva and $_->[3] == $route[0][2]{n}
+ }
+ @{ $polyline_ref // [] };
+ my $poly_end = first_index {
+ $_->[2] and $_->[2] == $to_eva and $_->[3] == $route[-1][2]{n}
+ }
@{ $polyline_ref // [] };
- @polyline
- = before_incl { $_->[2] and $_->[2] == $to_eva } @polyline;
- # ensure that before_incl matched -- otherwise, @polyline is too long
- if ( @polyline and $polyline[-1][2] == $to_eva ) {
+ if ( defined $poly_start and defined $poly_end ) {
+ $journey->{poly_dep_index} = $poly_start;
+ $journey->{poly_arr_index} = $poly_end;
+ my @polyline = @{$polyline_ref}[ $poly_start .. $poly_end ];
my $prev_station = shift @polyline;
for my $station (@polyline) {
$distance_polyline += $geo->distance_metal(
diff --git a/share/locales/de_DE.po b/share/locales/de_DE.po
index 028dda0..97e3877 100644
--- a/share/locales/de_DE.po
+++ b/share/locales/de_DE.po
@@ -152,10 +152,10 @@ msgstr "Webhook"
msgid "account.webhook.disabled"
msgstr "Nicht eingerichtet"
-msgid "account.webhook.active_pending"
+msgid "account.webhook.active-pending"
msgstr "Aktiv, noch nicht ausgeführt"
-msgid "account.webhook.active_error"
+msgid "account.webhook.active-error"
msgstr "Aktiv, fehlerhaft"
msgid "account.webhook.active"
@@ -164,7 +164,7 @@ msgstr "Aktiv"
msgid "account.traewelling.unsupported"
msgstr "Wird wegen Inkompatibilität zwischen bahn.de und transitous derzeit nicht unterstützt"
-msgid "account.registration_date"
+msgid "account.registration-date"
msgstr "Registriert am"
msgid "account.interaction"
@@ -188,6 +188,20 @@ msgstr "offene Anfragen"
msgid "account.interaction.disabled"
msgstr "Accounts können dir nicht folgen"
+# changelog.html.ep
+
+msgid "changelog.added"
+msgstr "Neues Feature"
+
+msgid "changelog.bugfix"
+msgstr "Bugfix"
+
+msgid "changelog.2-16.1"
+msgstr "Infrastruktur zur Übersetzung von travelynx in andere Sprachen. Der Großteil der Website ist noch Deutsch; einzelne Seiten sind bereits auf Englisch und Teilmengen davon zusätzlich auf Französisch, Polnisch und Ungarisch verfügbar. Weitere Übersetzungen und ggf. Sprachen sowie Dokumentation zur Unterstützung dabei folgen bei Zeiten. Die Sprache wird auf Basis des Accept-Language-Headers ausgewählt und kann zusätzlich in den Account-Einstellungen konfiguriert werden."
+
+msgid "changelog.2-16.2"
+msgstr "Korrekte Berechnung und Visualisierung der Wegstrecke bei Fahrten mit Ringlinien. Die Anzeige der Fahrten in der Fahrtenkarte ist noch teilweise fehlerhaft."
+
# journey.html.ep
msgid "journey.not-found"
diff --git a/share/locales/en_GB.po b/share/locales/en_GB.po
index e4bb7aa..6121045 100644
--- a/share/locales/en_GB.po
+++ b/share/locales/en_GB.po
@@ -150,13 +150,13 @@ msgid "account.webhook"
msgstr "Webhook"
msgid "account.webhook.disabled"
-msgstr "No webhook was configured"
+msgstr "No webhook configured"
-msgid "account.webhook.active_pending"
-msgstr "Active and Pending"
+msgid "account.webhook.active-pending"
+msgstr "Active; pending"
-msgid "account.webhook.active_error"
-msgstr "Active with Error"
+msgid "account.webhook.active-error"
+msgstr "Active; erroneous"
msgid "account.webhook.active"
msgstr "Active"
@@ -164,7 +164,7 @@ msgstr "Active"
msgid "account.traewelling.unsupported"
msgstr "Unsupported due to incompatibility with bahn.de and transitous"
-msgid "account.registration_date"
+msgid "account.registration-date"
msgstr "Registered on"
msgid "account.interaction"
@@ -188,6 +188,20 @@ msgstr "open requests"
msgid "account.interaction.disabled"
msgstr "Accounts cannot follow you"
+# changelog.html.ep
+
+msgid "changelog.added"
+msgstr "New Feature"
+
+msgid "changelog.bugfix"
+msgstr "Bugfix"
+
+msgid "changelog.2-16.1"
+msgstr "Localization support. Parts of travelynx are now available in English, and a subset of those is also available in French, Hungarian, and Polish. Further translations, languages, and translation how-tos will follow in due time. Locale selection respects the Accept-Language header and can be configured on the account settings page."
+
+msgid "changelog.2-16.2"
+msgstr "Fix distance calculation and visualization of ring line trips. Trips shown in the history map are still partially incorrect."
+
# journey.html.ep
msgid "journey.not-found"
diff --git a/share/locales/reference.md b/share/locales/reference.md
new file mode 100644
index 0000000..5b3fb6c
--- /dev/null
+++ b/share/locales/reference.md
@@ -0,0 +1,1416 @@
+# Translation Status
+
+* de-DE: 100.0% complete (0 missing)
+* en-GB: 100.0% complete (0 missing)
+* fr-FR: 35.4% complete (106 missing)
+* hu-HU: 85.4% complete (24 missing)
+* pl-PL: 35.4% complete (106 missing)
+
+###
+
+* de-DE: Language: de-DE
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Plural-Forms: nplurals=2; plural=n != 1;
+
+* en-GB: Language: en-GB
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Plural-Forms: nplurals=2; plural=n != 1;
+
+* fr-FR: Language: fr-FR
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Plural-Forms: nplurals=2; plural=(n > 1);
+
+* hu-HU: Language: hu-HU
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Plural-Forms: nplurals=2; plural=n != 1;
+
+* pl-PL: Language: pl-PL
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n>=2 && n<=4 && (n<10 || n>=20) ? 1 : 2);
+
+
+## Global Strings
+
+### strftime.datetime
+
+* de-DE: %d.%m.%Y %H:%M %Z
+* en-GB: %Y-%m-%d %H:%M %Z
+* fr-FR: %d/%m/%Y %Hh%M %Z
+* hu-HU *missing*
+* pl-PL: %d.%m.%Y %H:%M %Z
+
+### button.register
+
+* de-DE: Registrieren
+* en-GB: Register
+* fr-FR: Inscription
+* hu-HU: Regisztrálás
+* pl-PL: Rejestracja
+
+### button.login
+
+* de-DE: Anmelden
+* en-GB: Login
+* fr-FR: Connexion
+* hu-HU: Bejelentkezés
+* pl-PL: Logowanie
+
+### button.logout
+
+* de-DE: Abmelden
+* en-GB: Logout
+* fr-FR: Déconnexion
+* hu-HU: Kijelentkezés
+* pl-PL: Wyloguj się
+
+### footer.imprint
+
+* de-DE: Impressum
+* en-GB: Imprint
+* fr-FR: Mentions légales
+* hu-HU: Impresszum
+* pl-PL: Imprint
+
+### footer.privacy
+
+* de-DE: Datenschutz
+* en-GB: Privacy
+* fr-FR: Données personnelles
+* hu-HU: Adatvédelem
+* pl-PL: Prywatność
+
+### footer.legend
+
+* de-DE: Legende
+* en-GB: Legend
+* fr-FR: Légende
+* hu-HU: Jelmagyarázat
+* pl-PL: Legenda
+
+### footer.colour-scheme
+
+* de-DE: Farbschema
+* en-GB: Display Mode
+* fr-FR: Affichage
+* hu-HU: Színséma
+* pl-PL: Tryb wyświetlania
+
+### footer.colour-scheme.light
+
+* de-DE: hell
+* en-GB: light
+* fr-FR: clair
+* hu-HU: világos
+* pl-PL: jasny
+
+### footer.colour-scheme.dark
+
+* de-DE: dunkel
+* en-GB: dark
+* fr-FR: sombre
+* hu-HU: sötét
+* pl-PL: ciemny
+
+### footer.colour-scheme.auto
+
+* de-DE: automatisch
+* en-GB: auto
+* fr-FR: auto
+* hu-HU: automatikus
+* pl-PL: automatyczny
+
+### header.error
+
+* de-DE: Fehler
+* en-GB: Error
+* fr-FR *missing*
+* hu-HU: Hiba
+* pl-PL *missing*
+
+## Templates
+
+## about.html.ep
+
+### about.developed-by.lead
+
+* de-DE: Entwickelt von
+* en-GB: Developed by
+* fr-FR *missing*
+* hu-HU: Fejlesztették:
+* pl-PL *missing*
+
+### about.developed-by.and
+
+* de-DE: und
+* en-GB: and
+* fr-FR *missing*
+* hu-HU: ,
+* pl-PL *missing*
+
+### about.developed-by.others
+
+* de-DE: weiteren
+* en-GB: others
+* fr-FR *missing*
+* hu-HU: többiek
+* pl-PL *missing*
+
+### about.developed-by.tail
+
+* de-DE:
+* en-GB:
+* fr-FR *missing*
+* hu-HU:
+* pl-PL *missing*
+
+### about.source-code
+
+* de-DE: Quelltext
+* en-GB: Source code
+* fr-FR *missing*
+* hu-HU: A Forráskód
+* pl-PL *missing*
+
+### about.licence-agplv3
+
+* de-DE: lizensiert unter AGPL v3
+* en-GB: available under the terms of AGPL v3
+* fr-FR *missing*
+* hu-HU: az AGPL v3 licensz alatt elérhető
+* pl-PL *missing*
+
+### about.data-sources
+
+* de-DE: Backends
+* en-GB: Backends
+* fr-FR *missing*
+* hu-HU: Backendek:
+* pl-PL *missing*
+
+### about.data-sources.last-and
+
+* de-DE: und
+* en-GB: , and
+* fr-FR *missing*
+* hu-HU: és
+* pl-PL *missing*
+
+### about.disclaimer
+
+* de-DE: Travelynx ist ein kostenfreies, privat betriebenes Projekt ohne Verfügbarkeitsgarantie. Unangekündigte Downtimes oder eine kurzfristige Einstellung dieser Seite sind nicht vorgesehen, aber möglich. Feature Requests, Bug Reports und sonstige Nachrichten werden je nach Kapazität und Motivation zeitnah, verzögert oder gar nicht bearbeitet / beantwortet.
+* en-GB: Travelynx is a hobby project. It is provided free of charge, without any kind of availability guarantees. Unexpected downtimes or a cancellation of the entire site on short notice are not planned, but always possible. Depending on available spare time and motivation, feature requests, bug reports, and other messages are processed promptly, with delay, or not at all.
+* fr-FR *missing*
+* hu-HU: Travelynx egy ingyenes és bármi rendelkezésre állási garacia nélküli hobbi projekt. Be nem tervezett leállások, illetve a teljes oldal hírtelen bezárása nincs tervben, de bármikor előfordulhat. Feature requesteket, bug reportokat és egyéb üzeneteket kapacitástól és motivációtól függően azonnal, megkésve vagy akár soha sem lesznek feldolgozva.
+* pl-PL *missing*
+
+### about.contact
+
+* de-DE: Kontakt
+* en-GB: Contact
+* fr-FR *missing*
+* hu-HU: Kapcsolat
+* pl-PL *missing*
+
+### about.bugs
+
+* de-DE: Bugs
+* en-GB: Bugs
+* fr-FR *missing*
+* hu-HU: Bugok
+* pl-PL *missing*
+
+### about.changelog
+
+* de-DE: Änderungen
+* en-GB: Changelog
+* fr-FR *missing*
+* hu-HU: Changelog
+* pl-PL *missing*
+
+## account.html.ep
+
+### account.changed-name
+
+* de-DE: Name geändert
+* en-GB: Updated name
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.changed-mail
+
+* de-DE: Mail-Adresse geändert
+* en-GB: Updated email address
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.changed-password
+
+* de-DE: Passwort geändert
+* en-GB: Updated password
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.changed-language
+
+* de-DE: Sprache geändert
+* en-GB: Changed language
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.changed-privacy
+
+* de-DE: Einstellungen zu öffentlichen Account-Daten geändert
+* en-GB: Privacy settings have been saved
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.changed-social
+
+* de-DE: Einstellungen zur Interaktionen mit anderen Accounts geändert
+* en-GB: Social settings have been saved
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.changed-traewelling
+
+* de-DE: Träwelling-Verknüpfung aktualisiert
+* en-GB: Träwelling settings have been saved
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.changed-history
+
+* de-DE: Einstellungen zu vorgeschlagenen Verbindungen geändert
+* en-GB: Connection suggestion settings have been saved
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.changed-webhook
+
+* de-DE: Web Hook aktualisiert
+* en-GB: Web Hook has been updated
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.cleared-notifications
+
+* de-DE: Benachrichtigungen gelesen
+* en-GB: Notifications have been cleared
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.account
+
+* de-DE: Account
+* en-GB: Account
+* fr-FR: Mon compte
+* hu-HU: Fiók
+* pl-PL: Konto
+
+### account.name
+
+* de-DE: Name
+* en-GB: Name
+* fr-FR: Nom
+* hu-HU: Név
+* pl-PL: Nazwa
+
+### account.mail
+
+* de-DE: E-Mail
+* en-GB: E-Mail
+* fr-FR: E-mail
+* hu-HU: E-Mail
+* pl-PL: Adres e-mail
+
+### account.password
+
+* de-DE: Passwort
+* en-GB: Password
+* fr-FR: Mot de passe
+* hu-HU: Jelszó
+* pl-PL: Hasło
+
+### account.language
+
+* de-DE: Sprache
+* en-GB: Language
+* fr-FR: Langue
+* hu-HU: Nyelv
+* pl-PL: Język
+
+### account.connections
+
+* de-DE: Verbindungen
+* en-GB: Connections
+* fr-FR: Itinéraires préférés
+* hu-HU: Átszállások
+* pl-PL: Połączenia
+
+### account.connections.enabled
+
+* de-DE: Vorschläge aktiv
+* en-GB: Suggestions enabled
+* fr-FR: Suggestions activées
+* hu-HU: Javaslatok aktiválva
+* pl-PL: Sugestie włączone
+
+### account.connections.disabled
+
+* de-DE: Vorschläge deaktiviert
+* en-GB: Suggestions disabled
+* fr-FR: Suggestions désactivées
+* hu-HU: Javaslatok deaktiválva
+* pl-PL: Sugestie wyłączone
+
+### account.visibility
+
+* de-DE: Sichtbarkeit
+* en-GB: Visibility
+* fr-FR: Visibilité
+* hu-HU: Láthatóság
+* pl-PL: Widoczność
+
+### account.webhook
+
+* de-DE: Webhook
+* en-GB: Webhook
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.webhook.disabled
+
+* de-DE: Nicht eingerichtet
+* en-GB: No webhook was configured
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.webhook.active_pending
+
+* de-DE: Aktiv, noch nicht ausgeführt
+* en-GB: Active and Pending
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.webhook.active_error
+
+* de-DE: Aktiv, fehlerhaft
+* en-GB: Active with Error
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.webhook.active
+
+* de-DE: Aktiv
+* en-GB: Active
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.traewelling.unsupported
+
+* de-DE: Wird wegen Inkompatibilität zwischen bahn.de und transitous derzeit nicht unterstützt
+* en-GB: Unsupported due to incompatibility with bahn.de and transitous
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.registration_date
+
+* de-DE: Registriert am
+* en-GB: Registered on
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### account.interaction
+
+* de-DE: Interaktion
+* en-GB: Interaction
+* fr-FR: Intéraction
+* hu-HU: Interakció
+* pl-PL: Interakcje
+
+### account.interaction.accept-follows
+
+* de-DE: Accounts können dir direkt folgen
+* en-GB: Accounts may follow you
+* fr-FR: Autoriser à suivre
+* hu-HU: Bárki követhet
+* pl-PL: Inne konta mogą cię obserwować
+
+### account.interaction.accept-follow-requests
+
+* de-DE: Accounts können dir auf Anfrage folgen
+* en-GB: Accounts may send follow requests
+* fr-FR: Autoriser à recevoir une demande de suivi
+* hu-HU: Bárki küldhet követési kéréseket
+* pl-PL: Inne konta mogą wysyłać ci prośby o obserwowanie
+
+### account.interaction.one
+
+* de-DE: eine
+* en-GB: one
+* fr-FR: une
+* hu-HU: egy
+* pl-PL: jedna
+
+### account.interaction.open-request
+
+* de-DE: offene Anfrage
+* en-GB: open request
+* fr-FR: requête en attente
+* hu-HU: követési kérés
+* pl-PL: otwarta prośba
+
+### account.interaction.open-requests
+
+* de-DE: offene Anfragen
+* en-GB: open requests
+* fr-FR: requêtes en attente
+* hu-HU: követési kérések
+* pl-PL: otwarte prośby
+
+### account.interaction.disabled
+
+* de-DE: Accounts können dir nicht folgen
+* en-GB: Accounts cannot follow you
+* fr-FR: Aucun compte ne peut vous suivre
+* hu-HU: Senki sem követhet
+* pl-PL: Inne konta nie mogą cię obserwować
+
+## changelog.html.ep
+
+### changelog.added
+
+* de-DE: Neues Feature
+* en-GB: New Feature
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### changelog.bugfix
+
+* de-DE: Bugfix
+* en-GB: Bugfix
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### changelog.2-16.1
+
+* de-DE: Infrastruktur zur Übersetzung von travelynx in andere Sprachen. Der Großteil der Website ist noch Deutsch; einzelne Seiten sind bereits auf Englisch und Teilmengen davon zusätzlich auf Französisch, Polnisch und Ungarisch verfügbar. Weitere Übersetzungen und ggf. Sprachen sowie Dokumentation zur Unterstützung dabei folgen bei Zeiten. Die Sprache wird auf Basis des Accept-Language-Headers ausgewählt und kann zusätzlich in den Account-Einstellungen konfiguriert werden.
+* en-GB: Localization support. Parts of travelynx are now available in English, and a subset of those is also available in French, Hungarian, and Polish. Further translations, languages, and translation how-tos will follow in due time. Locale selection respects the Accept-Language header and can be configured on the account settings page.
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### changelog.2-16.2
+
+* de-DE: Korrekte Berechnung und Visualisierung der Wegstrecke bei Fahrten mit Ringlinien. Die Anzeige der Fahrten in der Fahrtenkarte ist noch teilweise fehlerhaft.
+* en-GB: Fix distance calculation and visualization of ring line trips. Trips shown in the history map are still partially incorrect.
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+## journey.html.ep
+
+### journey.not-found
+
+* de-DE: Fahrt nicht gefunden.
+* en-GB: Trip not found.
+* fr-FR *missing*
+* hu-HU: Utazás nem találva.
+* pl-PL *missing*
+
+### journey.trip
+
+*short*
+
+* de-DE: Fahrt
+* en-GB: Trip
+* fr-FR *missing*
+* hu-HU: Utazás
+* pl-PL *missing*
+
+### journey.from
+
+*short*
+
+* de-DE: Von
+* en-GB: From
+* fr-FR *missing*
+* hu-HU: Honnan
+* pl-PL *missing*
+
+### journey.to
+
+*short*
+
+* de-DE: Nach
+* en-GB: To
+* fr-FR *missing*
+* hu-HU: Hova
+* pl-PL *missing*
+
+### journey.departure
+
+*short*
+
+* de-DE: Abfahrt
+* en-GB: Departure
+* fr-FR *missing*
+* hu-HU: Indulás
+* pl-PL *missing*
+
+### journey.arrival
+
+*short*
+
+* de-DE: Ankunft
+* en-GB: Arrival
+* fr-FR *missing*
+* hu-HU: Érkezés
+* pl-PL *missing*
+
+### journey.distance
+
+*short*
+
+* de-DE: Strecke
+* en-GB: Distance
+* fr-FR *missing*
+* hu-HU: Távolság
+* pl-PL *missing*
+
+### journey.beeline.pre
+
+* de-DE: (Luftlinie:
+* en-GB: (
+* fr-FR *missing*
+* hu-HU: (Légvonalban:
+* pl-PL *missing*
+
+### journey.beeline.post
+
+* de-DE: )
+* en-GB: as the crow flies)
+* fr-FR *missing*
+* hu-HU: )
+* pl-PL *missing*
+
+### journey.speed
+
+*short*
+
+* de-DE: Tempo
+* en-GB: Speed
+* fr-FR *missing*
+* hu-HU: Sebesség
+* pl-PL *missing*
+
+### journey.operator
+
+*short*
+
+* de-DE: Betrieb
+* en-GB: Operator
+* fr-FR *missing*
+* hu-HU: Üzemeltető
+* pl-PL *missing*
+
+### journey.messages
+
+*short*
+
+* de-DE: Meldungen
+* en-GB: Messages
+* fr-FR *missing*
+* hu-HU: Jelentések
+* pl-PL *missing*
+
+### journey.comment
+
+*short*
+
+* de-DE: Kommentar
+* en-GB: Comment
+* fr-FR *missing*
+* hu-HU: Megjegyzés
+* pl-PL *missing*
+
+### journey.carriages
+
+*short*
+
+* de-DE: Rollmaterial
+* en-GB: Carriages
+* fr-FR *missing*
+* hu-HU: Járművek
+* pl-PL *missing*
+
+### journey.route
+
+*short*
+
+* de-DE: Route
+* en-GB: Route
+* fr-FR *missing*
+* hu-HU: Útvonal
+* pl-PL *missing*
+
+### journey.share
+
+* de-DE: Teilen
+* en-GB: Share
+* fr-FR *missing*
+* hu-HU: Megosztás
+* pl-PL *missing*
+
+### journey.export
+
+* de-DE: Exportieren
+* en-GB: Export
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### journey.edit
+
+* de-DE: Bearbeiten
+* en-GB: Edit
+* fr-FR *missing*
+* hu-HU: Szerkesztés
+* pl-PL *missing*
+
+### journey.delete
+
+* de-DE: Löschen
+* en-GB: Delete
+* fr-FR *missing*
+* hu-HU: Törlés
+* pl-PL *missing*
+
+## landingpage.html.ep
+
+### landingpage.greeting-prefix
+
+* de-DE: Hallo,
+* en-GB: Hello,
+* fr-FR *missing*
+* hu-HU: Üdv,
+* pl-PL *missing*
+
+### landingpage.greeting-suffix
+
+* de-DE: !
+* en-GB: !
+* fr-FR *missing*
+* hu-HU: !
+* pl-PL *missing*
+
+### landingpage.not-checked-in
+
+* de-DE: Du bist gerade nicht eingecheckt
+* en-GB: You are not checked in at the moment
+* fr-FR *missing*
+* hu-HU: Jelenleg nem vagy becsekkolva
+* pl-PL *missing*
+
+### landingpage.stop-geosearch
+
+* de-DE: Stationen in der Umgebung suchen
+* en-GB: Look for stops nearby
+* fr-FR *missing*
+* hu-HU: Közeledben lévő megállók keresése
+* pl-PL *missing*
+
+### landingpage.manual-stop-entry
+
+* de-DE: Manuelle Eingabe
+* en-GB: Enter stop manually
+* fr-FR *missing*
+* hu-HU: Manuális bevitel
+* pl-PL *missing*
+
+### landingpage.departures
+
+*As short as possible*
+
+* de-DE: Abfahrten
+* en-GB: Departures
+* fr-FR *missing*
+* hu-HU: Indulások
+* pl-PL *missing*
+
+### landingpage.latest-trips
+
+* de-DE: Letzte Fahrten
+* en-GB: Latest Trips
+* fr-FR *missing*
+* hu-HU: Legutóbbi utazások
+* pl-PL *missing*
+
+### landingpage.date-format
+
+* de-DE: %d.%m.%Y
+* en-GB: %b %d %Y
+* fr-FR *missing*
+* hu-HU: %Y.%m.%d
+* pl-PL *missing*
+
+### landingpage.about
+
+* de-DE: Travelynx erlaubt das Einchecken in Verkehrsmittel (Busse, Bahnen, Züge) unter anderem in Deutschland, Österreich, der Schweiz, Luxemburg, Irland, Dänemark und Teilen der USA. So können die eigenen Fahrten später inklusive Echtzeitdaten und eingetragenen Servicemeldungen nachvollzogen und brennende Fragen wie „Wie viele Stunden war ich letzten Monat unterwegs?“ beantwortet werden.
+* en-GB: Travelynx facilitates checkins into public transit (such as buses, trams, or trains) in Germany, Austria, Switzerlanz, Luxembourg, Ireland, Denmark, parts of the USA, and more. This way, you can track your own journeys (often including map and real-time data), share them with others, and examine highly relevant questions such as “how many hours did I spend in public transit in the past month?”.
+* fr-FR *missing*
+* hu-HU: Travelynx-el tömegközlekedési eszközökre (pl. Buszokra, Villamosokra, Vonatokra) be lehet csekkolni, többek között Németországban, Ausztriában, Svájcban, Luxemburgban, Írországban, Dániában és az USA egyes részeiben. Így nyomon követheted az utazásaidat (gyakran térképes és valós idejű adatokkal együtt), megoszthatod azokat másokkal, és a rendkívüli fontos kérdéseidre, mint például „hány órát tömegközlekedtem az elmúlt hónapban?”, is kapsz választ.
+* pl-PL *missing*
+
+### landingpage.traewelling.pre
+
+* de-DE: Die Idee dazu kommt von
+* en-GB: The idea for such a service was first proposed and implemented by
+* fr-FR *missing*
+* hu-HU: Az elsők akiknek hasonló ötlete volt és azt kivitelezték a
+* pl-PL *missing*
+
+### landingpage.traewelling.post
+
+* de-DE:
+* en-GB:
+* fr-FR *missing*
+* hu-HU: volt
+* pl-PL *missing*
+
+### landingpage.features
+
+* de-DE: Features:
+* en-GB: Features:
+* fr-FR *missing*
+* hu-HU *missing*
+* pl-PL *missing*
+
+### landingpage.features.log
+
+* de-DE: Protokoll von Fahrplan- und Echtzeitdaten an Start- und Zielbahnhof
+* en-GB: Log of scheduled and real-time departure and arrival times at departure and destination stop
+* fr-FR *missing*
+* hu-HU: Menetrend szerinti és valós idejű adatok naplózása. (indulási és célállomásokon)
+* pl-PL *missing*
+
+### landingpage.features.share
+
+* de-DE: Teilen von aktuellen und vergangenen Fahrten mit anderen Personen
+* en-GB: Sharing the current check-in and past journeys with others
+* fr-FR *missing*
+* hu-HU: Jelenlegi és korábbi utazások megosztása másokkal
+* pl-PL *missing*
+
+### landingpage.features.api-pre
+
+* de-DE: Web-Hooks und
+* en-GB: Web-Hooks and an
+* fr-FR *missing*
+* hu-HU: Web-Hook-ok és egy
+* pl-PL *missing*
+
+### landingpage.features.api-link
+
+* de-DE: API
+* en-GB: API
+* fr-FR *missing*
+* hu-HU: API
+* pl-PL *missing*
+
+### landingpage.features.api-post
+
+* de-DE: zum automatisierten Einchecken und Auslesen des aktuellen Status
+* en-GB: for automatic check-ins and passing the current status to other applications
+* fr-FR *missing*
+* hu-HU: az automatikus becsekkeléshez és az aktuális státusz leolvasásához
+* pl-PL *missing*
+
+### landingpage.features.stats
+
+* de-DE: Statistiken über Reisezeiten und Verspätungen
+* en-GB: Stats about journey times and delays
+* fr-FR *missing*
+* hu-HU: Statisztikák az utazások időtartamáról és a késésekről
+* pl-PL *missing*
+
+### landingpage.features.passenger-rights
+
+* de-DE: Unterstützung beim Ausfüllen von Fahrgastrechteformularen
+* en-GB: Support when dealing with passenger rights forms
+* fr-FR *missing*
+* hu-HU: Támogatás az utasjogi formanyomtatványok kitöltéséhez
+* pl-PL *missing*
+
+### landingpage.features.public
+
+* de-DE: Optional: Öffentlicher Reisestatus und öffentliche Angaben zu vergangenen Fahrten
+* en-GB: Optional: public travel status and public data about past journeys
+* fr-FR *missing*
+* hu-HU: Opcionális: utazási státusz és a korábbi utazásokról szóló adatok nyilvánosítása
+* pl-PL *missing*
+
+### landingpage.disclaimer.lead
+
+* de-DE: Travelynx ist ein kostenfreies, privat betriebenes Projekt ohne Verfügbarkeitsgarantie. Unangekündigte Downtimes oder eine kurzfristige Einstellung dieser Seite sind nicht vorgesehen, aber möglich.
+* en-GB: Travelynx is a hobby project. It is provided free of charge, without any kind of availability guarantees. Unexpected downtimes or a cancellation of the entire site on short notice are not planned, but always possible.
+* fr-FR *missing*
+* hu-HU: Travelynx egy ingyenes és bármi rendelkezésre állási garacia nélküli hobbi projekt. Be nem tervezett leállások, illetve a teljes oldal hírtelen bezárása nincs tervben, de bármikor előfordulhat.
+* pl-PL *missing*
+
+### landingpage.disclaimer.source-pre
+
+* de-DE: Wer mag, kann auch den
+* en-GB: If you like, you can download the
+* fr-FR *missing*
+* hu-HU: Aki szeretné az letöltheti a
+* pl-PL *missing*
+
+### landingpage.disclaimer.source-link
+
+* de-DE: Quelltext
+* en-GB: source code
+* fr-FR *missing*
+* hu-HU: forráskódot
+* pl-PL *missing*
+
+### landingpage.disclaimer.source-post
+
+* de-DE: laden und eine eigene Instanz aufsetzen.
+* en-GB: and host your own instance.
+* fr-FR *missing*
+* hu-HU: is és saját szervert működtethet.
+* pl-PL *missing*
+
+## language.html.ep
+
+### language.language
+
+* de-DE: Sprache
+* en-GB: Language
+* fr-FR *missing*
+* hu-HU: Nyelv
+* pl-PL *missing*
+
+### language.browser-default
+
+* de-DE: Gleiche Sprache wie Web-Browser
+* en-GB: Use language(s) requested by browser
+* fr-FR *missing*
+* hu-HU: A webböngésző nyelve(i)
+* pl-PL *missing*
+
+## login.html.ep
+
+### login.accept-tos-pre
+
+* de-DE: Mit der Anmeldung stimmst du den
+* en-GB: By logging in, you accept the
+* fr-FR: En vous connectant, vous acceptez les
+* hu-HU: A bejelentkezéssel elfogadod a
+* pl-PL: Logując się akceptujesz
+
+### login.tos
+
+* de-DE: Nutzungsbedingungen
+* en-GB: terms of use
+* fr-FR: conditions d'utilisation
+* hu-HU: használati feltételeket
+* pl-PL: zasady użytkowania
+
+### login.accept-tos-post
+
+* de-DE: zu.
+* en-GB:
+* fr-FR:
+* hu-HU:
+* pl-PL:
+
+### login.forgot-password
+
+* de-DE: Passwort vergessen
+* en-GB: Forgot password
+* fr-FR: Mot de passe oublié
+* hu-HU: Elfelejtett jelszó
+* pl-PL: Nie pamiętam hasła
+
+### login.registration-disabled
+
+* de-DE: Diese Instanz erlaubt derzeit keine Registrierung neuer Accounts
+* en-GB: This instance does not allow registration of new accounts at the moment
+* fr-FR: Cette instance n'accepte pas actuellement de nouvelles inscriptions.
+* hu-HU: Ez a weboldal jelenleg nem fogad új felhasználókat
+* pl-PL: Ta instancja nie zezwala w tej chwili na nowe rejestracje
+
+## register.html.ep
+
+### register.name
+
+* de-DE: Name (alphanumerisch)
+* en-GB: Name (alphanumeric)
+* fr-FR: Nom (alphanumeric)
+* hu-HU: Név (alfanumerikus)
+* pl-PL: Nazwa (alfanumeryczna)
+
+### register.mail
+
+* de-DE: E-Mail-Adresse
+* en-GB: Email address
+* fr-FR: Adresse e-mail
+* hu-HU: E-Mail cím
+* pl-PL: Adres e-mail
+
+### register.password
+
+* de-DE: Passwort
+* en-GB: Password
+* fr-FR: Mot de passe
+* hu-HU: Jelszó
+* pl-PL: Hasło
+
+### register.repeat-password
+
+* de-DE: Passwort wiederholen
+* en-GB: Repeat password
+* fr-FR: Vérifier le mot de passe
+* hu-HU: Jelszó újra
+* pl-PL: Powtórz hasło
+
+### register.accept-tos-pre
+
+* de-DE: Mit deiner Registrierung stimmst du den
+* en-GB: By submitting this registration form, you accept the
+* fr-FR: En envoyant ce formulaire d'inscription, vous acceptez les
+* hu-HU: A regisztrálással elfogadod a
+* pl-PL: Rejestrując się akceptujesz
+
+### register.tos
+
+* de-DE: Nutzungsbedingungen
+* en-GB: terms of use
+* fr-FR: conditions d'utilisation
+* hu-HU: használati feltételeket
+* pl-PL: zasady użytkowania
+
+### register.accept-tos-post
+
+* de-DE: zu.
+* en-GB: .
+* fr-FR:
+* hu-HU:
+* pl-PL:
+
+### register.expect-confirmation-link
+
+* de-DE: Nach der Registrierung wird ein für 48 Stunden gültiger Bestätigungslink an die angegebene Mail-Adresse geschickt. Eine Anmeldung ist erst nach Bestätigung der Mail-Adresse möglich.
+* en-GB: After submitting the registration, a confirmation link will be sent to the provided email address. Logging into the new travelynx account is only possible after following that link. The link is valid for 48 hours.
+* fr-FR: Après avoir envoyé votre demande d'inscription, un lien de confirmation sera envoyé à l'adresse e-mail fournie. Vous pourrez vous connecter sur votre compte travelynx après avoir ouvert ce lien. Le lien est valide pendant 48 heures.
+* hu-HU: A regisztráció elküldése után egy megerősítő linket küldünk a megadott e-mail címre. Az új travelynx-fiókba való bejelentkezés csak a link követése után lehetséges. A link 48 órán át érvényes.
+* pl-PL: Po przesłaniu rejestracji na podany adres e-mail zostanie wysłany link potwierdzający. Zalogowanie się do konta travelynx jest możliwe dopiero po kliknięciu tego linku. Link jest ważny przez 48 godzin.
+
+### register.why-mail
+
+* de-DE: Die Mail-Adresse wird ausschließlich zur Bestätigung der Anmeldung, für die „Passwort vergessen“-Funktionalität und für wichtige Informationen über den Account verwendet und nicht an Dritte weitergegeben.
+* en-GB: We collect your email address solely fo the purposes of confirming your registration, resetting your password and providing you with important information about your account. Your email address will never be shared with third parties.
+* fr-FR *missing*
+* hu-HU: Az e-mail címet csak a regisztráció megerősítésére, az „Elfelejtett jelszó” funkcióhoz és a fiókkal kapcsolatos fontos információkhoz használjuk, és nem adjuk tovább harmadik félnek.
+* pl-PL *missing*
+
+### register.privacy-pre
+
+* de-DE: Die
+* en-GB: Our
+* fr-FR *missing*
+* hu-HU: Az
+* pl-PL *missing*
+
+### register.privacy
+
+* de-DE: Datenschutzerklärung
+* en-GB: privacy policy
+* fr-FR *missing*
+* hu-HU: adatkezelési tájékoztató
+* pl-PL *missing*
+
+### register.privacy-post
+
+* de-DE: beschreibt weitere erhobene Daten sowie deren Zweck und Speicherfristen.
+* en-GB: describes additional data that we collect, how we store it and for what purpose.
+* fr-FR *missing*
+* hu-HU: leírja az egyéb begyűjtött adatokat, valamit azok célját és tárolási időtartamát.
+* pl-PL *missing*
+
+### register.account-deletion
+
+* de-DE: Accounts werden nach einem Jahr ohne Aktivität per E-Mail über die bevorstehende Löschung informiert und nach vier weiteren Wochen ohne Aktivität automatisch gelöscht.
+* en-GB: Accounts are automatically deleted after a year without activity. You will receive an email informing you about the deletion and giving you four weeks to intervene.
+* fr-FR: .
+* hu-HU: A fiókokat egy év inaktivitás után e-mailben értesítjük a közelgő törlésről, és további négy hét inaktivitás után automatikusan törlődnek.
+* pl-PL: .
+
+### register.disclaimer
+
+* de-DE: Bitte beachten: Travelynx ist ein privat betriebenes Projekt ohne Verfügbarkeitsgarantie. Unangekündigte Downtimes oder eine kurzfristige Einstellung dieser Seite sind nicht vorgesehen, aber möglich.
+* en-GB: Please be aware: Travelynx is a non-commerical personal project and is provided as is. We do not make guarantees in regards to availability of the service or a sudden end of the project.
+* fr-FR *missing*
+* hu-HU: Kérjük, vedd figyelembe: Travelynx egy ingyenes és bármi rendelkezésre állási garacia nélküli hobbi projekt. Be nem tervezett leállások, illetve a teljes oldal hírtelen bezárása nincs tervben, de bármikor előfordulhat.
+* pl-PL *missing*
+
+## _checked_in.html.ep, _public_status_card.html.ep
+
+### status.is-checked-in
+
+* de-DE: ist unterwegs
+* en-GB: is in transit
+* fr-FR: en chemin
+* hu-HU: útban van
+* pl-PL: jest w podróży
+
+### status.is-not-checked-in
+
+* de-DE: ist gerade nicht eingecheckt
+* en-GB: ist not in transit right now
+* fr-FR: n'est pas en chemin
+* hu-HU: jelenleg nincs becsekkolva
+* pl-PL: nie jest w tej chwili w podróży
+
+### status.select-destination
+
+* de-DE: Ziel wählen
+* en-GB: Choose destination
+* fr-FR *missing*
+* hu-HU: Cél kiválasztása
+* pl-PL *missing*
+
+### status.share
+
+* de-DE: Teilen
+* en-GB: Share
+* fr-FR: Partager
+* hu-HU: Megosztás
+* pl-PL: Udostępnij
+
+### status.check-out
+
+* de-DE: Auschecken
+* en-GB: check out
+* fr-FR *missing*
+* hu-HU: Kicsekkelés
+* pl-PL *missing*
+
+### status.boarding-in.pre
+
+*noun or verb*
+
+* de-DE: Einfahrt in
+* en-GB: arrives in
+* fr-FR *missing*
+* hu-HU: Beszállás
+* pl-PL *missing*
+
+### status.boarding-in.post
+
+*noun or verb*
+
+* de-DE:
+* en-GB:
+* fr-FR *missing*
+* hu-HU: -en belül
+* pl-PL *missing*
+
+### status.boarding-soon
+
+*noun or verb*
+
+* de-DE: fährt ein
+* en-GB: now arriving
+* fr-FR *missing*
+* hu-HU: A beszállás hamarosan következik
+* pl-PL *missing*
+
+### status.departure-in.pre
+
+*noun or verb*
+
+* de-DE: Abfahrt in
+* en-GB: departs in
+* fr-FR *missing*
+* hu-HU:
+* pl-PL *missing*
+
+### status.departure-in.post
+
+*noun or verb*
+
+* de-DE:
+* en-GB:
+* fr-FR *missing*
+* hu-HU: -en belül indul
+* pl-PL *missing*
+
+### status.departure-soon
+
+*noun or verb*
+
+* de-DE: fährt ab
+* en-GB: now departing
+* fr-FR *missing*
+* hu-HU: Egy percen belül indul
+* pl-PL *missing*
+
+### status.arrival-in.pre
+
+*noun or verb*
+
+* de-DE: Ankunft in
+* en-GB: arrives in
+* fr-FR: Arrive dans
+* hu-HU:
+* pl-PL: Przyjazd za
+
+### status.arrival-in.post
+
+*noun or verb*
+
+* de-DE:
+* en-GB:
+* fr-FR:
+* hu-HU: -en belül érkezik
+* pl-PL:
+
+### status.arrival-soon
+
+*noun or verb*
+
+* de-DE: Ankunft in weniger als einer Minute
+* en-GB: now arriving
+* fr-FR: Arrive dans moins d'une minute
+* hu-HU: Egy percen belül érkezik
+* pl-PL: Przyjazd za mniej niż minutę
+
+### status.arrival-unknown
+
+* de-DE: Ankunft unbekannt
+* en-GB: Arrival unknown
+* fr-FR: Heure d'arrivée inconnue
+* hu-HU: Érkezési időpont ismeretlen
+* pl-PL: Przyjazd nieznany
+
+### status.arrived
+
+* de-DE: Ziel erreicht
+* en-GB: arrived
+* fr-FR: Arrivé
+* hu-HU: Cél elérve
+* pl-PL: U celu
+
+### status.depart-from.pre
+
+* de-DE: von
+* en-GB: from
+* fr-FR *missing*
+* hu-HU:
+* pl-PL *missing*
+
+### status.depart-from.post
+
+* de-DE:
+* en-GB:
+* fr-FR *missing*
+* hu-HU: vágányról indul
+* pl-PL *missing*
+
+### status.arrive-on.pre
+
+* de-DE: auf
+* en-GB: on
+* fr-FR *missing*
+* hu-HU:
+* pl-PL *missing*
+
+### status.arrive-on.post
+
+* de-DE:
+* en-GB:
+* fr-FR *missing*
+* hu-HU: vágányra érkezik
+* pl-PL *missing*
+
+### status.delayed-auto-checkout
+
+* de-DE: Der automatische Checkout erfolgt etwa zehn Minuten nach der Ankunft.
+* en-GB: You will be automatically checked out about ten minutes after the expected arrival time.
+* fr-FR *missing*
+* hu-HU: Körülbelül tíz perccel az érkezés után automatikusan kicsekkolódsz.
+* pl-PL *missing*
+
+### status.realtime-unavailable
+
+* de-DE: Keine Echtzeitdaten vorhanden
+* en-GB: Real-time data unavailable
+* fr-FR *missing*
+* hu-HU: Valós idejű adatok nem állnak rendelkezésre
+* pl-PL *missing*
+
+### status.messages
+
+*service messages related to the trip*
+
+* de-DE: Meldungen
+* en-GB: Notifications
+* fr-FR *missing*
+* hu-HU: Jelentések
+* pl-PL *missing*
+
+### status.map
+
+* de-DE: Karte
+* en-GB: Map
+* fr-FR *missing*
+* hu-HU: Térkép
+* pl-PL *missing*
+
+### status.change-destination
+
+* de-DE: Ziel ändern?
+* en-GB: Change destination
+* fr-FR *missing*
+* hu-HU: Célváltás?
+* pl-PL *missing*
+
+### status.carriages
+
+* de-DE: Wagen
+* en-GB: Carriages
+* fr-FR: Voitures
+* hu-HU: Kocsik
+* pl-PL: Wagony
+
+### status.route
+
+* de-DE: Route
+* en-GB: Route
+* fr-FR: Route
+* hu-HU: Útvonal
+* pl-PL: Trasa
+
+### status.undo
+
+*shown during destination selection*
+
+* de-DE: Rückgängig
+* en-GB: Undo
+* fr-FR *missing*
+* hu-HU: Visszavonás
+* pl-PL *missing*
+
+### status.privacy-level
+
+*shown during destination selection*
+
+* de-DE: Sichtbarkeit
+* en-GB: Privacy
+* fr-FR *missing*
+* hu-HU: Láthatóság
+* pl-PL *missing*
+
+### status.undo-checkin
+
+*shown once check-in is completed*
+
+* de-DE: Checkin rückgängig
+* en-GB: Undo check-in
+* fr-FR *missing*
+* hu-HU: Becsekkelés visszavonása
+* pl-PL *missing*
+
+### status.force-checkout-lead
+
+* de-DE: Etwa zehn Minuten nach der Ankunft erfolgt ein automatischer Checkout. Falls das Backend ausgefallen ist oder die Fahrt aus anderen Gründen verloren ging:
+* en-GB: travelynx will perform an automatic check-out about ten minutes after arrival. In case of backend or data update issues:
+* fr-FR *missing*
+* hu-HU: Körülbelül tíz perccel az érkezés után automatikusan kicsekkolódsz. Ha a backend leállt, vagy az utazás más okokból elveszlett:
+* pl-PL *missing*
+
+### status.force-checkout
+
+* de-DE: Jetzt auschecken
+* en-GB: Force checkout
+* fr-FR *missing*
+* hu-HU: Most kicsekkelni
+* pl-PL *missing*
+
+## _wagons.html.ep
+
+### wagons.name-as-type
+
+* de-DE: als
+* en-GB: running as
+* fr-FR: sous le nom de
+* hu-HU: közlekedik mint:
+* pl-PL: jedzie jako
+
+### wagons.from.pre
+
+* de-DE: von
+* en-GB: from
+* fr-FR: en provenance de
+* hu-HU:
+* pl-PL: z
+
+### wagons.from.post
+
+* de-DE:
+* en-GB:
+* fr-FR:
+* hu-HU: -tól
+* pl-PL:
+
+### wagons.to.pre
+
+* de-DE: nach
+* en-GB: towards
+* fr-FR: à destination de
+* hu-HU:
+* pl-PL: do
+
+### wagons.to.post
+
+* de-DE:
+* en-GB:
+* fr-FR:
+* hu-HU: -ig
+* pl-PL:
+
+### wagons.carriage
+
+* de-DE: Wagen
+* en-GB: Carriage
+* fr-FR: Voiture
+* hu-HU: Kocsi
+* pl-PL: Wagon
+
diff --git a/share/locales/template.pot b/share/locales/template.pot
index cb11a2f..f5c7ef1 100644
--- a/share/locales/template.pot
+++ b/share/locales/template.pot
@@ -1,5 +1,6 @@
msgid ""
msgstr ""
+"Language: FIXME\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
@@ -88,6 +89,36 @@ msgstr ""
# account.html.ep
+msgid "account.changed-name"
+msgstr ""
+
+msgid "account.changed-mail"
+msgstr ""
+
+msgid "account.changed-password"
+msgstr ""
+
+msgid "account.changed-language"
+msgstr ""
+
+msgid "account.changed-privacy"
+msgstr ""
+
+msgid "account.changed-social"
+msgstr ""
+
+msgid "account.changed-traewelling"
+msgstr ""
+
+msgid "account.changed-history"
+msgstr ""
+
+msgid "account.changed-webhook"
+msgstr ""
+
+msgid "account.cleared-notifications"
+msgstr ""
+
msgid "account.account"
msgstr ""
@@ -121,10 +152,10 @@ msgstr ""
msgid "account.webhook.disabled"
msgstr ""
-msgid "account.webhook.active_pending"
+msgid "account.webhook.active-ppending"
msgstr ""
-msgid "account.webhook.active_error"
+msgid "account.webhook.active-error"
msgstr ""
msgid "account.webhook.active"
@@ -133,7 +164,7 @@ msgstr ""
msgid "account.traewelling.unsupported"
msgstr ""
-msgid "account.registration_date"
+msgid "account.registration-date"
msgstr ""
msgid "account.interaction"
@@ -157,6 +188,20 @@ msgstr ""
msgid "account.interaction.disabled"
msgstr ""
+# changelog.html.ep
+
+msgid "changelog.added"
+msgstr ""
+
+msgid "changelog.bugfix"
+msgstr ""
+
+msgid "changelog.2-16.1"
+msgstr ""
+
+msgid "changelog.2-16.2"
+msgstr ""
+
# journey.html.ep
msgid "journey.not-found"
@@ -219,6 +264,9 @@ msgstr ""
msgid "journey.share"
msgstr ""
+msgid "journey.export"
+msgstr ""
+
msgid "journey.edit"
msgstr ""
diff --git a/t/r-negative-delay.t b/t/r-negative-delay.t
index 45ebade..9f87510 100644
--- a/t/r-negative-delay.t
+++ b/t/r-negative-delay.t
@@ -92,7 +92,7 @@ $t->post_ok(
rt_arrival => '2018-10-16T18:32',
}
);
-$t->status_is(302)->header_is( location => '/journey/1' );
+$t->status_is(302)->header_is( location => '/journey/1' )->content_is(q{});
$t->get_ok('/history/2018/10')
->status_is(200)
diff --git a/templates/account.html.ep b/templates/account.html.ep
index 0a5dbda..5539a39 100644
--- a/templates/account.html.ep
+++ b/templates/account.html.ep
@@ -116,10 +116,10 @@
<span style="color: #999999;"><%= L('account.webhook.disabled') %></span>
% }
% elsif ($hook->{latest_run}->epoch == 0) {
- <%= L('account.webhook.active_pending') %>
+ <%= L('account.webhook.active-pending') %>
% }
% elsif ($hook->{errored}) {
- <%= L('account.webhook.active_error') %> <i class="material-icons" aria-hidden="true">error</i>
+ <%= L('account.webhook.active-error') %> <i class="material-icons" aria-hidden="true">error</i>
% }
% else {
<%= L('account.webhook.active') %>
@@ -159,7 +159,7 @@
</tr>
% }
<tr>
- <th scope="row"><%= L('account.registration_date') %></th>
+ <th scope="row"><%= L('account.registration-date') %></th>
<td><%= $acc->{registered_at}->strftime(L('strftime.datetime')) %></td>
</tr>
</table>
diff --git a/templates/changelog.html.ep b/templates/changelog.html.ep
index 0d1ecc5..f4b6449 100644
--- a/templates/changelog.html.ep
+++ b/templates/changelog.html.ep
@@ -2,6 +2,22 @@
<div class="row">
<div class="col s12 m1 l1">
+ 2.16
+ </div>
+ <div class="col s12 m11 l11">
+ <p>
+ <i class="material-icons left" aria-label="<%= L('changelog.added') %>">add</i>
+ %= L('changelog.2-16.1')
+ </p>
+ <p>
+ <i class="material-icons left" aria-label="<%= L('changelog.bugfix') %>">build</i>
+ %= L('changelog.2-16.2')
+ </p>
+ </div>
+</div>
+
+<div class="row">
+ <div class="col s12 m1 l1">
2.15
</div>
<div class="col s12 m11 l11">
diff --git a/templates/journey.html.ep b/templates/journey.html.ep
index 2699d4a..f9c78ec 100644
--- a/templates/journey.html.ep
+++ b/templates/journey.html.ep
@@ -257,11 +257,12 @@
% my $before = 1;
% my $within = 0;
% my $at_startstop = 0;
+ % my $i = 0;
% for my $station (@{$journey->{route}}) {
- % if (($station->[1] and $station->[1] == $journey->{from_eva}) or $station->[0] eq $journey->{from_name}) {
+ % if ($i == $journey->{route_dep_index}) {
% $within = 1; $at_startstop = 1;
% }
- % elsif (($station->[1] and $station->[1] == $journey->{to_eva}) or $station->[0] eq $journey->{to_name}) {
+ % elsif ($i == $journey->{route_arr_index}) {
% $within = 0; $at_startstop = 1;
% }
% else {
@@ -269,10 +270,10 @@
% }
<span style="color: #808080;">
% if ($before and $station->[2]{sched_dep}) {
- %= $station->[2]{sched_dep}->strftime('%H:%M')
+ %= $station->[2]{sched_dep_dt}->strftime('%H:%M')
% }
% elsif (not $before and $station->[2]{sched_arr}) {
- %= $station->[2]{sched_arr}->strftime('%H:%M')
+ %= $station->[2]{sched_arr_dt}->strftime('%H:%M')
% }
</span>
% if ($at_startstop or $within) {
@@ -298,6 +299,7 @@
% $before = 0;
% }
<br/>
+ % $i += 1;
% }
</td>
</tr>