Skip to content

Commit af2552b

Browse files
author
Dave MacFarlane
committed
[i18n] Add support for 日本語
This puts the infrastructure in place to translate strings in loris on the PHP side using the gettext library. Japanese is added as an example (mostly using machine translation which could probably use proof reading by a native speaker.) Each module has its own textdomain for strings that come from that module, as well as a "loris" textdomain for general LORIS terms (menu categories, common terms, etc). The translations for a module are in $MODULEDIR/locale and the loris textdomain is in $LORISROOT/locale. The module names, menu categories, and breadcrumb text are currently marked up as translateable strings. This is enough to ensure that the menus and breadcrumbs in LORIS get translated. A new LORIS middleware detects the preferred language by looking for: 1. A lang=? passed in the query parameter (this can be used for testing, but does not currently have any way in the GUI to select) 2. The User's preference under "my_preferences" 3. The HTTP request's Accept-Language header in that order and ensuring that it's a language supported by the project by looking for the language_code in the LORIS "language" table. The pot files at the root of the locale directories are translation templates to give to translators. The locale/$lang/LC_MESSAGES/$textdomain.po contain the translation mapping. The .mo files are the compiled version used by the software and can be generated by "make locales". (We may want to rethink the structure of the Makefile in the future to have `make $modulename` do both the locale and javascript compilation.) Projects may override strings by putting the (compiled) gettext .mo file in the $project/locale/ directory (which would also be a good place to store the .po files). These are generated with `msgfmt -o filename.mo inputfile.po` (msgfmt is included with gettext.) Note that the format of the locale directories must be: locale/$lang/LC_MESSAGES/$textdomain.mo. We do not have any control over that. (Other then the name of the first "locale" directory, but that seems pretty standard.) It's also worth noting that, from what I've read, the *server* must have the locale installed for gettext to support the translation, not just LORIS. I don't know why. It seems silly.
1 parent 184d730 commit af2552b

File tree

168 files changed

+2786
-155
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

168 files changed

