From 544e345f6afc8cd3b318e51d9cc82ed478cbe6d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Gaya?= <sebastien.gaya@gmail.com>
Date: Fri, 3 Feb 2023 16:15:55 +0100
Subject: [PATCH] people translations

---
 .../education/programs/roles_controller.rb    |  2 +-
 .../education/programs/teachers_controller.rb |  2 +-
 .../admin/education/programs_controller.rb    |  1 +
 .../education/schools/roles_controller.rb     |  2 +-
 .../admin/education/teachers_controller.rb    |  2 ++
 .../admin/research/researchers_controller.rb  |  4 ++--
 .../admin/university/alumni_controller.rb     | 10 ++++++----
 .../people/translations_controller.rb         | 20 +++++++++++++++++++
 .../admin/university/people_controller.rb     |  2 +-
 .../extranet/persons_controller.rb            |  2 +-
 .../server/universities_controller.rb         |  2 +-
 .../website/with_dependencies.rb              |  2 ++
 app/models/concerns/with_translations.rb      | 19 +++++++++---------
 app/models/university.rb                      | 11 +++++++++-
 app/models/university/person.rb               | 13 +++++++++++-
 app/models/university/person/administrator.rb |  6 ++++++
 app/models/university/person/alumnus.rb       |  6 ++++++
 app/models/university/person/author.rb        |  6 ++++++
 app/models/university/person/researcher.rb    |  6 ++++++
 app/models/university/person/teacher.rb       |  6 ++++++
 app/models/user/with_person.rb                |  3 ++-
 .../admin/application/i18n/_widget.html.erb   | 11 ++++++++--
 .../university/people/_main_infos.html.erb    |  3 ++-
 app/views/server/universities/_form.html.erb  |  1 +
 config/locales/university/en.yml              |  1 +
 config/locales/university/fr.yml              |  1 +
 config/routes/admin/university.rb             |  1 +
 ...37_add_default_language_to_universities.rb | 14 +++++++++++++
 ...355_add_i18n_infos_to_university_people.rb | 13 ++++++++++++
 db/schema.rb                                  | 11 +++++++++-
 test/fixtures/universities.yml                | 11 +++++++++-
 test/fixtures/university/people.yml           |  6 ++++++
 32 files changed, 171 insertions(+), 29 deletions(-)
 create mode 100644 app/controllers/admin/university/people/translations_controller.rb
 create mode 100644 db/migrate/20230203134137_add_default_language_to_universities.rb
 create mode 100644 db/migrate/20230203135355_add_i18n_infos_to_university_people.rb

diff --git a/app/controllers/admin/education/programs/roles_controller.rb b/app/controllers/admin/education/programs/roles_controller.rb
index d934a5c64..6d5495e3a 100644
--- a/app/controllers/admin/education/programs/roles_controller.rb
+++ b/app/controllers/admin/education/programs/roles_controller.rb
@@ -70,6 +70,6 @@ class Admin::Education::Programs::RolesController < Admin::Education::Programs::
   end
 
   def load_administration_people
-    @administration_people = current_university.people.administration.accessible_by(current_ability).ordered
+    @administration_people = current_university.people.where(language_id: current_university.default_language_id).administration.accessible_by(current_ability).ordered
   end
 end
diff --git a/app/controllers/admin/education/programs/teachers_controller.rb b/app/controllers/admin/education/programs/teachers_controller.rb
index 00261353a..ef69d3bbf 100644
--- a/app/controllers/admin/education/programs/teachers_controller.rb
+++ b/app/controllers/admin/education/programs/teachers_controller.rb
@@ -51,7 +51,7 @@ class Admin::Education::Programs::TeachersController < Admin::Education::Program
 
   def get_available_people
     used_person_ids = @program.university_person_involvements.where.not(id: @involvement.id).pluck(:person_id)
-    @available_people = current_university.people.teachers.where.not(id: used_person_ids).accessible_by(current_ability).ordered
+    @available_people = current_university.people.where(language_id: current_university.default_language_id).teachers.where.not(id: used_person_ids).accessible_by(current_ability).ordered
   end
 
   def breadcrumb
diff --git a/app/controllers/admin/education/programs_controller.rb b/app/controllers/admin/education/programs_controller.rb
index 2aa617c06..0a172ae2b 100644
--- a/app/controllers/admin/education/programs_controller.rb
+++ b/app/controllers/admin/education/programs_controller.rb
@@ -123,6 +123,7 @@ class Admin::Education::ProgramsController < Admin::Education::ApplicationContro
 
   def load_teacher_people
     @teacher_people = current_university.people
+                                        .where(language_id: current_university.default_language_id)
                                         .teachers
                                         .accessible_by(current_ability)
                                         .ordered
diff --git a/app/controllers/admin/education/schools/roles_controller.rb b/app/controllers/admin/education/schools/roles_controller.rb
index 8c1e62e8b..e8e67de6f 100644
--- a/app/controllers/admin/education/schools/roles_controller.rb
+++ b/app/controllers/admin/education/schools/roles_controller.rb
@@ -70,6 +70,6 @@ class Admin::Education::Schools::RolesController < Admin::Education::Schools::Ap
   end
 
   def load_administration_people
-    @administration_people = current_university.people.administration.accessible_by(current_ability).ordered
+    @administration_people = current_university.people.where(language_id: current_university.default_language_id).administration.accessible_by(current_ability).ordered
   end
 end
diff --git a/app/controllers/admin/education/teachers_controller.rb b/app/controllers/admin/education/teachers_controller.rb
index 16f8f9332..c7856b361 100644
--- a/app/controllers/admin/education/teachers_controller.rb
+++ b/app/controllers/admin/education/teachers_controller.rb
@@ -7,6 +7,7 @@ class Admin::Education::TeachersController < Admin::Education::ApplicationContro
   def index
     @teachers = apply_scopes(
       current_university.people
+                        .where(language_id: current_university.default_language_id)
                         .teachers
                         .accessible_by(current_ability)
     ).ordered.page(params[:page])
@@ -48,6 +49,7 @@ class Admin::Education::TeachersController < Admin::Education::ApplicationContro
 
   def load_teacher
     @teacher = current_university.people
+                                 .where(language_id: current_university.default_language_id)
                                  .teachers
                                  .accessible_by(current_ability)
                                  .find(params[:id])
diff --git a/app/controllers/admin/research/researchers_controller.rb b/app/controllers/admin/research/researchers_controller.rb
index 3dad9c5fe..3807dd45d 100644
--- a/app/controllers/admin/research/researchers_controller.rb
+++ b/app/controllers/admin/research/researchers_controller.rb
@@ -3,12 +3,12 @@ class Admin::Research::ResearchersController < Admin::Research::ApplicationContr
   has_scope :for_search_term
 
   def index
-    @researchers = apply_scopes(current_university.people.researchers.accessible_by(current_ability)).ordered.page(params[:page])
+    @researchers = apply_scopes(current_university.people.where(language_id: current_university.default_language_id).researchers.accessible_by(current_ability)).ordered.page(params[:page])
     breadcrumb
   end
 
   def show
-    @researcher = current_university.people.researchers.accessible_by(current_ability).find(params[:id])
+    @researcher = current_university.people.where(language_id: current_university.default_language_id).researchers.accessible_by(current_ability).find(params[:id])
     @papers = @researcher.research_journal_papers.ordered.page(params[:page])
     breadcrumb
   end
diff --git a/app/controllers/admin/university/alumni_controller.rb b/app/controllers/admin/university/alumni_controller.rb
index c267f090a..23bfd8d0d 100644
--- a/app/controllers/admin/university/alumni_controller.rb
+++ b/app/controllers/admin/university/alumni_controller.rb
@@ -9,10 +9,12 @@ class Admin::University::AlumniController < Admin::University::ApplicationContro
   has_scope :for_alumni_year
 
   def index
-    @alumni = apply_scopes(@alumni).alumni
-                     .accessible_by(current_ability)
-                     .ordered
-                     .page(params[:page])
+    @alumni = apply_scopes(@alumni)
+                .where(language_id: current_university.default_language_id)
+                .alumni
+                .accessible_by(current_ability)
+                .ordered
+                .page(params[:page])
     breadcrumb
   end
 