+2786
-155
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ SQL/Archive/autogenerated/*.sql
3434
VERSION
3535
.DS_Store
3636
.eslintcache
37-
tools/logs
37+
tools/logs
38+
*.mo

Dockerfile.test.php8

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ RUN apt-get update && \
55
apt-get install -y mariadb-client libzip-dev
66

77
# Install PHP extensions
8-
RUN docker-php-ext-install pdo_mysql zip
8+
RUN docker-php-ext-install pdo_mysql zip gettext
99

1010
# Ensure the logs directory exists
1111
RUN mkdir -p /app/logs

Makefile

+54-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ clean:
3232
rm -rf vendor
3333
rm -rf node_modules
3434
rm -rf modules/electrophysiology_browser/jsx/react-series-data-viewer/node_modules
35+
rm -f modules/*/locale/*/LC_MESSAGES/*.mo
3536

3637
# Perform static analysis checks
3738
checkstatic: phpdev
@@ -56,7 +57,59 @@ check: checkstatic unittests
5657
testdata:
5758
php tools/raisinbread_refresh.php
5859

59-
data_release:
60+
locales:
61+
msgfmt -o locale/ja/LC_MESSAGES/loris.mo locale/ja/LC_MESSAGES/loris.po
62+
msgfmt -o modules/new_profile/locale/ja/LC_MESSAGES/new_profile.mo modules/new_profile/locale/ja/LC_MESSAGES/new_profile.po
63+
msgfmt -o modules/acknowledgements/locale/ja/LC_MESSAGES/acknowledgements.mo modules/acknowledgements/locale/ja/LC_MESSAGES/acknowledgements.po
64+
msgfmt -o modules/api_docs/locale/ja/LC_MESSAGES/api_docs.mo modules/api_docs/locale/ja/LC_MESSAGES/api_docs.po
65+
msgfmt -o modules/battery_manager/locale/ja/LC_MESSAGES/battery_manager.mo modules/battery_manager/locale/ja/LC_MESSAGES/battery_manager.po
66+
msgfmt -o modules/behavioural_qc/locale/ja/LC_MESSAGES/behavioural_qc.mo modules/behavioural_qc/locale/ja/LC_MESSAGES/behavioural_qc.po
67+
msgfmt -o modules/brainbrowser/locale/ja/LC_MESSAGES/brainbrowser.mo modules/brainbrowser/locale/ja/LC_MESSAGES/brainbrowser.po
68+
msgfmt -o modules/bvl_feedback/locale/ja/LC_MESSAGES/bvl_feedback.mo modules/bvl_feedback/locale/ja/LC_MESSAGES/bvl_feedback.po
69+
msgfmt -o modules/candidate_list/locale/ja/LC_MESSAGES/candidate_list.mo modules/candidate_list/locale/ja/LC_MESSAGES/candidate_list.po
70+
msgfmt -o modules/candidate_parameters/locale/ja/LC_MESSAGES/candidate_parameters.mo modules/candidate_parameters/locale/ja/LC_MESSAGES/candidate_parameters.po
71+
msgfmt -o modules/candidate_profile/locale/ja/LC_MESSAGES/candidate_profile.mo modules/candidate_profile/locale/ja/LC_MESSAGES/candidate_profile.po
72+
msgfmt -o modules/configuration/locale/ja/LC_MESSAGES/configuration.mo modules/configuration/locale/ja/LC_MESSAGES/configuration.po
73+
msgfmt -o modules/configuration/locale/ja/LC_MESSAGES/configuration.mo modules/configuration/locale/ja/LC_MESSAGES/configuration.po
74+
msgfmt -o modules/conflict_resolver/locale/ja/LC_MESSAGES/conflict_resolver.mo modules/conflict_resolver/locale/ja/LC_MESSAGES/conflict_resolver.po
75+
msgfmt -o modules/create_timepoint/locale/ja/LC_MESSAGES/create_timepoint.mo modules/create_timepoint/locale/ja/LC_MESSAGES/create_timepoint.po
76+
msgfmt -o modules/dashboard/locale/ja/LC_MESSAGES/dashboard.mo modules/dashboard/locale/ja/LC_MESSAGES/dashboard.po
77+
msgfmt -o modules/datadict/locale/ja/LC_MESSAGES/datadict.mo modules/datadict/locale/ja/LC_MESSAGES/datadict.po
78+
msgfmt -o modules/dataquery/locale/ja/LC_MESSAGES/dataquery.mo modules/dataquery/locale/ja/LC_MESSAGES/dataquery.po
79+
msgfmt -o modules/data_release/locale/ja/LC_MESSAGES/data_release.mo modules/data_release/locale/ja/LC_MESSAGES/data_release.po
80+
msgfmt -o modules/dicom_archive/locale/ja/LC_MESSAGES/dicom_archive.mo modules/dicom_archive/locale/ja/LC_MESSAGES/dicom_archive.po
81+
msgfmt -o modules/dictionary/locale/ja/LC_MESSAGES/dictionary.mo modules/dictionary/locale/ja/LC_MESSAGES/dictionary.po
82+
msgfmt -o modules/document_repository/locale/ja/LC_MESSAGES/document_repository.mo modules/document_repository/locale/ja/LC_MESSAGES/document_repository.po
83+
msgfmt -o modules/dqt/locale/ja/LC_MESSAGES/dqt.mo modules/dqt/locale/ja/LC_MESSAGES/dqt.po
84+
msgfmt -o modules/electrophysiology_browser/locale/ja/LC_MESSAGES/electrophysiology_browser.mo modules/electrophysiology_browser/locale/ja/LC_MESSAGES/electrophysiology_browser.po
85+
msgfmt -o modules/electrophysiology_uploader/locale/ja/LC_MESSAGES/electrophysiology_uploader.mo modules/electrophysiology_uploader/locale/ja/LC_MESSAGES/electrophysiology_uploader.po
86+
msgfmt -o modules/examiner/locale/ja/LC_MESSAGES/examiner.mo modules/examiner/locale/ja/LC_MESSAGES/examiner.po
87+
msgfmt -o modules/genomic_browser/locale/ja/LC_MESSAGES/genomic_browser.mo modules/genomic_browser/locale/ja/LC_MESSAGES/genomic_browser.po
88+
msgfmt -o modules/help_editor/locale/ja/LC_MESSAGES/help_editor.mo modules/help_editor/locale/ja/LC_MESSAGES/help_editor.po
89+
msgfmt -o modules/imaging_browser/locale/ja/LC_MESSAGES/imaging_browser.mo modules/imaging_browser/locale/ja/LC_MESSAGES/imaging_browser.po
90+
msgfmt -o modules/imaging_qc/locale/ja/LC_MESSAGES/imaging_qc.mo modules/imaging_qc/locale/ja/LC_MESSAGES/imaging_qc.po
91+
msgfmt -o modules/imaging_uploader/locale/ja/LC_MESSAGES/imaging_uploader.mo modules/imaging_uploader/locale/ja/LC_MESSAGES/imaging_uploader.po
92+
msgfmt -o modules/instrument_builder/locale/ja/LC_MESSAGES/instrument_builder.mo modules/instrument_builder/locale/ja/LC_MESSAGES/instrument_builder.po
93+
msgfmt -o modules/instrument_list/locale/ja/LC_MESSAGES/instrument_list.mo modules/instrument_list/locale/ja/LC_MESSAGES/instrument_list.po
94+
msgfmt -o modules/instrument_manager/locale/ja/LC_MESSAGES/instrument_manager.mo modules/instrument_manager/locale/ja/LC_MESSAGES/instrument_manager.po
95+
msgfmt -o modules/instruments/locale/ja/LC_MESSAGES/instruments.mo modules/instruments/locale/ja/LC_MESSAGES/instruments.po
96+
msgfmt -o modules/issue_tracker/locale/ja/LC_MESSAGES/issue_tracker.mo modules/issue_tracker/locale/ja/LC_MESSAGES/issue_tracker.po
97+
msgfmt -o modules/login/locale/ja/LC_MESSAGES/login.mo modules/login/locale/ja/LC_MESSAGES/login.po
98+
msgfmt -o modules/media/locale/ja/LC_MESSAGES/media.mo modules/media/locale/ja/LC_MESSAGES/media.po
99+
msgfmt -o modules/module_manager/locale/ja/LC_MESSAGES/module_manager.mo modules/module_manager/locale/ja/LC_MESSAGES/module_manager.po
100+
msgfmt -o modules/mri_violations/locale/ja/LC_MESSAGES/mri_violations.mo modules/mri_violations/locale/ja/LC_MESSAGES/mri_violations.po
101+
msgfmt -o modules/next_stage/locale/ja/LC_MESSAGES/next_stage.mo modules/next_stage/locale/ja/LC_MESSAGES/next_stage.po
102+
msgfmt -o modules/oidc/locale/ja/LC_MESSAGES/oidc.mo modules/oidc/locale/ja/LC_MESSAGES/oidc.po
103+
msgfmt -o modules/publication/locale/ja/LC_MESSAGES/publication.mo modules/publication/locale/ja/LC_MESSAGES/publication.po
104+
msgfmt -o modules/schedule_module/locale/ja/LC_MESSAGES/schedule_module.mo modules/schedule_module/locale/ja/LC_MESSAGES/schedule_module.po
105+
msgfmt -o modules/server_processes_manager/locale/ja/LC_MESSAGES/server_processes_manager.mo modules/server_processes_manager/locale/ja/LC_MESSAGES/server_processes_manager.po
106+
msgfmt -o modules/statistics/locale/ja/LC_MESSAGES/statistics.mo modules/statistics/locale/ja/LC_MESSAGES/statistics.po
107+
msgfmt -o modules/survey_accounts/locale/ja/LC_MESSAGES/survey_accounts.mo modules/survey_accounts/locale/ja/LC_MESSAGES/survey_accounts.po
108+
msgfmt -o modules/timepoint_list/locale/ja/LC_MESSAGES/timepoint_list.mo modules/timepoint_list/locale/ja/LC_MESSAGES/timepoint_list.po
109+
msgfmt -o modules/user_accounts/locale/ja/LC_MESSAGES/user_accounts.mo modules/user_accounts/locale/ja/LC_MESSAGES/user_accounts.po
110+
111+
112+
data_release:
60113
target=data_release npm run compile
61114

62115
instrument_manager:

htdocs/index.php

+6-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
// See: https://www.php.net/manual/en/session.configuration.php#ini.session.use-strict-mode
3131
ini_set('session.use_strict_mode', '1');
3232

33+
bind_textdomain_codeset("loris", 'UTF-8');
34+
bindtextdomain("loris", __DIR__ . '/../locale');
35+
textdomain("loris");
36+
3337
// FIXME: The code in NDB_Client should mostly be replaced by middleware.
3438
$client = new \NDB_Client;
3539
$client->initialize();
@@ -38,7 +42,8 @@
3842
// Middleware that happens on every request. This doesn't include
3943
// any authentication middleware, because that's done dynamically
4044
// based on the module router, depending on if the module is public.
41-
$middlewarechain = (new \LORIS\Middleware\ContentLength())
45+
$middlewarechain = (new \LORIS\Middleware\Language())
46+
->withMiddleware(new \LORIS\Middleware\ContentLength())
4247
->withMiddleware(new \LORIS\Middleware\AWS())
4348
->withMiddleware(new \LORIS\Middleware\ResponseGenerator());
4449

@@ -82,7 +87,6 @@
8287
->withAttribute("loris", $lorisInstance);
8388

8489
// Now handle the request.
85-
//
8690
$response = $middlewarechain->process($serverrequest, $entrypoint);
8791

8892
// Add the HTTP header line.

locale/ja/LC_MESSAGES/loris.po

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Default LORIS strings to be translated (English).
2+
# Copy this to a language specific file and add translations to the
3+
# new file.
4+
# Copyright (C) 2025
5+
# This file is distributed under the same license as the LORIS package.
6+
# Dave MacFarlane <[email protected]>, 2025.
7+
#
8+
msgid ""
9+
msgstr ""
10+
"Project-Id-Version: LORIS 27\n"
11+
"Report-Msgid-Bugs-To: https://github.com/aces/Loris/issues\n"
12+
"POT-Creation-Date: 2025-04-08 14:37-0400\n"
13+
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
15+
"Language-Team: LANGUAGE <[email protected]>\n"
16+
"Language: ja\n"
17+
"MIME-Version: 1.0\n"
18+
"Content-Type: text/plain; charset=UTF-8\n"
19+
"Content-Transfer-Encoding: 8bit\n"
20+
21+
# Smarty template main.tpl strings
22+
msgid "LORIS"
23+
msgstr "ロリス"
24+
25+
msgid "DEV"
26+
msgstr "開発環境"
27+
28+
msgid "Affiliations"
29+
msgstr "所属"
30+
31+
msgid "Site Affiliation"
32+
msgstr "サイトの所属"
33+
34+
msgid "Site Affiliations"
35+
msgstr "サイトの所属"
36+
37+
msgid "Project Affiliation"
38+
msgstr "プロジェクトの所属"
39+
40+
msgid "Project Affiliations"
41+
msgstr "プロジェクトの所属"
42+
43+
msgid "Log Out"
44+
msgstr "ログアウト"
45+
46+
msgid "My Preferences"
47+
msgstr "私の好み"
48+
49+
# Auto extracted strings from PHP
50+
# Menu categories
51+
#: modules/configuration/php/module.class.inc:50
52+
#: modules/help_editor/php/module.class.inc:47
53+
#: modules/instrument_manager/php/module.class.inc:53
54+
#: modules/module_manager/php/module.class.inc:50
55+
#: modules/server_processes_manager/php/module.class.inc:104
56+
#: modules/survey_accounts/php/module.class.inc:48
57+
#: modules/user_accounts/php/module.class.inc:53
58+
msgid "Admin"
59+
msgstr "行政上の"
60+
#: modules/acknowledgements/php/module.class.inc:54
61+
#: modules/api_docs/php/module.class.inc:66
62+
#: modules/battery_manager/php/module.class.inc:55
63+
#: modules/datadict/php/module.class.inc:49
64+
#: modules/data_release/php/module.class.inc:56
65+
#: modules/dictionary/php/module.class.inc:49
66+
#: modules/document_repository/php/module.class.inc:56
67+
#: modules/instrument_builder/php/module.class.inc:48
68+
#: modules/issue_tracker/php/module.class.inc:54
69+
#: modules/schedule_module/php/module.class.inc:49
70+
msgid "Tools"
71+
msgstr "ツール"
72+
73+
#: modules/behavioural_qc/php/module.class.inc:51
74+
#: modules/conflict_resolver/php/module.class.inc:83
75+
#: modules/examiner/php/module.class.inc:50
76+
#: modules/media/php/module.class.inc:33
77+
msgid "Clinical"
78+
msgstr "臨床的"
79+
80+
#: modules/dicom_archive/php/module.class.inc:115
81+
#: modules/imaging_browser/php/module.class.inc:40
82+
#: modules/imaging_qc/php/module.class.inc:50
83+
#: modules/imaging_uploader/php/module.class.inc:54
84+
#: modules/mri_violations/php/module.class.inc:53
85+
msgid "Imaging"
86+
msgstr "イメージング"
87+
88+
#: modules/candidate_list/php/module.class.inc:76
89+
#: modules/new_profile/php/module.class.inc:50
90+
msgid "Candidate"
91+
msgstr "為り手"
92+
93+
#: modules/genomic_browser/php/module.class.inc:53
94+
msgid "Genomics"
95+
msgstr "ゲノミクス"
96+
97+
#: modules/electrophysiology_browser/php/module.class.inc:54
98+
msgid "Electrophysiology"
99+
msgstr "電気生理学"
100+
101+
#: modules/dataquery/php/module.class.inc:33
102+
#: modules/dqt/php/module.class.inc:49
103+
#: modules/publication/php/module.class.inc:55
104+
#: modules/statistics/php/module.class.inc:49
105+
msgid "Reports"
106+
msgstr "レポート"
107+
108+
# Common loris terms. Consider moving these to their own textdomain?
109+
msgid "TimePoint"
110+
msgstr "タイムポイント"
111+
112+
# Common select option labels
113+
msgid "Yes"
114+
msgstr "はい"
115+
116+
msgid "No"
117+
msgstr "いいえ"
118+

locale/loris.pot

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Default LORIS strings to be translated (English).
2+
# Copy this to a language specific file and add translations to the
3+
# new file.
4+
# Copyright (C) 2025
5+
# This file is distributed under the same license as the LORIS package.
6+
# Dave MacFarlane <[email protected]>, 2025.
7+
#
8+
msgid ""
9+
msgstr ""
10+
"Project-Id-Version: LORIS 27\n"
11+
"Report-Msgid-Bugs-To: https://github.com/aces/Loris/issues\n"
12+
"POT-Creation-Date: 2025-04-08 14:37-0400\n"
13+
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
15+
"Language-Team: LANGUAGE <[email protected]>\n"
16+
"Language: \n"
17+
"MIME-Version: 1.0\n"
18+
"Content-Type: text/plain; charset=UTF-8\n"
19+
"Content-Transfer-Encoding: 8bit\n"
20+
21+
# Smarty template main.tpl strings
22+
msgid "LORIS"
23+
msgstr ""
24+
25+
msgid "DEV"
26+
msgstr ""
27+
28+
msgid "Affiliations"
29+
msgstr ""
30+
31+
msgid "Site Affiliation"
32+
msgstr ""
33+
34+
msgid "Site Affiliations"
35+
msgstr ""
36+
37+
msgid "Project Affiliation"
38+
msgstr ""
39+
40+
msgid "Project Affiliations"
41+
msgstr ""
42+
43+
msgid "Log Out"
44+
msgstr ""
45+
46+
msgid "My Preferences"
47+
msgstr ""
48+
49+
# Auto extracted strings from PHP
50+
# Menu categories
51+
#: modules/configuration/php/module.class.inc:50
52+
#: modules/help_editor/php/module.class.inc:47
53+
#: modules/instrument_manager/php/module.class.inc:53
54+
#: modules/module_manager/php/module.class.inc:50
55+
#: modules/server_processes_manager/php/module.class.inc:104
56+
#: modules/survey_accounts/php/module.class.inc:48
57+
#: modules/user_accounts/php/module.class.inc:53
58+
msgid "Admin"
59+
msgstr ""
60+
#: modules/acknowledgements/php/module.class.inc:54
61+
#: modules/api_docs/php/module.class.inc:66
62+
#: modules/battery_manager/php/module.class.inc:55
63+
#: modules/datadict/php/module.class.inc:49
64+
#: modules/data_release/php/module.class.inc:56
65+
#: modules/dictionary/php/module.class.inc:49
66+
#: modules/document_repository/php/module.class.inc:56
67+
#: modules/instrument_builder/php/module.class.inc:48
68+
#: modules/issue_tracker/php/module.class.inc:54
69+
#: modules/schedule_module/php/module.class.inc:49
70+
msgid "Tools"
71+
msgstr ""
72+
73+
#: modules/behavioural_qc/php/module.class.inc:51
74+
#: modules/conflict_resolver/php/module.class.inc:83
75+
#: modules/examiner/php/module.class.inc:50
76+
#: modules/media/php/module.class.inc:33
77+
msgid "Clinical"
78+
msgstr ""
79+
80+
#: modules/dicom_archive/php/module.class.inc:115
81+
#: modules/imaging_browser/php/module.class.inc:40
82+
#: modules/imaging_qc/php/module.class.inc:50
83+
#: modules/imaging_uploader/php/module.class.inc:54
84+
#: modules/mri_violations/php/module.class.inc:53
85+
msgid "Imaging"
86+
msgstr ""
87+
88+
#: modules/candidate_list/php/module.class.inc:76
89+
#: modules/new_profile/php/module.class.inc:50
90+
msgid "Candidate"
91+
msgstr ""
92+
93+
#: modules/genomic_browser/php/module.class.inc:53
94+
msgid "Genomics"
95+
msgstr ""
96+
97+
#: modules/electrophysiology_browser/php/module.class.inc:54
98+
msgid "Electrophysiology"
99+
msgstr ""
100+
101+
#: modules/dataquery/php/module.class.inc:33
102+
#: modules/dqt/php/module.class.inc:49
103+
#: modules/publication/php/module.class.inc:55
104+
#: modules/statistics/php/module.class.inc:49
105+
msgid "Reports"
106+
msgstr ""
107+
108+
# Common loris terms. Consider moving these to their own textdomain?
109+
msgid "TimePoint"
110+
msgstr ""
111+
112+
# Common select option labels
113+
msgid "Yes"
114+
msgstr ""
115+
116+
msgid "No"
117+
msgstr ""
118+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Default LORIS strings to be translated (English).
2+
# Copy this to a language specific file and add translations to the
3+
# new file.
4+
# Copyright (C) 2025
5+
# This file is distributed under the same license as the LORIS package.
6+
# Dave MacFarlane <[email protected]>, 2025.
7+
#
8+
msgid ""
9+
msgstr ""
10+
"Project-Id-Version: LORIS 27\n"
11+
"Report-Msgid-Bugs-To: https://github.com/aces/Loris/issues\n"
12+
"POT-Creation-Date: 2025-04-08 14:37-0400\n"
13+
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
15+
"Language-Team: LANGUAGE <[email protected]>\n"
16+
"Language: \n"
17+
"MIME-Version: 1.0\n"
18+
"Content-Type: text/plain; charset=UTF-8\n"
19+
"Content-Transfer-Encoding: 8bit\n"
20+
21+
msgid "Acknowledgements"
22+
msgstr ""
23+

0 commit comments

Comments
 (0)