diff --git a/app/controllers/admin/university/people/translations_controller.rb b/app/controllers/admin/university/people/translations_controller.rb
new file mode 100644
index 000000000..216608d23
--- /dev/null
+++ b/app/controllers/admin/university/people/translations_controller.rb
@@ -0,0 +1,20 @@
+class Admin::University::People::TranslationsController < Admin::University::ApplicationController
+  load_and_authorize_resource :person,
+                              class: University::Person,
+                              id_param: :person_id,
+                              through: :current_university,
+                              through_association: :people,
+                              parent: false
+
+  def show
+    language = Language.find_by!(iso_code: params[:lang])
+    # Early return if language is correct
+    return [:admin, @person] if @person.language_id == language.id
+    # Look up for translation from person
+    translation = @person.translation_for(language)
+    # If not found, translate the current person (with blocks and all) for given language
+    translation ||= @person.translate!(language)
+    # Redirect to the translation
+    redirect_to [:admin, translation.becomes(translation.class.base_class)]
+  end
+end
diff --git a/app/controllers/admin/university/people_controller.rb b/app/controllers/admin/university/people_controller.rb
index 0fde20c47..5a1b32db0 100644
--- a/app/controllers/admin/university/people_controller.rb
+++ b/app/controllers/admin/university/people_controller.rb
@@ -8,7 +8,7 @@ class Admin::University::PeopleController < Admin::University::ApplicationContro
   has_scope :for_role
 
   def index
-    @people = apply_scopes(@people).ordered.page(params[:page])
+    @people = apply_scopes(@people).where(language_id: current_university.default_language_id).ordered.page(params[:page])
     breadcrumb
   end
 
diff --git a/app/controllers/extranet/persons_controller.rb b/app/controllers/extranet/persons_controller.rb
index 73df7551c..00537f9af 100644
--- a/app/controllers/extranet/persons_controller.rb
+++ b/app/controllers/extranet/persons_controller.rb
@@ -1,7 +1,7 @@
 class Extranet::PersonsController < Extranet::ApplicationController
   def index
     @facets = University::Person::Alumnus::Facets.new params[:facets], {
-      model: about&.university_person_alumni,
+      model: about&.university_person_alumni.where(language_id: current_university.default_language_id),
       about: about
     }
     @people = @facets.results
diff --git a/app/controllers/server/universities_controller.rb b/app/controllers/server/universities_controller.rb
index 5628d8d2b..985c8bb3a 100644
--- a/app/controllers/server/universities_controller.rb
+++ b/app/controllers/server/universities_controller.rb
@@ -59,7 +59,7 @@ class Server::UniversitiesController < Server::ApplicationController
   end
 
   def university_params
-    params.require(:university).permit(:name,
+    params.require(:university).permit(:name, :default_language_id,
       :address, :zipcode, :city, :country,
       :private, :identifier, :logo, :logo_delete, :sms_sender_name,
       :has_sso, :sso_target_url, :sso_cert, :sso_name_identifier_format, :sso_mapping, :sso_button_label,
diff --git a/app/models/communication/website/with_dependencies.rb b/app/models/communication/website/with_dependencies.rb
index 0f3d17760..708ad848f 100644
--- a/app/models/communication/website/with_dependencies.rb
+++ b/app/models/communication/website/with_dependencies.rb
@@ -84,6 +84,7 @@ module Communication::Website::WithDependencies
   end
 
   def people
+    # TODO: Scoper aux langues du website dans le cas où une personne serait traduite dans + de langues
     @people ||= begin
       people = []
       people += authors if has_authors?
@@ -96,6 +97,7 @@ module Communication::Website::WithDependencies
   end
 
   def people_with_facets
+    # TODO: Scoper aux langues du website dans le cas où une personne serait traduite dans + de langues
     @people_with_facets ||= begin
       people_with_facets = people
       people_with_facets += authors.compact.map(&:author) if has_authors?
diff --git a/app/models/concerns/with_translations.rb b/app/models/concerns/with_translations.rb
index bc62d97f5..634f59a9b 100644
--- a/app/models/concerns/with_translations.rb
+++ b/app/models/concerns/with_translations.rb
@@ -42,7 +42,7 @@ module WithTranslations
     # Handle publication
     translation.published = false if respond_to?(:published)
     # Handle featured image if object has one
-    translate_featured_image(translation) if respond_to?(:featured_image) && featured_image.attached?
+    translate_attachment(translation, :featured_image) if respond_to?(:featured_image) && featured_image.attached?
     translation.save
     # Handle blocks if object has any
     translate_blocks!(translation) if respond_to?(:blocks)
@@ -53,14 +53,6 @@ module WithTranslations
 
   protected
 
-  def translate_featured_image(translation)
-    translation.featured_image.attach(
-      io: URI.open(featured_image.url),
-      filename: featured_image.filename.to_s,
-      content_type: featured_image.content_type
-    )
-  end
-
   def translate_blocks!(translation)
     blocks.ordered.each do |block|
       block_duplicate = block.dup
@@ -69,6 +61,15 @@ module WithTranslations
     end
   end
 
+  # Utility method to duplicate attachments
+  def translate_attachment(translation, attachment_name)
+    translation.public_send(attachment_name).attach(
+      io: URI.open(public_send(attachment_name).url),
+      filename: public_send(attachment_name).filename.to_s,
+      content_type: public_send(attachment_name).content_type
+    )
+  end
+
   def translate_additional_data!(translation)
     # Overridable method to handle custom cases
   end
diff --git a/app/models/university.rb b/app/models/university.rb
index 96f44ec4a..a255b9591 100644
--- a/app/models/university.rb
+++ b/app/models/university.rb
@@ -25,6 +25,15 @@
 #  zipcode                    :string
 #  created_at                 :datetime         not null
 #  updated_at                 :datetime         not null
+#  default_language_id        :uuid             not null, indexed
+#
+# Indexes
+#
+#  index_universities_on_default_language_id  (default_language_id)
+#
+# Foreign Keys
+#
+#  fk_rails_a8022b1c3f  (default_language_id => languages.id)
 #
 class University < ApplicationRecord
   self.filter_attributes += [:sso_cert]
@@ -43,8 +52,8 @@ class University < ApplicationRecord
   # Can't use dependent: :destroy because of attachments
   # We use after_destroy to let the attachment go first
   has_many :active_storage_blobs, class_name: 'ActiveStorage::Blob'
-
   has_many :imports, dependent: :destroy
+  belongs_to :default_language, class_name: "Language"
 
   validates_presence_of :name
   validates :sms_sender_name, presence: true, length: { maximum: 11 }
diff --git a/app/models/university/person.rb b/app/models/university/person.rb
index 547438c54..5706c62d7 100644
--- a/app/models/university/person.rb
+++ b/app/models/university/person.rb
@@ -34,16 +34,22 @@
 #  zipcode               :string
 #  created_at            :datetime         not null
 #  updated_at            :datetime         not null
+#  language_id           :uuid             not null, indexed
+#  original_id           :uuid             indexed
 #  university_id         :uuid             not null, indexed
 #  user_id               :uuid             indexed
 #
 # Indexes
 #
+#  index_university_people_on_language_id    (language_id)
+#  index_university_people_on_original_id    (original_id)
 #  index_university_people_on_university_id  (university_id)
 #  index_university_people_on_user_id        (user_id)
 #
 # Foreign Keys
 #
+#  fk_rails_08f468090d  (original_id => university_people.id)
+#  fk_rails_49a0628c42  (language_id => languages.id)
 #  fk_rails_b47a769440  (user_id => users.id)
 #  fk_rails_da35e70d61  (university_id => universities.id)
 #
@@ -59,6 +65,7 @@ class University::Person < ApplicationRecord
   include WithRoles
   include WithBlocks
   include WithPermalink
+  include WithTranslations
 
   LIST_OF_ROLES = [
     :administration,
@@ -112,7 +119,7 @@ class University::Person < ApplicationRecord
 
   validates_presence_of   :first_name, :last_name
   validates_uniqueness_of :email,
-                          scope: :university_id,
+                          scope: [:university_id, :language_id],
                           allow_blank: true,
                           if: :will_save_change_to_email?
   validates_format_of     :email,
@@ -240,4 +247,8 @@ class University::Person < ApplicationRecord
   def prepare_name
     self.name = to_s
   end
+
+  def translate_additional_data!(translation)
+    translate_attachment(translation, :picture) if picture.attached?
+  end
 end
diff --git a/app/models/university/person/administrator.rb b/app/models/university/person/administrator.rb
index c69d82a9a..14c3f802b 100644
--- a/app/models/university/person/administrator.rb
+++ b/app/models/university/person/administrator.rb
@@ -34,16 +34,22 @@
 #  zipcode               :string
 #  created_at            :datetime         not null
 #  updated_at            :datetime         not null
+#  language_id           :uuid             not null, indexed
+#  original_id           :uuid             indexed
 #  university_id         :uuid             not null, indexed
 #  user_id               :uuid             indexed
 #
 # Indexes
 #
+#  index_university_people_on_language_id    (language_id)
+#  index_university_people_on_original_id    (original_id)
 #  index_university_people_on_university_id  (university_id)
 #  index_university_people_on_user_id        (user_id)
 #
 # Foreign Keys
 #
+#  fk_rails_08f468090d  (original_id => university_people.id)
+#  fk_rails_49a0628c42  (language_id => languages.id)
 #  fk_rails_b47a769440  (user_id => users.id)
 #  fk_rails_da35e70d61  (university_id => universities.id)
 #
diff --git a/app/models/university/person/alumnus.rb b/app/models/university/person/alumnus.rb
index ba7b52692..0208d188b 100644
--- a/app/models/university/person/alumnus.rb
+++ b/app/models/university/person/alumnus.rb
@@ -34,16 +34,22 @@
 #  zipcode               :string
 #  created_at            :datetime         not null
 #  updated_at            :datetime         not null
+#  language_id           :uuid             not null, indexed
+#  original_id           :uuid             indexed
 #  university_id         :uuid             not null, indexed
 #  user_id               :uuid             indexed
 #
 # Indexes
 #
+#  index_university_people_on_language_id    (language_id)
+#  index_university_people_on_original_id    (original_id)
 #  index_university_people_on_university_id  (university_id)
 #  index_university_people_on_user_id        (user_id)
 #
 # Foreign Keys
 #
+#  fk_rails_08f468090d  (original_id => university_people.id)
+#  fk_rails_49a0628c42  (language_id => languages.id)
 #  fk_rails_b47a769440  (user_id => users.id)
 #  fk_rails_da35e70d61  (university_id => universities.id)
 #
diff --git a/app/models/university/person/author.rb b/app/models/university/person/author.rb
index dc57d751a..496f29bab 100644
--- a/app/models/university/person/author.rb
+++ b/app/models/university/person/author.rb
@@ -34,16 +34,22 @@
 #  zipcode               :string
 #  created_at            :datetime         not null
 #  updated_at            :datetime         not null
+#  language_id           :uuid             not null, indexed
+#  original_id           :uuid             indexed
 #  university_id         :uuid             not null, indexed
 #  user_id               :uuid             indexed
 #
 # Indexes
 #
+#  index_university_people_on_language_id    (language_id)
+#  index_university_people_on_original_id    (original_id)
 #  index_university_people_on_university_id  (university_id)
 #  index_university_people_on_user_id        (user_id)
 #
 # Foreign Keys
 #
+#  fk_rails_08f468090d  (original_id => university_people.id)
+#  fk_rails_49a0628c42  (language_id => languages.id)
 #  fk_rails_b47a769440  (user_id => users.id)
 #  fk_rails_da35e70d61  (university_id => universities.id)
 #
diff --git a/app/models/university/person/researcher.rb b/app/models/university/person/researcher.rb
index b6b7464af..72e633ff0 100644
--- a/app/models/university/person/researcher.rb
+++ b/app/models/university/person/researcher.rb
@@ -34,16 +34,22 @@
 #  zipcode               :string
 #  created_at            :datetime         not null
 #  updated_at            :datetime         not null
+#  language_id           :uuid             not null, indexed
+#  original_id           :uuid             indexed
 #  university_id         :uuid             not null, indexed
 #  user_id               :uuid             indexed
 #
 # Indexes
 #
+#  index_university_people_on_language_id    (language_id)
+#  index_university_people_on_original_id    (original_id)
 #  index_university_people_on_university_id  (university_id)
 #  index_university_people_on_user_id        (user_id)
 #
 # Foreign Keys
 #
+#  fk_rails_08f468090d  (original_id => university_people.id)
+#  fk_rails_49a0628c42  (language_id => languages.id)
 #  fk_rails_b47a769440  (user_id => users.id)
 #  fk_rails_da35e70d61  (university_id => universities.id)
 #
diff --git a/app/models/university/person/teacher.rb b/app/models/university/person/teacher.rb
index f02acc600..00f430100 100644
--- a/app/models/university/person/teacher.rb
+++ b/app/models/university/person/teacher.rb
@@ -34,16 +34,22 @@
 #  zipcode               :string
 #  created_at            :datetime         not null
 #  updated_at            :datetime         not null
+#  language_id           :uuid             not null, indexed
+#  original_id           :uuid             indexed
 #  university_id         :uuid             not null, indexed
 #  user_id               :uuid             indexed
 #
 # Indexes
 #
+#  index_university_people_on_language_id    (language_id)
+#  index_university_people_on_original_id    (original_id)
 #  index_university_people_on_university_id  (university_id)
 #  index_university_people_on_user_id        (user_id)
 #
 # Foreign Keys
 #
+#  fk_rails_08f468090d  (original_id => university_people.id)
+#  fk_rails_49a0628c42  (language_id => languages.id)
 #  fk_rails_b47a769440  (user_id => users.id)
 #  fk_rails_da35e70d61  (university_id => universities.id)
 #
diff --git a/app/models/user/with_person.rb b/app/models/user/with_person.rb
index 0b6a4a000..ff92a6e3f 100644
--- a/app/models/user/with_person.rb
+++ b/app/models/user/with_person.rb
@@ -2,7 +2,8 @@ module User::WithPerson
   extend ActiveSupport::Concern
 
   included do
-    has_one :person, class_name: 'University::Person', dependent: :nullify
+    # Master person
+    has_one :person, -> { where(original_id: nil) }, class_name: 'University::Person', dependent: :nullify
 
     delegate :experiences, to: :person
 
diff --git a/app/views/admin/application/i18n/_widget.html.erb b/app/views/admin/application/i18n/_widget.html.erb
index ef5d22b73..5aadebf52 100644
--- a/app/views/admin/application/i18n/_widget.html.erb
+++ b/app/views/admin/application/i18n/_widget.html.erb
@@ -1,11 +1,18 @@
 <%
+if about.respond_to?(:website)
+  languages = about.website.languages
+  route_args = [:admin, about.becomes(about.class.base_class)]
+else
+  languages = Language.all
+  route_args = [:admin, about.becomes(about.class.base_class), :translation]
+end
 action = "<i class=\"fas fa-language fa-2x float-end \"></i>"
 %>
 <%= osuny_panel t('internationalization.label'), action: action do %>
   <p><%= t('internationalization.text_html', lang: t("languages.#{about.language.iso_code}")) %></p>
   <h3 class="h5 mt-4"><%= t('internationalization.translations_title') %></h3>
   <ol class="list-unstyled">
-    <% about.website.languages.each do |language| %>
+    <% languages.each do |language| %>
       <% next if language.id == about.language_id %>
       <li>
         <% if about.original_with_translations.detect { |translation| translation.language_id == language.id }.present? %>
@@ -13,7 +20,7 @@ action = "<i class=\"fas fa-language fa-2x float-end \"></i>"
         <% else %>
           <i class="fas fa-add float-end"></i>
         <% end %>
-        <%= link_to t(language.iso_code, scope: :languages), [:admin, about.becomes(about.class.base_class), lang: language.iso_code] %>
+        <%= link_to t(language.iso_code, scope: :languages), [*route_args, lang: language.iso_code] %>
       </li>
     <% end %>
   </ol>
diff --git a/app/views/admin/university/people/_main_infos.html.erb b/app/views/admin/university/people/_main_infos.html.erb
index 72b3fd55b..76efc126f 100644
--- a/app/views/admin/university/people/_main_infos.html.erb
+++ b/app/views/admin/university/people/_main_infos.html.erb
@@ -43,7 +43,7 @@
         <%= person.biography %>
       <% end %>
     <% end %>
-    
+
     <%= osuny_panel University::Person.human_attribute_name('socials') do %>
       <% unless person.url.blank? %>
         <h3 class="h5"><%= University::Person.human_attribute_name('url') %></h3>
@@ -74,6 +74,7 @@
           <p><%= link_to_if can?(:read, person.user), person.user, admin_user_path(person.user) %></p>
         <% end %>
     <% end %>
+    <%= render 'admin/application/i18n/widget', about: person %>
     <%= osuny_panel t('activerecord.attributes.university/person.picture') do %>
       <% if person.best_picture_inherits_from_user? %>
         <p>
diff --git a/app/views/server/universities/_form.html.erb b/app/views/server/universities/_form.html.erb
index f55c3a5bb..792181f1a 100644
--- a/app/views/server/universities/_form.html.erb
+++ b/app/views/server/universities/_form.html.erb
@@ -6,6 +6,7 @@
     <div class="col-md-4">
       <%= f.input :name %>
       <%= f.input :identifier %>
+      <%= f.association :default_language, include_blank: false %>
       <%= f.input :private %>
       <%= f.input :sms_sender_name,
                   maxlength: 11 %>
diff --git a/config/locales/university/en.yml b/config/locales/university/en.yml
index 54a67b638..63811528f 100644
--- a/config/locales/university/en.yml
+++ b/config/locales/university/en.yml
@@ -5,6 +5,7 @@ en:
         address: Address
         city: City
         country: Country
+        default_language: Default language
         has_sso: Has SSO?
         identifier: Identifier
         invoice_amount: Invoice amount
diff --git a/config/locales/university/fr.yml b/config/locales/university/fr.yml
index 5cc0997b8..30f56db57 100644
--- a/config/locales/university/fr.yml
+++ b/config/locales/university/fr.yml
@@ -5,6 +5,7 @@ fr:
         address: Adresse
         city: Ville
         country: Pays
+        default_language: Langue par défaut
         has_sso: A un SSO ?
         identifier: Identifiant
         invoice_amount: Montant de facturation
diff --git a/config/routes/admin/university.rb b/config/routes/admin/university.rb
index 9f4701b02..8f397cc6f 100644
--- a/config/routes/admin/university.rb
+++ b/config/routes/admin/university.rb
@@ -20,6 +20,7 @@ namespace :university do
     end
   end
   resources :people do
+    resources :translations, only: :show, param: :lang, controller: "people/translations"
     member do
       get :static
     end
diff --git a/db/migrate/20230203134137_add_default_language_to_universities.rb b/db/migrate/20230203134137_add_default_language_to_universities.rb
new file mode 100644
index 000000000..2f0e8f922
--- /dev/null
+++ b/db/migrate/20230203134137_add_default_language_to_universities.rb
@@ -0,0 +1,14 @@
+class AddDefaultLanguageToUniversities < ActiveRecord::Migration[7.0]
+  def change
+    add_reference :universities, :default_language, foreign_key: { to_table: :languages }, type: :uuid
+
+    University.reset_column_information
+    language = Language.find_by(iso_code: 'fr')
+    language ||= Language.find_by(iso_code: 'en')
+    language ||= Language.first
+
+    University.all.update_all(default_language_id: language.id)
+
+    change_column_null :universities, :default_language_id, false
+  end
+end
diff --git a/db/migrate/20230203135355_add_i18n_infos_to_university_people.rb b/db/migrate/20230203135355_add_i18n_infos_to_university_people.rb
new file mode 100644
index 000000000..43785a9e4
--- /dev/null
+++ b/db/migrate/20230203135355_add_i18n_infos_to_university_people.rb
@@ -0,0 +1,13 @@
+class AddI18nInfosToUniversityPeople < ActiveRecord::Migration[7.0]
+  def change
+    add_reference :university_people, :language, foreign_key: true, type: :uuid
+
+    University::Person.reset_column_information
+    University.all.find_each do |university|
+      university.people.update_all(language_id: university.default_language_id)
+    end
+
+    change_column_null :university_people, :language_id, false
+    add_reference :university_people, :original, foreign_key: {to_table: :university_people}, type: :uuid
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index a7e8d904d..aae0b24e6 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema[7.0].define(version: 2023_01_26_163347) do
+ActiveRecord::Schema[7.0].define(version: 2023_02_03_135355) do
   # These are extensions that must be enabled in order to support this database
   enable_extension "pgcrypto"
   enable_extension "plpgsql"
@@ -733,6 +733,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_26_163347) do
     t.string "sso_name_identifier_format"
     t.jsonb "sso_mapping"
     t.string "sso_button_label"
+    t.uuid "default_language_id", null: false
+    t.index ["default_language_id"], name: "index_universities_on_default_language_id"
   end
 
   create_table "university_organizations", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
@@ -796,6 +798,10 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_26_163347) do
     t.string "country"
     t.string "hal_person_identifier"
     t.string "mastodon"
+    t.uuid "language_id", null: false
+    t.uuid "original_id"
+    t.index ["language_id"], name: "index_university_people_on_language_id"
+    t.index ["original_id"], name: "index_university_people_on_original_id"
     t.index ["university_id"], name: "index_university_people_on_university_id"
     t.index ["user_id"], name: "index_university_people_on_user_id"
   end
@@ -969,8 +975,11 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_26_163347) do
   add_foreign_key "research_theses", "universities"
   add_foreign_key "research_theses", "university_people", column: "author_id"
   add_foreign_key "research_theses", "university_people", column: "director_id"
+  add_foreign_key "universities", "languages", column: "default_language_id"
   add_foreign_key "university_organizations", "universities"
+  add_foreign_key "university_people", "languages"
   add_foreign_key "university_people", "universities"
+  add_foreign_key "university_people", "university_people", column: "original_id"
   add_foreign_key "university_people", "users"
   add_foreign_key "university_person_experiences", "universities"
   add_foreign_key "university_person_experiences", "university_organizations", column: "organization_id"
diff --git a/test/fixtures/universities.yml b/test/fixtures/universities.yml
index a472c793d..5a30986b3 100644
--- a/test/fixtures/universities.yml
+++ b/test/fixtures/universities.yml
@@ -25,6 +25,15 @@
 #  zipcode                    :string
 #  created_at                 :datetime         not null
 #  updated_at                 :datetime         not null
+#  default_language_id        :uuid             not null, indexed
+#
+# Indexes
+#
+#  index_universities_on_default_language_id  (default_language_id)
+#
+# Foreign Keys
+#
+#  fk_rails_a8022b1c3f  (default_language_id => languages.id)
 #
 default_university:
   name: Université de test
@@ -34,4 +43,4 @@ default_university:
 stale_university:
   name: Université obsolète
   identifier: stale-university
-  sms_sender_name: "unistale"
\ No newline at end of file
+  sms_sender_name: "unistale"
diff --git a/test/fixtures/university/people.yml b/test/fixtures/university/people.yml
index 8fbe73952..5c4288849 100644
--- a/test/fixtures/university/people.yml
+++ b/test/fixtures/university/people.yml
@@ -34,16 +34,22 @@
 #  zipcode               :string
 #  created_at            :datetime         not null
 #  updated_at            :datetime         not null
+#  language_id           :uuid             not null, indexed
+#  original_id           :uuid             indexed
 #  university_id         :uuid             not null, indexed
 #  user_id               :uuid             indexed
 #
 # Indexes
 #
+#  index_university_people_on_language_id    (language_id)
+#  index_university_people_on_original_id    (original_id)
 #  index_university_people_on_university_id  (university_id)
 #  index_university_people_on_user_id        (user_id)
 #
 # Foreign Keys
 #
+#  fk_rails_08f468090d  (original_id => university_people.id)
+#  fk_rails_49a0628c42  (language_id => languages.id)
 #  fk_rails_b47a769440  (user_id => users.id)
 #  fk_rails_da35e70d61  (university_id => universities.id)
 #
-- 
GitLab