diff --git a/app/assets/javascripts/admin/communication/websites.js b/app/assets/javascripts/admin/communication/websites.js
index 677df2eb65e8c59b5747c7fd81efe6995caf4869..d27e2bf1b60fd7858eb8ea2a56d9962ea24a234e 100644
--- a/app/assets/javascripts/admin/communication/websites.js
+++ b/app/assets/javascripts/admin/communication/websites.js
@@ -5,8 +5,10 @@ window.osuny.communication.websites = {
         this.languagesCheckboxes = document.querySelectorAll('.js-languages input[type="checkbox"]');
         this.defaultLanguageSelect = document.querySelector('.js-default-language');
         this.defaultLanguageOptions = this.defaultLanguageSelect.querySelectorAll('option');
-        this.initEvents();
-        this.onChangeCheckbox();
+        if (this.defaultLanguageSelect) {
+            this.initEvents();
+            this.onChangeCheckbox();
+        }
     },
 
     initEvents: function () {
diff --git a/app/assets/stylesheets/admin/appstack/style.sass b/app/assets/stylesheets/admin/appstack/style.sass
index 1853079914fc84a1ba924f0a138c6156b2e110b5..a168205a25c7e22483089b13022ce339e3d4c300 100644
--- a/app/assets/stylesheets/admin/appstack/style.sass
+++ b/app/assets/stylesheets/admin/appstack/style.sass
@@ -72,7 +72,7 @@ p
     pointer-events: none
 .sidebar-link, a.sidebar-link
     color: rgba(255, 255, 255, 0.8)
-.sidebar-item.active .sidebar-link:hover, 
+.sidebar-item.active .sidebar-link:hover,
 .sidebar-item.active > .sidebar-link
     color: white
 
@@ -147,4 +147,8 @@ p
             &:nth-child(even)
                 background: $table-striped-bg
             &:hover
-                background: $table-hover-bg
\ No newline at end of file
+                background: $table-hover-bg
+
+/* used in admin/communication/websites/XXX and the selected item is over the bottom menu bar */
+.list-group-item.active
+    z-index: auto
\ No newline at end of file
diff --git a/app/assets/stylesheets/admin/pure/style.sass b/app/assets/stylesheets/admin/pure/style.sass
index 511469210c475bef716565923075c68cdac9a452..0900d9a71b7fbc17aa661ac00e45a62e40a33ee7 100644
--- a/app/assets/stylesheets/admin/pure/style.sass
+++ b/app/assets/stylesheets/admin/pure/style.sass
@@ -87,7 +87,7 @@ p + .form-label
     &.kind--selected
         background: $color-accent
         border-color: $color-accent
-        &, p
+        &, p, label
             color: $color-background
         
 
diff --git a/app/controllers/admin/application_controller.rb b/app/controllers/admin/application_controller.rb
index 3a0fedef61919ef02f94d6a12bd1a46c36fac6d5..edd21493eaff454a34c888c26d3db88747f97208 100644
--- a/app/controllers/admin/application_controller.rb
+++ b/app/controllers/admin/application_controller.rb
@@ -22,7 +22,8 @@ class Admin::ApplicationController < ApplicationController
 
   def breadcrumb_for(object, **options)
     return unless object
-    object.persisted? ? add_breadcrumb(object, [:admin, object, options])
+    title = object.to_s.truncate(50)
+    object.persisted? ? add_breadcrumb(title, [:admin, object, options])
                       : add_breadcrumb(t('create'))
   end
 
diff --git a/app/controllers/admin/communication/application_controller.rb b/app/controllers/admin/communication/application_controller.rb
index 9c3c780ff6a2f21ab0975890352b32d440dc19a4..de63e2580e8a5cdffc14e77954e62669d210f245 100644
--- a/app/controllers/admin/communication/application_controller.rb
+++ b/app/controllers/admin/communication/application_controller.rb
@@ -3,21 +3,8 @@ class Admin::Communication::ApplicationController < Admin::ApplicationController
   protected
 
   def breadcrumb
-    if current_user.can_display_global_menu?
-      if @website
-        short_breadcrumb
-        breadcrumb_for @website
-      else
-        super
-        add_breadcrumb Communication.model_name.human
-      end
-    else
-      super
-      if @website
-        add_breadcrumb Communication::Website.model_name.human(count: 2), admin_communication_websites_path
-        breadcrumb_for @website
-      end
-
-    end
+    super
+    add_breadcrumb Communication.model_name.human
+    @menu_collapsed = true if @website
   end
 end
diff --git a/app/controllers/admin/communication/blocks_controller.rb b/app/controllers/admin/communication/blocks_controller.rb
index 806c3911ceb0751c7c985c02f5d62b1dca709645..ee36c58203abe3812194f54682744f5729ea3a70 100644
--- a/app/controllers/admin/communication/blocks_controller.rb
+++ b/app/controllers/admin/communication/blocks_controller.rb
@@ -77,7 +77,9 @@ class Admin::Communication::BlocksController < Admin::Communication::Application
   def about_path
     # La formation ou la page concernée
     path_method = "admin_#{@block.about.class.base_class.to_s.parameterize.underscore}_path"
-    send path_method, id: @block.about_id, website_id: website_id
+    path_method_options = { id: @block.about_id, website_id: website_id }
+    path_method_options[:lang] = @block.about.language.iso_code if @block.about.respond_to?(:language)
+    public_send path_method, **path_method_options
   end
 
   def breadcrumb
diff --git a/app/controllers/admin/communication/websites/application_controller.rb b/app/controllers/admin/communication/websites/application_controller.rb
index 253b5a04ddf61d021238d3c2786457541fa161e6..f5111244d3ac6ba18697d14938e0ae8550d29ae3 100644
--- a/app/controllers/admin/communication/websites/application_controller.rb
+++ b/app/controllers/admin/communication/websites/application_controller.rb
@@ -6,10 +6,23 @@ class Admin::Communication::Websites::ApplicationController < Admin::Communicati
 
   protected
 
+  def current_website_language
+    @current_website_language ||= @website.best_language_for(params[:lang])
+  end
+  helper_method :current_website_language
+
+  def breadcrumb
+    super
+    add_breadcrumb Communication::Website.model_name.human(count: 2), admin_communication_websites_path
+    breadcrumb_for @website
+  end
+
   def default_url_options
-    return {} unless params.has_key? :website_id
-    {
-      website_id: params[:website_id]
-    }
+    options = {}
+    if @website.present?
+      options[:website_id] = @website.id
+      options[:lang] = current_website_language.iso_code
+    end
+    options
   end
 end
diff --git a/app/controllers/admin/communication/websites/categories_controller.rb b/app/controllers/admin/communication/websites/categories_controller.rb
index b0fe23ac07eb54a65f5573a31f8433feb8286ea7..175cf6cfffdf9a5ea2fb367eb19206e002863122 100644
--- a/app/controllers/admin/communication/websites/categories_controller.rb
+++ b/app/controllers/admin/communication/websites/categories_controller.rb
@@ -1,10 +1,12 @@
 class Admin::Communication::Websites::CategoriesController < Admin::Communication::Websites::ApplicationController
   load_and_authorize_resource class: Communication::Website::Category, through: :website
 
+  include Admin::Translatable
+
   before_action :get_root_categories, only: [:index, :new, :create, :edit, :update]
 
   def index
-    @categories = @website.categories.ordered
+    @categories = @website.categories.for_language(current_website_language).ordered
     breadcrumb
   end
 
@@ -30,7 +32,7 @@ class Admin::Communication::Websites::CategoriesController < Admin::Communicatio
 
   def children
     return unless request.xhr?
-    @category = @website.categories.find(params[:id])
+    @category = @website.categories.for_language(current_website_language).find(params[:id])
     @children = @category.children.ordered
   end
 
@@ -84,7 +86,7 @@ class Admin::Communication::Websites::CategoriesController < Admin::Communicatio
   protected
 
   def get_root_categories
-    @root_categories = @website.categories.root.ordered
+    @root_categories = @website.categories.root.for_language(current_website_language).ordered
   end
 
   def breadcrumb
@@ -97,9 +99,12 @@ class Admin::Communication::Websites::CategoriesController < Admin::Communicatio
   def category_params
     params.require(:communication_website_category)
           .permit(
-            :website_id, :name, :meta_description, :summary, :slug, :parent_id,
+            :name, :meta_description, :summary, :slug, :parent_id,
             :featured_image, :featured_image_delete, :featured_image_infos, :featured_image_alt, :featured_image_credit
           )
-          .merge(university_id: current_university.id)
+          .merge(
+            university_id: current_university.id,
+            language_id: current_website_language.id
+          )
   end
 end
diff --git a/app/controllers/admin/communication/websites/menus_controller.rb b/app/controllers/admin/communication/websites/menus_controller.rb
index 7f32950f9effbbd7f214922455716ae1f502a46b..f738bbb5e0f4004db87fe9095973069f01a050b9 100644
--- a/app/controllers/admin/communication/websites/menus_controller.rb
+++ b/app/controllers/admin/communication/websites/menus_controller.rb
@@ -1,8 +1,10 @@
 class Admin::Communication::Websites::MenusController < Admin::Communication::Websites::ApplicationController
   load_and_authorize_resource class: Communication::Website::Menu, through: :website
 
+  include Admin::Translatable
+
   def index
-    @menus = @menus.ordered.page(params[:page])
+    @menus = @menus.for_language(current_website_language).ordered.page(params[:page])
     breadcrumb
   end
 
@@ -58,7 +60,10 @@ class Admin::Communication::Websites::MenusController < Admin::Communication::We
 
   def menu_params
     params.require(:communication_website_menu)
-          .permit(:website_id, :title, :identifier)
-          .merge(university_id: current_university.id)
+          .permit(:title, :identifier)
+          .merge(
+            university_id: current_university.id,
+            language_id: current_website_language.id
+          )
   end
 end
diff --git a/app/controllers/admin/communication/websites/pages_controller.rb b/app/controllers/admin/communication/websites/pages_controller.rb
index 896913746ee9f9c4a2a9be773026d520b1b2f0ad..887f3e1ae8ab2d6219bc6d1e0998bca1923a023b 100644
--- a/app/controllers/admin/communication/websites/pages_controller.rb
+++ b/app/controllers/admin/communication/websites/pages_controller.rb
@@ -2,9 +2,12 @@ class Admin::Communication::Websites::PagesController < Admin::Communication::We
   load_and_authorize_resource class: Communication::Website::Page,
                               through: :website
 
+  include Admin::Translatable
+
   def index
-    @homepage = @website.special_page(Communication::Website::Page::Home)
+    @homepage = @website.special_page(Communication::Website::Page::Home, language: current_website_language)
     @first_level_pages = @homepage.children.ordered
+    @pages = @website.pages.for_language(current_website_language)
     breadcrumb
   end
 
@@ -105,8 +108,12 @@ class Admin::Communication::Websites::PagesController < Admin::Communication::We
             :communication_website_id, :title, :breadcrumb_title, :bodyclass,
             :meta_description, :summary, :header_text, :text, :slug, :published, :full_width,
             :featured_image, :featured_image_delete, :featured_image_infos, :featured_image_alt, :featured_image_credit,
-            :parent_id, :language_id
+            :parent_id
+          )
+          .merge(
+            university_id: current_university.id,
+            language_id: current_website_language.id
           )
-          .merge(university_id: current_university.id)
   end
+
 end
diff --git a/app/controllers/admin/communication/websites/posts_controller.rb b/app/controllers/admin/communication/websites/posts_controller.rb
index 92fbb46fe980d4f00769d13c9ca4550634fc5681..2bd77c0533e969dd494b495d654e0a8559aec167 100644
--- a/app/controllers/admin/communication/websites/posts_controller.rb
+++ b/app/controllers/admin/communication/websites/posts_controller.rb
@@ -3,7 +3,10 @@ class Admin::Communication::Websites::PostsController < Admin::Communication::We
 
   load_and_authorize_resource class: Communication::Website::Post, through: :website
 
+  include Admin::Translatable
+
   before_action :load_filters, only: :index
+  before_action :load_categories, only: [:new, :edit]
 
   has_scope :for_search_term
   has_scope :for_author
@@ -11,11 +14,12 @@ class Admin::Communication::Websites::PostsController < Admin::Communication::We
   has_scope :for_pinned
 
   def index
-    @posts = apply_scopes(@posts).ordered.page params[:page]
-    @authors =  @website.authors.accessible_by(current_ability)
+    @posts = apply_scopes(@posts).for_language(current_website_language).ordered.page params[:page]
+    @authors =  @website.authors.for_language(current_website_language)
+                                .accessible_by(current_ability)
                                 .ordered
                                 .page(params[:authors_page])
-    @root_categories = @website.categories.root.ordered
+    @root_categories = @website.categories.for_language(current_website_language).root.ordered
     breadcrumb
   end
 
@@ -48,7 +52,9 @@ class Admin::Communication::Websites::PostsController < Admin::Communication::We
 
   def new
     @post.website = @website
-    @post.author_id = current_user.person&.id
+    if current_user.person.present?
+      @post.author_id = current_user.person.find_or_translate!(current_website_language).id
+    end
     breadcrumb
   end
 
@@ -63,6 +69,7 @@ class Admin::Communication::Websites::PostsController < Admin::Communication::We
     if @post.save_and_sync
       redirect_to admin_communication_website_post_path(@post), notice: t('admin.successfully_created_html', model: @post.to_s)
     else
+      load_categories
       breadcrumb
       render :new, status: :unprocessable_entity
     end
@@ -73,6 +80,7 @@ class Admin::Communication::Websites::PostsController < Admin::Communication::We
     if @post.update_and_sync(post_params)
       redirect_to admin_communication_website_post_path(@post), notice: t('admin.successfully_updated_html', model: @post.to_s)
     else
+      load_categories
       breadcrumb
       add_breadcrumb t('edit')
       render :edit, status: :unprocessable_entity
@@ -95,16 +103,23 @@ class Admin::Communication::Websites::PostsController < Admin::Communication::We
 
   def post_params
     params.require(:communication_website_post)
-          .permit(
-            :university_id, :website_id, :title, :meta_description, :summary, :text,
-            :published, :published_at, :slug, :pinned,
-            :featured_image, :featured_image_delete, :featured_image_infos, :featured_image_alt, :featured_image_credit,
-            :author_id, :language_id, category_ids: []
-          )
-          .merge(university_id: current_university.id)
+    .permit(
+      :title, :meta_description, :summary, :text,
+      :published, :published_at, :slug, :pinned,
+      :featured_image, :featured_image_delete, :featured_image_infos, :featured_image_alt, :featured_image_credit,
+      :author_id, category_ids: []
+    )
+    .merge(
+      university_id: current_university.id,
+      language_id: current_website_language.id
+    )
   end
 
   def load_filters
     @filters = ::Filters::Admin::Communication::Website::Posts.new(current_user, @website).list
   end
+
+  def load_categories
+    @categories = @website.categories.for_language(current_website_language)
+  end
 end
diff --git a/app/controllers/admin/communication/websites_controller.rb b/app/controllers/admin/communication/websites_controller.rb
index 4b9c570dd2f28fa603324a9b730023dc5f8bb994..722d408593fb4cdf649664b1dde842fcb15286db 100644
--- a/app/controllers/admin/communication/websites_controller.rb
+++ b/app/controllers/admin/communication/websites_controller.rb
@@ -1,8 +1,4 @@
-class Admin::Communication::WebsitesController < Admin::Communication::ApplicationController
-  load_and_authorize_resource class: Communication::Website,
-                              through: :current_university,
-                              through_association: :communication_websites
-
+class Admin::Communication::WebsitesController < Admin::Communication::Websites::ApplicationController
   has_scope :for_search_term
   has_scope :for_about_type
 
@@ -13,8 +9,10 @@ class Admin::Communication::WebsitesController < Admin::Communication::Applicati
   end
 
   def show
-    @pages = @website.pages.accessible_by(current_ability).recent
-    @posts = @website.posts.accessible_by(current_ability).recent
+    @all_pages = @website.pages.accessible_by(current_ability).for_language(current_website_language)
+    @pages = @all_pages.recent
+    @all_posts = @website.posts.accessible_by(current_ability).for_language(current_website_language)
+    @posts = @all_posts.recent
     breadcrumb
   end
 
@@ -79,10 +77,25 @@ class Admin::Communication::WebsitesController < Admin::Communication::Applicati
 
   protected
 
+  def breadcrumb
+    super
+    add_breadcrumb Communication::Website.model_name.human(count: 2), admin_communication_websites_path
+    breadcrumb_for @website
+  end
+
   def website_params
-    params.require(:communication_website).permit(
+    attribute_names = [
       :name, :url, :repository, :access_token, :about_type, :about_id, :in_production,
-      :git_provider, :git_endpoint, :git_branch, :plausible_url, :default_language_id, language_ids: []
-    )
+      :git_provider, :git_endpoint, :git_branch, :plausible_url, language_ids: []
+    ]
+    # For now, default language can't be changed, too many implications, especially around special pages.
+    attribute_names << :default_language_id unless @website&.persisted?
+    params.require(:communication_website).permit(*attribute_names)
+  end
+
+  def default_url_options
+    options = {}
+    options[:lang] = current_website_language.iso_code if @website.present?
+    options
   end
 end
diff --git a/app/controllers/admin/education/application_controller.rb b/app/controllers/admin/education/application_controller.rb
index 2fc99b4a74bf0263639b57b4f67a59f834ce98cf..63562bfc545d8f314bc03bbbe294b065721e3ff6 100644
--- a/app/controllers/admin/education/application_controller.rb
+++ b/app/controllers/admin/education/application_controller.rb
@@ -1,11 +1,10 @@
 class Admin::Education::ApplicationController < Admin::ApplicationController
-  def breadcrumb
-    if @program
-      short_breadcrumb
-    else
-      super
-      add_breadcrumb Education.model_name.human
-    end
 
+  protected
+
+  def breadcrumb
+    super
+    add_breadcrumb Education.model_name.human
+    @menu_collapsed = true if @program
   end
 end
diff --git a/app/controllers/admin/education/diplomas_controller.rb b/app/controllers/admin/education/diplomas_controller.rb
index 0898fac6fb041c9feb6d6f988a22f869d4f2aa96..f6ef994b2d969633a98889cc2fa57a3fa3ade32d 100644
--- a/app/controllers/admin/education/diplomas_controller.rb
+++ b/app/controllers/admin/education/diplomas_controller.rb
@@ -14,6 +14,7 @@ class Admin::Education::DiplomasController < Admin::Education::ApplicationContro
   def static
     @about = @diploma
     @website = @diploma.websites&.first
+    @programs = @website.education_programs.root.ordered
     render layout: false
   end
 
diff --git a/app/controllers/admin/education/programs/roles_controller.rb b/app/controllers/admin/education/programs/roles_controller.rb
index d934a5c64c7e4a40896052da431beff06e8f0a51..fa6a9d1228b5bf2510798764e4609a34f127575d 100644
--- a/app/controllers/admin/education/programs/roles_controller.rb
+++ b/app/controllers/admin/education/programs/roles_controller.rb
@@ -70,6 +70,10 @@ 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
+                                                .for_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 00261353a60815b84ad4dc2766f830924fcb5233..e5f379ea2a96f00884ca4f94a11d267cbbac7b9b 100644
--- a/app/controllers/admin/education/programs/teachers_controller.rb
+++ b/app/controllers/admin/education/programs/teachers_controller.rb
@@ -51,7 +51,12 @@ 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
+                                          .for_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 2aa617c06b74f800626a1b70ad88eb4458502b28..9a24a588efd2597671eb6af488eba4ee0642c8b8 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
+                                        .for_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 8c1e62e8b8dbbe548f600e08304baa95fcf65a7d..cff87cbf661b4d3575fff42d001ee235207a5221 100644
--- a/app/controllers/admin/education/schools/roles_controller.rb
+++ b/app/controllers/admin/education/schools/roles_controller.rb
@@ -70,6 +70,10 @@ 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
+                                                .for_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 16f8f93320553ca7474a5257d20074e29e1b41c7..aba3eb08ba66efe486060592f976d2434348a4ab 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
+                        .for_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
+                                 .for_language_id(current_university.default_language_id)
                                  .teachers
                                  .accessible_by(current_ability)
                                  .find(params[:id])
diff --git a/app/controllers/admin/research/application_controller.rb b/app/controllers/admin/research/application_controller.rb
index 181ceaa185af59ab1dc650034edbee62ee94bbd5..3ab257c4435ac7310c6918e99ef2ed8a1029dce0 100644
--- a/app/controllers/admin/research/application_controller.rb
+++ b/app/controllers/admin/research/application_controller.rb
@@ -3,15 +3,8 @@ class Admin::Research::ApplicationController < Admin::ApplicationController
   protected
 
   def breadcrumb
-    if @journal
-      short_breadcrumb
-      breadcrumb_for @journal
-    elsif @laboratory
-      short_breadcrumb
-      breadcrumb_for @laboratory
-    else
-      super
-      add_breadcrumb Research.model_name.human
-    end
+    super
+    add_breadcrumb Research.model_name.human
+    @menu_collapsed = true if @journal || @laboratory
   end
 end
diff --git a/app/controllers/admin/research/journals_controller.rb b/app/controllers/admin/research/journals_controller.rb
index 048aaaef7d1dee966ad1213e3f027704aea1738a..c8f15e52e1255c996d26123308dbaeda8289e17e 100644
--- a/app/controllers/admin/research/journals_controller.rb
+++ b/app/controllers/admin/research/journals_controller.rb
@@ -53,6 +53,12 @@ class Admin::Research::JournalsController < Admin::Research::ApplicationControll
 
   protected
 
+  def breadcrumb
+    super
+    add_breadcrumb Research::Journal.model_name.human(count: 2), admin_research_journals_path
+    breadcrumb_for @journal
+  end
+
   def journal_params
     params.require(:research_journal)
           .permit(:title, :meta_description, :summary, :issn)
diff --git a/app/controllers/admin/research/researchers_controller.rb b/app/controllers/admin/research/researchers_controller.rb
index 6adbc1bc00c7dbe209555919e3ae20e7deda03f3..c76b7cd4500daf1c98e9f26e8e88b6a014019f3d 100644
--- a/app/controllers/admin/research/researchers_controller.rb
+++ b/app/controllers/admin/research/researchers_controller.rb
@@ -3,7 +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)
+                    .for_language_id(current_university.default_language_id)
+                    .researchers
+                    .accessible_by(current_ability)
+                    .ordered
+                    .page(params[:page])
     breadcrumb
   end
 
@@ -27,7 +32,11 @@ class Admin::Research::ResearchersController < Admin::Research::ApplicationContr
   protected
 
   def load
-    @researcher = current_university.people.researchers.accessible_by(current_ability).find(params[:id])
+    @researcher = current_university.people
+                                    .for_language_id(current_university.default_language_id)
+                                    .researchers
+                                    .accessible_by(current_ability)
+                                    .find(params[:id])
   end
 
   def breadcrumb
diff --git a/app/controllers/admin/university/alumni_controller.rb b/app/controllers/admin/university/alumni_controller.rb
index c267f090afd414dc189d33fecfb7cbf0712e0674..9171fc22e8e60f1137f57d7784e30089e8e14350 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)
+                .for_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_controller.rb b/app/controllers/admin/university/people_controller.rb
index 33b261dfc814c1946a13f0c4987f7281e8c55d8a..e82dd9efb708d814f0aaf8bc3ac3315b1a923a8f 100644
--- a/app/controllers/admin/university/people_controller.rb
+++ b/app/controllers/admin/university/people_controller.rb
@@ -8,7 +8,10 @@ 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)
+                .for_language_id(current_university.default_language_id)
+                .ordered
+                .page(params[:page])
     breadcrumb
   end
 
@@ -18,6 +21,18 @@ class Admin::University::PeopleController < Admin::University::ApplicationContro
     breadcrumb
   end
 
+  def in_language
+    language = Language.find_by!(iso_code: params[:lang])
+    translation = @person.find_or_translate!(language)
+    if translation.newly_translated
+      # There's an attribute accessor named "newly_translated" that we set to true
+      # when we just created the translation. We use it to redirect to the form instead of the show.
+      redirect_to [:edit, :admin, translation.becomes(translation.class.base_class)]
+    else
+      redirect_to [:admin, translation.becomes(translation.class.base_class)]
+    end
+  end
+
   def static
     @about = @person
     @website = @person.websites&.first
diff --git a/app/controllers/concerns/admin/translatable.rb b/app/controllers/concerns/admin/translatable.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fe8a43c8bf88f18b89ea1762c979b59b37b8b546
--- /dev/null
+++ b/app/controllers/concerns/admin/translatable.rb
@@ -0,0 +1,41 @@
+module Admin::Translatable
+  extend ActiveSupport::Concern
+
+  included do
+    before_action :check_or_redirect_translatable_resource, only: [:show, :edit, :update, :destroy]
+
+    protected
+
+    # If we don't have a website, it will not work
+
+    def check_or_redirect_translatable_resource
+      # Early return if language is correct
+      return if resource.language_id == current_website_language.id
+      # Look up for translation or translate (with blocks and all) from resource
+      translation = resource.find_or_translate!(current_website_language)
+      # Redirect to the translation
+      redirect_to_translation(translation)
+    end
+
+    def redirect_to_translation
+      if ['edit', 'update'].include?(action_name) || translation.newly_translated
+        # Safety net on update action if called on wrong language
+        # There's an attribute accessor named "newly_translated" that we set to true
+        # when we just created the translation. We use it to redirect to the form instead of the show.
+        redirect_to [:edit, :admin, translation.becomes(translation.class.base_class)]
+      else
+        # Safety net on destroy action if called on wrong language
+        redirect_to [:admin, translation.becomes(translation.class.base_class)]
+      end
+    end
+
+    def resource_name
+      self.class.to_s.remove("Controller").demodulize.singularize.underscore
+    end
+
+    def resource
+      instance_variable_get("@#{resource_name}")
+    end
+  end
+
+end
diff --git a/app/controllers/extranet/persons_controller.rb b/app/controllers/extranet/persons_controller.rb
index 73df7551cfe7721522c9cef5004de5afb9519a82..4ab759241764783d6ce4115c3f6914903e6eb014 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.for_language_id(current_university.default_language_id),
       about: about
     }
     @people = @facets.results
diff --git a/app/controllers/server/application_controller.rb b/app/controllers/server/application_controller.rb
index b6a2a5b819a02011c3538b5ef02877b57e51ed6c..3966163c2dbd6ae8361e2caf16691c4fa8f035dc 100644
--- a/app/controllers/server/application_controller.rb
+++ b/app/controllers/server/application_controller.rb
@@ -5,6 +5,10 @@ class Server::ApplicationController < ApplicationController
 
   protected
 
+  def current_admin_theme
+    'pure'
+  end
+
   def breadcrumb
     add_breadcrumb t('admin.dashboard'), :server_root_path
   end
diff --git a/app/controllers/server/universities_controller.rb b/app/controllers/server/universities_controller.rb
index 5628d8d2b472d1566188e0f1732df2daaa254fb6..985c8bb3abee721ecf3b4329d918a2899d1a4797 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/block.rb b/app/models/communication/block.rb
index 29f0ef5cf08814656a2bb7a7c78b0d15db1566fc..9c1a12b62e18038f9c59966f9222933de883c357 100644
--- a/app/models/communication/block.rb
+++ b/app/models/communication/block.rb
@@ -65,7 +65,7 @@ class Communication::Block < ApplicationRecord
     utilities: [:files, :definitions, :embed, :contact]
   }
 
-  scope :published, -> { where(published: true) } 
+  scope :published, -> { where(published: true) }
 
   before_save :attach_template_blobs
 
@@ -97,12 +97,25 @@ class Communication::Block < ApplicationRecord
     @template = nil
   end
 
+  def language
+    return @language if defined?(@language)
+    @language ||= about.respond_to?(:language) ? about.language : nil
+  end
+
   def duplicate
     block = self.dup
     block.save
     block
   end
 
+  def translate!(about_translation)
+    translation = self.dup
+    translation.about = about_translation
+    translation.template.translate!
+    translation.data = translation.template.data
+    translation.save
+  end
+
   def to_s
     title.blank?  ? "#{Communication::Block.model_name.human} #{position}"
                   : "#{title}"
diff --git a/app/models/communication/block/component/base.rb b/app/models/communication/block/component/base.rb
index 50089bb53a0312b04b9b140136ea84f232625e07..b0e3accc8fd9b0ca49593b863d10e67669a60350 100644
--- a/app/models/communication/block/component/base.rb
+++ b/app/models/communication/block/component/base.rb
@@ -32,4 +32,8 @@ class Communication::Block::Component::Base
   def website
     template.block.about&.website
   end
+
+  def translate!
+    # By default, does nothing. Specific cases are handled in their own definitions. (example: post)
+  end
 end
diff --git a/app/models/communication/block/component/category.rb b/app/models/communication/block/component/category.rb
index a7db68fa12e70859a1274757e1c5e04ac46f043d..dc0b84dfc19f494d1be0278c8f6bdae1f68d37f7 100644
--- a/app/models/communication/block/component/category.rb
+++ b/app/models/communication/block/component/category.rb
@@ -9,4 +9,9 @@ class Communication::Block::Component::Category < Communication::Block::Componen
     [category, category&.best_featured_image&.blob]
   end
 
+  def translate!
+    return unless category.present?
+    @data = category.find_or_translate!(template.language).id
+  end
+
 end
diff --git a/app/models/communication/block/component/organization.rb b/app/models/communication/block/component/organization.rb
index dd52f491f0a92429259d137c7344772b3c6f2883..f76dff77147a332ced94c9f7d93ec8280ea4b319 100644
--- a/app/models/communication/block/component/organization.rb
+++ b/app/models/communication/block/component/organization.rb
@@ -12,4 +12,8 @@ class Communication::Block::Component::Organization < Communication::Block::Comp
     ]
   end
 
+  def translate!
+    # TODO: Traduction des Organisations à faire
+  end
+
 end
diff --git a/app/models/communication/block/component/page.rb b/app/models/communication/block/component/page.rb
index 2f5617ad6d5f1b5b17bc5aa48ace9ae2f945db70..6c28a4321d126a115a1176a3529c529927a60fe2 100644
--- a/app/models/communication/block/component/page.rb
+++ b/app/models/communication/block/component/page.rb
@@ -9,4 +9,10 @@ class Communication::Block::Component::Page < Communication::Block::Component::B
     [page, page&.best_featured_image&.blob]
   end
 
+  def translate!
+    return unless website && data.present?
+    source_page = website.pages.find_by(id: data)
+    @data = source_page.find_or_translate!(template.language).id if source_page.present?
+  end
+
 end
diff --git a/app/models/communication/block/component/person.rb b/app/models/communication/block/component/person.rb
index e082a01394a75e991c08c73e1357c74c78adfd20..0a5f7dbbd167337816f273d985ad56f874f2d76f 100644
--- a/app/models/communication/block/component/person.rb
+++ b/app/models/communication/block/component/person.rb
@@ -8,4 +8,9 @@ class Communication::Block::Component::Person < Communication::Block::Component:
     [person, person&.picture&.blob]
   end
 
+  def translate!
+    return unless data.present?
+    @data = person.find_or_translate!(template.language).id
+  end
+
 end
diff --git a/app/models/communication/block/component/post.rb b/app/models/communication/block/component/post.rb
index 8b08eb17d0ae81a98a31f7993824300b7f025e11..094386831cada1289724f4338334f30258beda84 100644
--- a/app/models/communication/block/component/post.rb
+++ b/app/models/communication/block/component/post.rb
@@ -14,4 +14,10 @@ class Communication::Block::Component::Post < Communication::Block::Component::B
     ]
   end
 
+  def translate!
+    return unless website && data.present?
+    source_post = website.posts.find_by(id: data)
+    @data = source_post.find_or_translate!(template.language).id if source_post.present?
+  end
+
 end
diff --git a/app/models/communication/block/component/program.rb b/app/models/communication/block/component/program.rb
index 8153ac398cdf80d0a73d24d1dd21237e94d43902..d8a1f80259095aaef93e498a5f7f6fa48e694145 100644
--- a/app/models/communication/block/component/program.rb
+++ b/app/models/communication/block/component/program.rb
@@ -7,4 +7,8 @@ class Communication::Block::Component::Program < Communication::Block::Component
   def git_dependencies
     [program, program&.best_featured_image&.blob]
   end
+
+  def translate!
+    # TODO: Traduction des Formations à faire
+  end
 end
diff --git a/app/models/communication/block/template.rb b/app/models/communication/block/template.rb
deleted file mode 100644
index 4cab374a72d2c461bbc15c2711428f8124d5074c..0000000000000000000000000000000000000000
--- a/app/models/communication/block/template.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-class Communication::Block::Template
-  attr_reader :block
-
-  def initialize(block)
-    @block = block
-  end
-
-  def sanitized_data
-    data
-  end
-
-  def git_dependencies
-    unless @git_dependencies
-      @git_dependencies = []
-      build_git_dependencies
-      @git_dependencies.uniq!
-    end
-    @git_dependencies
-  end
-
-  def active_storage_blobs
-    []
-  end
-
-  def allowed_for_about?
-    template.allowed_for_about?
-  end
-
-  protected
-
-  def build_git_dependencies
-  end
-
-  def add_dependency(dependency)
-    if dependency.is_a? Array
-      @git_dependencies += dependency
-    else
-      @git_dependencies += [dependency]
-    end
-  end
-
-  def find_blob(object, key)
-    id = object.dig(key, 'id')
-    return if id.blank?
-    university.active_storage_blobs.find id
-  end
-
-  def data
-    block.data || {}
-  end
-
-  def elements
-    data.has_key?('elements') ? data['elements']
-                              : []
-  end
-
-  def exclude_for
-    []
-  end
-
-  def university
-    block.university
-  end
-end
diff --git a/app/models/communication/block/template/base.rb b/app/models/communication/block/template/base.rb
index 55d8e165465622259b41c515ba91d93c30d91d1a..fbc8a7f2a6881f24ef3aba3f527b71c44dab4f01 100644
--- a/app/models/communication/block/template/base.rb
+++ b/app/models/communication/block/template/base.rb
@@ -9,6 +9,7 @@ class Communication::Block::Template::Base
 
   delegate :university, to: :block
   delegate :about, to: :block
+  delegate :language, to: :block
   delegate :template_kind, to: :block
   alias :kind :template_kind
 
@@ -93,6 +94,11 @@ class Communication::Block::Template::Base
     hash
   end
 
+  def translate!
+    components.each(&:translate!)
+    elements.each(&:translate!) if has_element_class?
+  end
+
   def git_dependencies
     unless @git_dependencies
       @git_dependencies = []
diff --git a/app/models/communication/block/template/page.rb b/app/models/communication/block/template/page.rb
index f4784136816a8a6852f6a6c5a8315cacb4436fd5..21d8ebf0dd8fa7ce7f7898da49fcd98781b7cbe6 100644
--- a/app/models/communication/block/template/page.rb
+++ b/app/models/communication/block/template/page.rb
@@ -36,7 +36,7 @@ class Communication::Block::Template::Page < Communication::Block::Template::Bas
 
   def selected_pages_children
     return [] unless page
-    page.children.published.ordered
+    page.children.for_language(block.language).published.ordered
   end
 
 end
diff --git a/app/models/communication/block/template/post.rb b/app/models/communication/block/template/post.rb
index 5d95ffd1bd70e04297e6890bc675c49a36f9e7c9..eb7b6ec57c559f42777e19c5684585cd68fb2587 100644
--- a/app/models/communication/block/template/post.rb
+++ b/app/models/communication/block/template/post.rb
@@ -34,6 +34,7 @@ class Communication::Block::Template::Post < Communication::Block::Template::Bas
   def selected_posts_all
     block.about&.website
                 .posts
+                .for_language(block.language)
                 .published
                 .ordered
                 .limit(posts_quantity)
@@ -42,7 +43,8 @@ class Communication::Block::Template::Post < Communication::Block::Template::Bas
   def selected_posts_category
     return [] if category.nil?
     category_ids = [category.id, category.descendants.map(&:id)].flatten
-    university.communication_website_posts.joins(:categories)
+    university.communication_website_posts.for_language(block.language)
+                                          .joins(:categories)
                                           .where(categories: { id: category_ids })
                                           .distinct
                                           .published
@@ -60,6 +62,7 @@ class Communication::Block::Template::Post < Communication::Block::Template::Bas
     return if id.blank?
     block.about&.website
                 .posts
+                .for_language(block.language)
                 .published
                 .find_by(id: id)
   end
diff --git a/app/models/communication/website.rb b/app/models/communication/website.rb
index 361cbb6b04c8f67256764b3fabdf1d667999e08c..a750fc64799da4e044e784fcbb69cdc2d4238fff 100644
--- a/app/models/communication/website.rb
+++ b/app/models/communication/website.rb
@@ -62,6 +62,7 @@ class Communication::Website < ApplicationRecord
                           association_foreign_key: 'language_id'
 
   validates :languages, length: { minimum: 1 }
+  validate :languages_must_include_default_language
 
   scope :ordered, -> { order(:name) }
   scope :in_production, -> { where(in_production: true) }
@@ -96,4 +97,16 @@ class Communication::Website < ApplicationRecord
     dependencies += about.git_dependencies(website) if about.present?
     dependencies
   end
+
+  def best_language_for(iso_code)
+    # We look for the language by the ISO code in the websites languages.
+    # If not found, we fallback to the default language.
+    languages.find_by(iso_code: iso_code) || default_language
+  end
+
+  protected
+
+  def languages_must_include_default_language
+    errors.add(:languages, :must_include_default) unless language_ids.include?(default_language_id)
+  end
 end
diff --git a/app/models/communication/website/category.rb b/app/models/communication/website/category.rb
index ba31fe5647d9d23b7538539e105754248c51f586..2658bb7db55f7c209366e7cb7c5edfe23c41abb3 100644
--- a/app/models/communication/website/category.rb
+++ b/app/models/communication/website/category.rb
@@ -16,6 +16,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  program_id               :uuid             indexed
 #  university_id            :uuid             not null, indexed
@@ -23,12 +25,16 @@
 # Indexes
 #
 #  idx_communication_website_post_cats_on_communication_website_id  (communication_website_id)
+#  index_communication_website_categories_on_language_id            (language_id)
+#  index_communication_website_categories_on_original_id            (original_id)
 #  index_communication_website_categories_on_parent_id              (parent_id)
 #  index_communication_website_categories_on_program_id             (program_id)
 #  index_communication_website_categories_on_university_id          (university_id)
 #
 # Foreign Keys
 #
+#  fk_rails_3186d8e327  (language_id => languages.id)
+#  fk_rails_52bd5968c9  (original_id => communication_website_categories.id)
 #  fk_rails_86a9ce3cea  (parent_id => communication_website_categories.id)
 #  fk_rails_9d4210dc43  (university_id => universities.id)
 #  fk_rails_c7c9f7ddc7  (communication_website_id => communication_websites.id)
@@ -46,6 +52,7 @@ class Communication::Website::Category < ApplicationRecord
   include WithTree
   include WithPermalink
   include WithPosition
+  include WithTranslations
 
   has_one                 :imported_category,
                           class_name: 'Communication::Website::Imported::Category',
@@ -121,11 +128,11 @@ class Communication::Website::Category < ApplicationRecord
   protected
 
   def last_ordered_element
-    website.categories.where(parent_id: parent_id).ordered.last
+    website.categories.where(parent_id: parent_id, language_id: language_id).ordered.last
   end
 
   def slug_unavailable?(slug)
-    self.class.unscoped.where(communication_website_id: self.communication_website_id, slug: slug).where.not(id: self.id).exists?
+    self.class.unscoped.where(communication_website_id: self.communication_website_id, language_id: language_id, slug: slug).where.not(id: self.id).exists?
   end
 
   def explicit_blob_ids
diff --git a/app/models/communication/website/menu.rb b/app/models/communication/website/menu.rb
index 44f048cbdcf5f61678fc528192bd0b50d5ccbae5..0642a267e1977d83bdb1365e02d2eeaedf22efdb 100644
--- a/app/models/communication/website/menu.rb
+++ b/app/models/communication/website/menu.rb
@@ -9,15 +9,21 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
 # Indexes
 #
 #  idx_comm_website_menus_on_communication_website_id  (communication_website_id)
+#  index_communication_website_menus_on_language_id    (language_id)
+#  index_communication_website_menus_on_original_id    (original_id)
 #  index_communication_website_menus_on_university_id  (university_id)
 #
 # Foreign Keys
 #
+#  fk_rails_2901ebb799  (original_id => communication_website_menus.id)
+#  fk_rails_4d43d36541  (language_id => languages.id)
 #  fk_rails_8d6227916e  (university_id => universities.id)
 #  fk_rails_dcc7198fc5  (communication_website_id => communication_websites.id)
 #
@@ -25,12 +31,13 @@ class Communication::Website::Menu < ApplicationRecord
   include WithUniversity
   include Sanitizable
   include WithGit
+  include WithTranslations
 
   belongs_to :website, foreign_key: :communication_website_id
   has_many :items, class_name: 'Communication::Website::Menu::Item', dependent: :destroy
 
   validates :title, :identifier, presence: true
-  validates :identifier, uniqueness: { scope: :communication_website_id }
+  validates :identifier, uniqueness: { scope: [:communication_website_id, :language_id] }
 
   scope :ordered, -> { order(created_at: :asc) }
 
@@ -39,13 +46,40 @@ class Communication::Website::Menu < ApplicationRecord
   end
 
   def git_path(website)
-    path = "data/menus/"
-    # TODO I18n : Right now, we continue to send only a master version
-    # path += "#{website.default_language.iso_code}/" if website.languages.any?
-    "#{path}#{identifier}.yml"
+    "data/menus/#{language.iso_code}/#{identifier}.yml"
   end
 
   def template_static
     "admin/communication/websites/menus/static"
   end
+
+  def translate_additional_data!(translation)
+    items.root.ordered.each { |item| translate_menu_item!(item, translation) }
+  end
+
+  def translate_menu_item!(item, menu_translation, parent_translation = nil)
+    item_translation = item.dup
+    item_translation.menu = menu_translation
+    item_translation.parent = parent_translation
+
+    # TODO : I18n
+    # For now, only pages, posts, categories are handled.
+    # We need to translate programs, diplomas, volumes and papers
+    set_item_translation_attributes(item_translation, item, menu_translation)
+
+    # If no translation and no children to translate, translation won't be save, as about is nil and kind requires one.
+    if item_translation.save
+      item.children.ordered.each do |child|
+        translate_menu_item!(child, menu_translation, item_translation)
+      end
+    end
+  end
+
+  def set_item_translation_attributes(item_translation, item, menu_translation)
+    return unless item.about.present? && item.about.respond_to?(:translation_for)
+    # Search for the target translation based on the given language.
+    item_translation.about = item.about.translation_for(menu_translation.language)
+    # If no target translation found, convert to a blank menu item if item has children.
+    item_translation.kind = 'blank' if item_translation.about.nil? && item.children.any?
+  end
 end
diff --git a/app/models/communication/website/menu/item.rb b/app/models/communication/website/menu/item.rb
index f66fa1e69f565d1ea0e4ce6b45ace2007123a787..c2bd314353df08e931b8040a7a21c98b30091558 100644
--- a/app/models/communication/website/menu/item.rb
+++ b/app/models/communication/website/menu/item.rb
@@ -67,7 +67,7 @@ class Communication::Website::Menu::Item < ApplicationRecord
 
   def self.icon_for(kind)
     icons = {
-      'blank' => 'font',
+      'blank' => Icon::COMMUNICATION_WEBSITE_MENU_BLANK,
       'diploma' => Icon::EDUCATION_DIPLOMA,
       'post' => Icon::COMMUNICATION_WEBSITE_POST,
       'category' => Icon::COMMUNICATION_WEBSITE_POST,
@@ -75,9 +75,9 @@ class Communication::Website::Menu::Item < ApplicationRecord
       'program' => Icon::EDUCATION_PROGRAM,
       'paper' => Icon::RESEARCH_LABORATORY,
       'volume' => Icon::RESEARCH_LABORATORY,
-      'url' => 'globe',
+      'url' => Icon::COMMUNICATION_WEBSITE_MENU_URL,
     }
-    "fas fa-#{icons[kind]}" if icons.has_key? kind
+    icons[kind] if icons.has_key? kind
   end
 
   def to_s
diff --git a/app/models/communication/website/page.rb b/app/models/communication/website/page.rb
index dcf2c6fd4f5765fde1b497578c0f4b8d8b405a8b..97a71f1a77c4addd1edaff7ff79f71f85aaa873a 100644
--- a/app/models/communication/website/page.rb
+++ b/app/models/communication/website/page.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 
@@ -48,14 +51,15 @@ class Communication::Website::Page < ApplicationRecord
   include WithUniversity
   include WithBlobs
   include WithBlocks
-  include WithGit
   include WithFeaturedImage
+  include WithGit
   include WithMenuItemTarget
   include WithPosition
   include WithTree
   include WithPath
   include WithType
   include WithPermalink
+  include WithTranslations
 
   has_summernote :text
 
@@ -64,7 +68,10 @@ class Communication::Website::Page < ApplicationRecord
   belongs_to :parent,
              class_name: 'Communication::Website::Page',
              optional: true
-  belongs_to :language, optional: true
+  belongs_to :original,
+             class_name: 'Communication::Website::Page',
+             optional: true
+  belongs_to :language
   has_one    :imported_page,
              class_name: 'Communication::Website::Imported::Page',
              dependent: :nullify
@@ -72,6 +79,9 @@ class Communication::Website::Page < ApplicationRecord
              class_name: 'Communication::Website::Page',
              foreign_key: :parent_id,
              dependent: :destroy
+  has_many   :translations,
+             class_name: 'Communication::Website::Page',
+             foreign_key: :original_id
 
   validates :title, presence: true
 
@@ -141,7 +151,7 @@ class Communication::Website::Page < ApplicationRecord
   end
 
   def last_ordered_element
-    website.pages.where(parent_id: parent_id).ordered.last
+    website.pages.where(parent_id: parent_id, language_id: language_id).ordered.last
   end
 
   def explicit_blob_ids
diff --git a/app/models/communication/website/page/accessibility.rb b/app/models/communication/website/page/accessibility.rb
index ef0fd58644fee35315173bb0b4054fad8995cf4d..b2a97fa5d352f38eaaa980add592d7b72f4e9cdd 100644
--- a/app/models/communication/website/page/accessibility.rb
+++ b/app/models/communication/website/page/accessibility.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::Accessibility < Communication::Website::Page
diff --git a/app/models/communication/website/page/administrator.rb b/app/models/communication/website/page/administrator.rb
index c39764290a06a55d3a85f493c0090d88a432385e..af96e7e54cdb47a236bc7800a61ebdeff9357ea4 100644
--- a/app/models/communication/website/page/administrator.rb
+++ b/app/models/communication/website/page/administrator.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::Administrator < Communication::Website::Page
@@ -53,7 +56,7 @@ class Communication::Website::Page::Administrator < Communication::Website::Page
   end
 
   def default_parent
-    website.special_page(Communication::Website::Page::Person)
+    website.special_page(Communication::Website::Page::Person, language: language)
   end
 
   def type_git_dependencies
diff --git a/app/models/communication/website/page/author.rb b/app/models/communication/website/page/author.rb
index d80c8daede27aec05f573ec3b5b4b4b8ac305255..ece2d2cfdf3b08956ad0dec5f15601db42e21631 100644
--- a/app/models/communication/website/page/author.rb
+++ b/app/models/communication/website/page/author.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::Author < Communication::Website::Page
@@ -48,7 +51,7 @@ class Communication::Website::Page::Author < Communication::Website::Page
   end
 
   def default_parent
-    website.special_page(Communication::Website::Page::Person)
+    website.special_page(Communication::Website::Page::Person, language: language)
   end
 
   def type_git_dependencies
diff --git a/app/models/communication/website/page/communication_post.rb b/app/models/communication/website/page/communication_post.rb
index 418530277d70a1cfa3d82fdab2f8ab0b0d95ace3..ce811f85eb8e5f7f04e060ddd22f3a0f4fd28f87 100644
--- a/app/models/communication/website/page/communication_post.rb
+++ b/app/models/communication/website/page/communication_post.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::CommunicationPost < Communication::Website::Page
diff --git a/app/models/communication/website/page/education_diploma.rb b/app/models/communication/website/page/education_diploma.rb
index 11ab1648a52782c99c7a7167be64f2e9a12f050f..1c1933758bb26498411b45362f1fa69aba01b231 100644
--- a/app/models/communication/website/page/education_diploma.rb
+++ b/app/models/communication/website/page/education_diploma.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::EducationDiploma < Communication::Website::Page
diff --git a/app/models/communication/website/page/education_program.rb b/app/models/communication/website/page/education_program.rb
index 74fd9b65350f537e7d5450e982447bcf54881dd7..a91fd7f06abae57684613331d900a6ea335f89f0 100644
--- a/app/models/communication/website/page/education_program.rb
+++ b/app/models/communication/website/page/education_program.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::EducationProgram < Communication::Website::Page
diff --git a/app/models/communication/website/page/home.rb b/app/models/communication/website/page/home.rb
index 5e4ee047a0a31f90738a49ca6c5465aaebaf41a6..f7e48dd76699736f501aa7aa55414cde3c5cce4d 100644
--- a/app/models/communication/website/page/home.rb
+++ b/app/models/communication/website/page/home.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::Home < Communication::Website::Page
diff --git a/app/models/communication/website/page/legal_term.rb b/app/models/communication/website/page/legal_term.rb
index 607cf9cf2709beeb9f445d5ffe745076925972b0..cc33f3bb6a3aa33ce8f6cc3acc04e01ec1f1ad52 100644
--- a/app/models/communication/website/page/legal_term.rb
+++ b/app/models/communication/website/page/legal_term.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::LegalTerm < Communication::Website::Page
diff --git a/app/models/communication/website/page/organization.rb b/app/models/communication/website/page/organization.rb
index 0fe98cfcce3b38aa8d7bf184575147e0e29c62de..c4ad7051d22e710800792ed469481c42d49062a6 100644
--- a/app/models/communication/website/page/organization.rb
+++ b/app/models/communication/website/page/organization.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::Organization < Communication::Website::Page
diff --git a/app/models/communication/website/page/person.rb b/app/models/communication/website/page/person.rb
index e035d5dd9085984c67003592f68424c943a11902..07f991cd0f3a8d1bb994525cf7e8022e7cbe0c1f 100644
--- a/app/models/communication/website/page/person.rb
+++ b/app/models/communication/website/page/person.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::Person < Communication::Website::Page
diff --git a/app/models/communication/website/page/privacy_policy.rb b/app/models/communication/website/page/privacy_policy.rb
index e94f7b8854b23d129df9afc513bf8ee8fb945b4a..478783031efce361ff29e566fe8b37532f38bb3a 100644
--- a/app/models/communication/website/page/privacy_policy.rb
+++ b/app/models/communication/website/page/privacy_policy.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::PrivacyPolicy < Communication::Website::Page
diff --git a/app/models/communication/website/page/research_paper.rb b/app/models/communication/website/page/research_paper.rb
index a485ea2e8eacc31e1a5cc7648425ba7f43b34872..ea15cbfe84d6cfeb814235d414f48021b3dda5ce 100644
--- a/app/models/communication/website/page/research_paper.rb
+++ b/app/models/communication/website/page/research_paper.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::ResearchPaper < Communication::Website::Page
diff --git a/app/models/communication/website/page/research_volume.rb b/app/models/communication/website/page/research_volume.rb
index 0cb730dab1dca26cdab01fdadddf2e0fa22c58aa..2eff7f3a6d4ac8cce23d62dd46169b0e24d6992e 100644
--- a/app/models/communication/website/page/research_volume.rb
+++ b/app/models/communication/website/page/research_volume.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::ResearchVolume < Communication::Website::Page
diff --git a/app/models/communication/website/page/researcher.rb b/app/models/communication/website/page/researcher.rb
index 4dc3ca98d7c6bb366ead2726157c86b42f095fd6..4fe548b24d4d69c8d7f20db9771316c36c6b3856 100644
--- a/app/models/communication/website/page/researcher.rb
+++ b/app/models/communication/website/page/researcher.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::Researcher < Communication::Website::Page
@@ -46,13 +49,13 @@ class Communication::Website::Page::Researcher < Communication::Website::Page
   end
 
   protected
-  
+
   def current_git_path
     @current_git_path ||= "#{git_path_prefix}researchers/_index.html"
   end
 
   def default_parent
-    website.special_page(Communication::Website::Page::Person)
+    website.special_page(Communication::Website::Page::Person, language: language)
   end
 
   def type_git_dependencies
diff --git a/app/models/communication/website/page/sitemap.rb b/app/models/communication/website/page/sitemap.rb
index 6da00584bc11275950ea23f0411cae9d3415d1e1..3675c8417c7b17d5dead1738be3353cd3f18113b 100644
--- a/app/models/communication/website/page/sitemap.rb
+++ b/app/models/communication/website/page/sitemap.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::Sitemap < Communication::Website::Page
diff --git a/app/models/communication/website/page/teacher.rb b/app/models/communication/website/page/teacher.rb
index d9d1daacb49fb42432ad5155aa9d4d579030b7ce..0332253ad6cf1bd28bd14b910324b408df1bb35c 100644
--- a/app/models/communication/website/page/teacher.rb
+++ b/app/models/communication/website/page/teacher.rb
@@ -22,7 +22,8 @@
 #  created_at               :datetime         not null
 #  updated_at               :datetime         not null
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  parent_id                :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
@@ -30,6 +31,7 @@
 #
 #  index_communication_website_pages_on_communication_website_id  (communication_website_id)
 #  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
 #  index_communication_website_pages_on_parent_id                 (parent_id)
 #  index_communication_website_pages_on_university_id             (university_id)
 #
@@ -37,6 +39,7 @@
 #
 #  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
 #  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
 #  fk_rails_d208d15a73  (university_id => universities.id)
 #
 class Communication::Website::Page::Teacher < Communication::Website::Page
@@ -46,13 +49,13 @@ class Communication::Website::Page::Teacher < Communication::Website::Page
   end
 
   protected
-  
+
   def current_git_path
     @current_git_path ||= "#{git_path_prefix}teachers/_index.html"
   end
 
   def default_parent
-    website.special_page(Communication::Website::Page::Person)
+    website.special_page(Communication::Website::Page::Person, language: language)
   end
 
   def type_git_dependencies
diff --git a/app/models/communication/website/page/with_path.rb b/app/models/communication/website/page/with_path.rb
index 99b4692e3c6dbc8efee742b5d5a6185543c07d23..838522b21787f90f29ef9973d897afa901352445 100644
--- a/app/models/communication/website/page/with_path.rb
+++ b/app/models/communication/website/page/with_path.rb
@@ -8,9 +8,8 @@ module Communication::Website::Page::WithPath
 
   def path
     path = ''
-    # TODO i18n remplacer le choix de la langue
     if website.languages.many?
-      path += "/#{website.default_language.iso_code}"
+      path += "/#{language.iso_code}"
     end
     path += "/#{slug_with_ancestors}/"
     path.gsub(/\/+/, '/')
@@ -55,7 +54,7 @@ module Communication::Website::Page::WithPath
 
   def slug_unavailable?(slug)
     self.class.unscoped
-              .where(communication_website_id: self.communication_website_id, slug: slug)
+              .where(communication_website_id: self.communication_website_id, language_id: language_id, slug: slug)
               .where.not(id: self.id)
               .exists?
   end
diff --git a/app/models/communication/website/page/with_type.rb b/app/models/communication/website/page/with_type.rb
index c465ecd000765298eb3fe26fdbe044371d2e4586..861602c81e09ff5c161b3797aded0a4a5987cd13 100644
--- a/app/models/communication/website/page/with_type.rb
+++ b/app/models/communication/website/page/with_type.rb
@@ -88,7 +88,7 @@ module Communication::Website::Page::WithType
   protected
 
   def default_parent
-    website.special_page(Communication::Website::Page::Home)
+    website.special_page(Communication::Website::Page::Home, language: language)
   end
 
   def type_git_dependencies
@@ -103,5 +103,5 @@ module Communication::Website::Page::WithType
     self.full_width = full_width_by_default?
     self.published = published_by_default?
   end
-  
+
 end
diff --git a/app/models/communication/website/permalink.rb b/app/models/communication/website/permalink.rb
index 276d8df83af5f599212cc2f6cc269c287ae73505..88f821fd28202d11e3338cce0ff2accac97f517b 100644
--- a/app/models/communication/website/permalink.rb
+++ b/app/models/communication/website/permalink.rb
@@ -54,9 +54,9 @@ class Communication::Website::Permalink < ApplicationRecord
   scope :current, -> { where(is_current: true) }
   scope :not_current, -> { where(is_current: false) }
 
-  def self.config_in_website(website)
+  def self.config_in_website(website, language)
     required_kinds_in_website(website).map { |permalink_class|
-      [permalink_class.static_config_key, permalink_class.pattern_in_website(website)]
+      [permalink_class.static_config_key, permalink_class.pattern_in_website(website, language)]
     }.to_h
   end
 
@@ -84,12 +84,13 @@ class Communication::Website::Permalink < ApplicationRecord
     MAPPING.keys.include?(lookup_key)
   end
 
-  def self.pattern_in_website(website)
+  def self.pattern_in_website(website, language)
     raise NotImplementedError
   end
 
   def pattern
-    self.class.pattern_in_website(website)
+    language = about.respond_to?(:language) ? about.language : website.default_language
+    self.class.pattern_in_website(website, language)
   end
 
   def computed_path
@@ -122,8 +123,9 @@ class Communication::Website::Permalink < ApplicationRecord
   # Can be overwritten (Page for example)
   def published_path
     # TODO I18n doit prendre la langue du about
+    language = about.respond_to?(:language) ? about.language : website.default_language
     p = ""
-    p += "/#{website.default_language.iso_code}" if website.languages.many?
+    p += "/#{language.iso_code}" if website.languages.many?
     p += pattern
     substitutions.each do |key, value|
       p.gsub! ":#{key}", "#{value}"
diff --git a/app/models/communication/website/permalink/administrator.rb b/app/models/communication/website/permalink/administrator.rb
index 9a95d44d5c83cced6089ee19483dbe44e0f0f4fe..7270c11c7daa32b803ad45ff9bac8c3151c4e464 100644
--- a/app/models/communication/website/permalink/administrator.rb
+++ b/app/models/communication/website/permalink/administrator.rb
@@ -33,7 +33,7 @@ class Communication::Website::Permalink::Administrator < Communication::Website:
   end
 
   # /equipe/:slug/roles/
-  def self.pattern_in_website(website)
-    "/#{website.special_page(Communication::Website::Page::Person).slug_with_ancestors}/:slug/roles/"
+  def self.pattern_in_website(website, language)
+    "/#{website.special_page(Communication::Website::Page::Person, language: language).slug_with_ancestors}/:slug/roles/"
   end
 end
diff --git a/app/models/communication/website/permalink/author.rb b/app/models/communication/website/permalink/author.rb
index 8c0b639b40240d817036818b4b92b9546e5b8fa3..0740788a9694c43ba9768718e3984d381abb2474 100644
--- a/app/models/communication/website/permalink/author.rb
+++ b/app/models/communication/website/permalink/author.rb
@@ -34,7 +34,7 @@ class Communication::Website::Permalink::Author < Communication::Website::Permal
   end
 
   # /equipe/:slug/actualites/
-  def self.pattern_in_website(website)
-    "/#{website.special_page(Communication::Website::Page::Person).slug_with_ancestors}/:slug/#{website.special_page(Communication::Website::Page::CommunicationPost).slug}/"
+  def self.pattern_in_website(website, language)
+    "/#{website.special_page(Communication::Website::Page::Person, language: language).slug_with_ancestors}/:slug/#{website.special_page(Communication::Website::Page::CommunicationPost, language: language).slug}/"
   end
 end
diff --git a/app/models/communication/website/permalink/category.rb b/app/models/communication/website/permalink/category.rb
index 852dbfb3fe07c16c8b5f671e225666688caccb49..b4ae97e26c4ebf150a1e5579a75cfa5e7317c8e4 100644
--- a/app/models/communication/website/permalink/category.rb
+++ b/app/models/communication/website/permalink/category.rb
@@ -33,8 +33,8 @@ class Communication::Website::Permalink::Category < Communication::Website::Perm
   end
 
   # /actualites/:slug/
-  def self.pattern_in_website(website)
-    "/#{website.special_page(Communication::Website::Page::CommunicationPost).slug_with_ancestors}/:slug/"
+  def self.pattern_in_website(website, language)
+    "/#{website.special_page(Communication::Website::Page::CommunicationPost, language: language).slug_with_ancestors}/:slug/"
   end
 
   protected
diff --git a/app/models/communication/website/permalink/diploma.rb b/app/models/communication/website/permalink/diploma.rb
index 64e9f2a9e37862da851be2f0019635105dbbfd7c..8b46f82a384e6ec208415bf4460a328432d040c6 100644
--- a/app/models/communication/website/permalink/diploma.rb
+++ b/app/models/communication/website/permalink/diploma.rb
@@ -33,7 +33,7 @@ class Communication::Website::Permalink::Diploma < Communication::Website::Perma
   end
 
   # /diplomes/:slug/
-  def self.pattern_in_website(website)
-    "/#{website.special_page(Communication::Website::Page::EducationDiploma).slug_with_ancestors}/:slug/"
+  def self.pattern_in_website(website, language)
+    "/#{website.special_page(Communication::Website::Page::EducationDiploma, language: language).slug_with_ancestors}/:slug/"
   end
 end
diff --git a/app/models/communication/website/permalink/organization.rb b/app/models/communication/website/permalink/organization.rb
index c3d3e8ec7d1c9e200d83eba3e935a80b801a82a1..6c203f9e8ab5895887ed9c0683c06a311b669f87 100644
--- a/app/models/communication/website/permalink/organization.rb
+++ b/app/models/communication/website/permalink/organization.rb
@@ -33,7 +33,7 @@ class Communication::Website::Permalink::Organization < Communication::Website::
   end
 
   # /organisations/:slug/
-  def self.pattern_in_website(website)
-    "/#{website.special_page(Communication::Website::Page::Organization).slug_with_ancestors}/:slug/"
+  def self.pattern_in_website(website, language)
+    "/#{website.special_page(Communication::Website::Page::Organization, language: language).slug_with_ancestors}/:slug/"
   end
 end
diff --git a/app/models/communication/website/permalink/paper.rb b/app/models/communication/website/permalink/paper.rb
index 430fee83b4743d2f765a2391b1576e27da5fd88a..2ef3f41e6fb8c3c8d8b447d1d8aefb1cf7bfd1e7 100644
--- a/app/models/communication/website/permalink/paper.rb
+++ b/app/models/communication/website/permalink/paper.rb
@@ -33,8 +33,8 @@ class Communication::Website::Permalink::Paper < Communication::Website::Permali
   end
 
   # /papiers/:slug/
-  def self.pattern_in_website(website)
-    "/#{website.special_page(Communication::Website::Page::ResearchPaper).slug_with_ancestors}/:year-:month-:day-:slug/"
+  def self.pattern_in_website(website, language)
+    "/#{website.special_page(Communication::Website::Page::ResearchPaper, language: language).slug_with_ancestors}/:year-:month-:day-:slug/"
   end
 
   protected
diff --git a/app/models/communication/website/permalink/person.rb b/app/models/communication/website/permalink/person.rb
index 760c5d9ce0c3ca9a8b5eab4031de9bf0f26bc4bb..82cee2af84fae0494a11e73228a0dd2e3766ef74 100644
--- a/app/models/communication/website/permalink/person.rb
+++ b/app/models/communication/website/permalink/person.rb
@@ -33,7 +33,7 @@ class Communication::Website::Permalink::Person < Communication::Website::Permal
   end
 
   # /equipe/:slug/
-  def self.pattern_in_website(website)
-    "/#{website.special_page(Communication::Website::Page::Person).slug_with_ancestors}/:slug/"
+  def self.pattern_in_website(website, language)
+    "/#{website.special_page(Communication::Website::Page::Person, language: language).slug_with_ancestors}/:slug/"
   end
 end
diff --git a/app/models/communication/website/permalink/post.rb b/app/models/communication/website/permalink/post.rb
index 8525ea0e63446354baa41106427fb8556fa2debd..b12ce72babd5d143531909e42f75e1f207deeacf 100644
--- a/app/models/communication/website/permalink/post.rb
+++ b/app/models/communication/website/permalink/post.rb
@@ -33,8 +33,8 @@ class Communication::Website::Permalink::Post < Communication::Website::Permalin
   end
 
   # /actualites/2022-10-21-un-article/
-  def self.pattern_in_website(website)
-    "/#{website.special_page(Communication::Website::Page::CommunicationPost).slug_with_ancestors}/:year-:month-:day-:slug/"
+  def self.pattern_in_website(website, language)
+    "/#{website.special_page(Communication::Website::Page::CommunicationPost, language: language).slug_with_ancestors}/:year-:month-:day-:slug/"
   end
 
   protected
diff --git a/app/models/communication/website/permalink/program.rb b/app/models/communication/website/permalink/program.rb
index 8c1c0e02dda11940996d4c18f0e22708c47399e3..00290533a28e38b844e4abc8ee07b942d8eb99ab 100644
--- a/app/models/communication/website/permalink/program.rb
+++ b/app/models/communication/website/permalink/program.rb
@@ -33,7 +33,7 @@ class Communication::Website::Permalink::Program < Communication::Website::Perma
   end
 
   # /formations/:slug/
-  def self.pattern_in_website(website)
-    "/#{website.special_page(Communication::Website::Page::EducationProgram).slug_with_ancestors}/:slug/"
+  def self.pattern_in_website(website, language)
+    "/#{website.special_page(Communication::Website::Page::EducationProgram, language: language).slug_with_ancestors}/:slug/"
   end
 end
diff --git a/app/models/communication/website/permalink/researcher.rb b/app/models/communication/website/permalink/researcher.rb
index a97b8aa54260c3d2a665db8408ae63ad1a5aa286..c0b045b8294fd5bb9f25e1859bb87c580eb0deb6 100644
--- a/app/models/communication/website/permalink/researcher.rb
+++ b/app/models/communication/website/permalink/researcher.rb
@@ -34,7 +34,7 @@ class Communication::Website::Permalink::Researcher < Communication::Website::Pe
 
   # /equipe/:slug/publications/
   # FIXME
-  def self.pattern_in_website(website)
-    "/#{website.special_page(Communication::Website::Page::Person).slug_with_ancestors}/:slug/publications/"
+  def self.pattern_in_website(website, language)
+    "/#{website.special_page(Communication::Website::Page::Person, language: language).slug_with_ancestors}/:slug/papers/"
   end
 end
diff --git a/app/models/communication/website/permalink/teacher.rb b/app/models/communication/website/permalink/teacher.rb
index 6a75142aa196cd79dd88b2cac0285bd3fcb5c63f..de3dd68499f1dc443ac196c832170d1a4bfbe16e 100644
--- a/app/models/communication/website/permalink/teacher.rb
+++ b/app/models/communication/website/permalink/teacher.rb
@@ -34,7 +34,7 @@ class Communication::Website::Permalink::Teacher < Communication::Website::Perma
 
   # /equipe/:slug/programs/
   # FIXME
-  def self.pattern_in_website(website)
-    "/#{website.special_page(Communication::Website::Page::Person).slug_with_ancestors}/:slug/programs/"
+  def self.pattern_in_website(website, language)
+    "/#{website.special_page(Communication::Website::Page::Person, language: language).slug_with_ancestors}/:slug/programs/"
   end
 end
diff --git a/app/models/communication/website/permalink/volume.rb b/app/models/communication/website/permalink/volume.rb
index 96774e65de72512b292fee2ca322f3c8ae6790f6..1ceed05c79f00fcc80e1ffc613899998a68afeba 100644
--- a/app/models/communication/website/permalink/volume.rb
+++ b/app/models/communication/website/permalink/volume.rb
@@ -33,8 +33,8 @@ class Communication::Website::Permalink::Volume < Communication::Website::Permal
   end
 
   # /volumes/:slug/
-  def self.pattern_in_website(website)
-    "/#{website.special_page(Communication::Website::Page::ResearchVolume).slug_with_ancestors}/:year-:slug/"
+  def self.pattern_in_website(website, language)
+    "/#{website.special_page(Communication::Website::Page::ResearchVolume, language: language).slug_with_ancestors}/:year-:slug/"
   end
 
   protected
diff --git a/app/models/communication/website/post.rb b/app/models/communication/website/post.rb
index 9d38b4b54887e612bf804d509f2f47fcba52eaff..5299cd44121f92a0b6a3aa2eb28d9e099f77242f 100644
--- a/app/models/communication/website/post.rb
+++ b/app/models/communication/website/post.rb
@@ -18,7 +18,8 @@
 #  updated_at               :datetime         not null
 #  author_id                :uuid             indexed
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
 # Indexes
@@ -26,11 +27,13 @@
 #  index_communication_website_posts_on_author_id                 (author_id)
 #  index_communication_website_posts_on_communication_website_id  (communication_website_id)
 #  index_communication_website_posts_on_language_id               (language_id)
+#  index_communication_website_posts_on_original_id               (original_id)
 #  index_communication_website_posts_on_university_id             (university_id)
 #
 # Foreign Keys
 #
 #  fk_rails_1e0d058a25  (university_id => universities.id)
+#  fk_rails_bbbef3b1e9  (original_id => communication_website_posts.id)
 #  fk_rails_d1c1a10946  (communication_website_id => communication_websites.id)
 #  fk_rails_e0eec447b0  (author_id => university_people.id)
 #
@@ -44,6 +47,7 @@ class Communication::Website::Post < ApplicationRecord
   include WithMenuItemTarget
   include WithPermalink
   include WithSlug # We override slug_unavailable? method
+  include WithTranslations
 
   has_summernote :text
 
@@ -56,7 +60,6 @@ class Communication::Website::Post < ApplicationRecord
   belongs_to :author,
              class_name: 'University::Person',
              optional: true
-  belongs_to :language, optional: true
   has_and_belongs_to_many :categories,
                           class_name: 'Communication::Website::Category',
                           join_table: 'communication_website_categories_posts',
@@ -123,8 +126,9 @@ class Communication::Website::Post < ApplicationRecord
     dependencies += git_block_dependencies
     dependencies += university.communication_blocks.where(template_kind: :posts).includes(:about).map(&:about).uniq
     if author.present?
-      dependencies += [author, author.author]
+      dependencies += [author, author.author, translated_author, translated_author.author]
       dependencies += author.active_storage_blobs
+      dependencies += translated_author.active_storage_blobs
     end
     dependencies
   end
@@ -141,6 +145,10 @@ class Communication::Website::Post < ApplicationRecord
     "#{Static.remove_trailing_slash website.url}#{Static.clean_path current_permalink_in_website(website).path}"
   end
 
+  def translated_author
+    @translated_author ||= author.find_or_translate!(language)
+  end
+
   def to_s
     "#{title}"
   end
@@ -149,7 +157,7 @@ class Communication::Website::Post < ApplicationRecord
 
   def slug_unavailable?(slug)
     self.class.unscoped
-              .where(communication_website_id: self.communication_website_id, slug: slug)
+              .where(communication_website_id: self.communication_website_id, language_id: language_id, slug: slug)
               .where.not(id: self.id)
               .exists?
   end
@@ -173,4 +181,12 @@ class Communication::Website::Post < ApplicationRecord
     end
     author.update_and_sync(is_author: true) if author_id
   end
+
+  def translate_additional_data!(translation)
+    categories.each do |category|
+      translated_category = category.find_or_translate!(translation.language)
+      translation.categories << translated_category
+    end
+    translation.update(author_id: author.find_or_translate!(translation.language).id) if author_id.present?
+  end
 end
diff --git a/app/models/communication/website/with_dependencies.rb b/app/models/communication/website/with_dependencies.rb
index 0f3d17760593c3c122fa653e602167d1a5cdf0c0..708ad848f3aade75ce49030ba0481abe7a591b33 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/communication/website/with_menus.rb b/app/models/communication/website/with_menus.rb
index 8ed45032fc4cdc2f4c721ab795b723444d17da9e..256f7343ff18133830966dd02c807762e30192a6 100644
--- a/app/models/communication/website/with_menus.rb
+++ b/app/models/communication/website/with_menus.rb
@@ -66,33 +66,34 @@ module Communication::Website::WithMenus
   def initialize_menus
     find_or_create_menu 'primary'
     find_or_create_menu 'social'
-    menu = find_or_create_menu 'legal'
-    fill_legal_menu menu
+    find_or_create_menu('legal') do |menu|
+      # Only executed after menu creation
+      fill_legal_menu(menu)
+    end
+  end
+
+  def find_or_create_menu(identifier)
+    menu = menus.where(identifier: identifier, university: university, language: default_language).first_or_initialize do |menu|
+      menu.title = t("communication.menus.default_title.#{identifier}")
+    end
+    unless menu.persisted?
+      menu.save
+      yield(menu) if block_given?
+    end
+    menu
   end
 
   def fill_legal_menu(menu)
-    return if menu.items.any?
     [
       Communication::Website::Page::LegalTerm,
       Communication::Website::Page::PrivacyPolicy,
       Communication::Website::Page::Accessibility,
       Communication::Website::Page::Sitemap
     ].each do |page_class|
-      page = special_page(page_class)
-      menu.items.where( kind: 'page', 
-                        about: page,
-                        university: university,
-                        website: self)
-                .first_or_create do |item|
+      page = special_page(page_class, language: menu.language)
+      menu.items.where(kind: 'page', about: page, university: university, website: self).first_or_create do |item|
         item.title = page.title
       end
     end
   end
-
-  def find_or_create_menu(identifier)
-    title = Communication::Website::Menu.human_attribute_name(identifier)
-    menus.where(identifier: identifier, university: university).first_or_create do |menu|
-      menu.title = title
-    end
-  end
 end
diff --git a/app/models/communication/website/with_program_categories.rb b/app/models/communication/website/with_program_categories.rb
index 109e5096504ad858ea9f248efba9fd6c2f25a2a3..a2f9bd468a7298aad76962dd2c746e32946ec5b4 100644
--- a/app/models/communication/website/with_program_categories.rb
+++ b/app/models/communication/website/with_program_categories.rb
@@ -5,8 +5,10 @@ module Communication::Website::WithProgramCategories
     after_save_commit :set_programs_categories!, if: -> (website) { website.has_education_programs? }
   end
 
+  # TODO : I18n
+  # Actuellement, on ne crée que dans la langue par défaut du website, on ne gère pas les autres langues
   def set_programs_categories!
-    programs_root_category = categories.where(is_programs_root: true).first_or_create(
+    programs_root_category = categories.for_language_id(default_language_id).where(is_programs_root: true).first_or_create(
       name: 'Offre de formation',
       slug: 'offre-de-formation',
       is_programs_root: true,
@@ -20,7 +22,7 @@ module Communication::Website::WithProgramCategories
 
   def set_programs_categories_at_level!(parent_category, programs)
     programs.map.with_index do |program, index|
-      category = categories.where(program_id: program.id).first_or_initialize(
+      category = categories.for_language_id(default_language_id).where(program_id: program.id).first_or_initialize(
         name: program.name,
         slug: program.name.parameterize,
         university_id: university.id
diff --git a/app/models/communication/website/with_special_pages.rb b/app/models/communication/website/with_special_pages.rb
index aa82562110c39874a53aeb11705e9d0d3e243539..52e36e59acb75057090a8f3075b7d6672d384986 100644
--- a/app/models/communication/website/with_special_pages.rb
+++ b/app/models/communication/website/with_special_pages.rb
@@ -6,16 +6,28 @@ module Communication::Website::WithSpecialPages
     after_touch :create_missing_special_pages
   end
 
-  def special_page(type)
-    pages.where(type: type.to_s).first
+  def special_page(type, language: default_language)
+    find_special_page(type, language) || translate_special_page(type, language)
   end
 
   def create_missing_special_pages
     Communication::Website::Page::TYPES.each do |page_class|
-      page = page_class.where(website: self, university: university).first_or_initialize # Special pages have an before_validation (:on_create) callback to preset title, slug, ...
+      page = page_class.where(website: self, university: university, language_id: default_language_id).first_or_initialize # Special pages have an before_validation (:on_create) callback to preset title, slug, ...
       next if page.persisted? # No resave
       next unless page.is_necessary_for_website? # No useless pages
       page.save_and_sync
     end
   end
+
+  protected
+
+  def find_special_page(type, language)
+    pages.where(type: type.to_s, language_id: language.id).first
+  end
+
+  def translate_special_page(type, language)
+    # Not found for given language, we create it from the page in default_language
+    original_special_page = pages.where(type: type.to_s, language_id: default_language_id).first
+    original_special_page.translate!(language) if original_special_page.present?
+  end
 end
diff --git a/app/models/concerns/with_blocks.rb b/app/models/concerns/with_blocks.rb
index a710e06fbb736c612fb74cb2e607910f6cf877e3..ccf1e024f8c47811b2990cf26529470406f5ca17 100644
--- a/app/models/concerns/with_blocks.rb
+++ b/app/models/concerns/with_blocks.rb
@@ -11,7 +11,8 @@ module WithBlocks
 
   # Basic rule is: TOC if 2 titles or more
   def show_toc?
-    blocks.collect(&:title)
+    blocks.published
+          .collect(&:title)
           .reject(&:blank?)
           .many?
   end
diff --git a/app/models/concerns/with_featured_image.rb b/app/models/concerns/with_featured_image.rb
index 615a2e0ba03810fa32efafddb0585040351a10ad..d7f8102d2d1fa8bdffe9a164adc8598a1c6b8bde 100644
--- a/app/models/concerns/with_featured_image.rb
+++ b/app/models/concerns/with_featured_image.rb
@@ -33,23 +33,16 @@ module WithFeaturedImage
     photo = Unsplash::Photo.find id
     url = "#{photo['urls']['full']}&w=2048&fit=max"
     filename = "#{photo['id']}.jpg"
-    begin
-      file = URI.open url
-      featured_image.attach(io: file, filename: filename)
-      photo.track_download
-    rescue
-    end
+    file = URI.open url
+    featured_image.attach(io: file, filename: filename)
+    photo.track_download
   end
 
   def photo_import_pexels(id)
     photo = Pexels::Client.new.photos.find id
     url = "#{photo.src['original']}?auto=compress&cs=tinysrgb&w=2048"
     filename = "#{photo.id}.png"
-    begin
-      file = URI.open url
-      featured_image.attach(io: file, filename: filename)
-    rescue
-      byebug
-    end
+    file = URI.open url
+    featured_image.attach(io: file, filename: filename)
   end
 end
diff --git a/app/models/concerns/with_git.rb b/app/models/concerns/with_git.rb
index 6fec8601b9fbb866450d12192d19f6abd910f77a..e920fb7c867e2af41471c49998ac08d123c05109 100644
--- a/app/models/concerns/with_git.rb
+++ b/app/models/concerns/with_git.rb
@@ -13,12 +13,11 @@ module WithGit
   end
 
   def git_path_content_prefix(website)
-    # Handle legacy language-less websites
-    # TODO I18n: Right now, we use the language of the website. It HAS TO get the language from the object including this concern.
+    # Handle language-less objects
+    # TODO I18n: Right now, we use the language of the object, fallbacking on the language of the website. In the end, we'll only use the language of the object
     path = "content/"
-    if website.languages.any?
-      path += "#{website.default_language.iso_code}/"
-    end
+    path_language = respond_to?(:language_id) && language_id.present? ? language : website.default_language
+    path += "#{path_language.iso_code}/"
     path
   end
 
diff --git a/app/models/concerns/with_slug.rb b/app/models/concerns/with_slug.rb
index 26fbf3ab9f5a784aa76967808c5c2bd57c570426..f7042b19fc7f9514d5bb80d7c8e831a65dcb051e 100644
--- a/app/models/concerns/with_slug.rb
+++ b/app/models/concerns/with_slug.rb
@@ -25,8 +25,11 @@ module WithSlug
     protected
 
     def slug_unavailable?(slug)
+      existence_params = { university_id: self.university_id, slug: slug }
+      existence_params[:language_id] = self.language_id if respond_to?(:language_id)
+
       self.class.unscoped
-                .where(university_id: self.university_id, slug: slug)
+                .where(**existence_params)
                 .where.not(id: self.id)
                 .exists?
     end
diff --git a/app/models/concerns/with_translations.rb b/app/models/concerns/with_translations.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3d1771af782eeccbbe4ecb55004e9a3f356eef04
--- /dev/null
+++ b/app/models/concerns/with_translations.rb
@@ -0,0 +1,102 @@
+module WithTranslations
+  extend ActiveSupport::Concern
+
+  included do
+    attr_accessor :newly_translated
+
+    belongs_to  :language
+    belongs_to  :original, class_name: base_class.to_s, optional: true
+    has_many    :translations, class_name: base_class.to_s, foreign_key: :original_id, dependent: :nullify
+
+    scope :for_language, -> (language) { for_language_id(language.id) }
+    # The for_language_id scope can be used when you have the ID without needing to load the Language itself
+    scope :for_language_id, -> (language_id) { where(language_id: language_id) }
+
+  end
+
+  def available_languages
+    @available_languages ||= begin
+      languages = respond_to?(:website) ? website.languages : Language.all
+      languages.ordered
+    end
+  end
+
+  def find_or_translate!(language)
+    translation = translation_for(language)
+    translation ||= translate!(language)
+    translation
+  end
+
+  def translation_for(language)
+    # If the requested language is the object language, we return itself
+    return self if language_id == language.id
+    # All translations share the same original.
+    # If the current object is a translation, we call translation_for on the original.
+    # Else, if the current object is the original, we search the translation with the language.
+    original_id.present?  ? original.translation_for(language)
+                          : translations.find_by(language_id: language.id)
+  end
+
+  def original_object
+    @original_object ||= (self.original || self)
+  end
+
+  def original_with_translations
+    original_object.translations + [original_object]
+  end
+
+  # Used by Hugo to link translations with themselves
+  def static_translation_key
+    "#{self.class.polymorphic_name.parameterize}-#{self.original_object.id}"
+  end
+
+  def translate!(language)
+    translation = self.dup
+
+    # Inherits from original_id or set it to itself
+    translation.assign_attributes(
+      original_id: original_object.id,
+      language_id: language.id,
+      newly_translated: true
+    )
+
+    # Handle publication
+    translation.published = false if respond_to?(:published)
+    # Translate parent if needed
+    translation.parent_id = translate_parent!(language)&.id if respond_to?(:parent_id)
+    # Handle featured image if object has one
+    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)
+    translate_additional_data!(translation)
+
+    translation
+  end
+
+  protected
+
+  def translate_parent!(language)
+    return nil if parent_id.nil?
+    parent.find_or_translate!(language)
+  end
+
+  def translate_blocks!(translation)
+    blocks.ordered.each do |block|
+      block.translate!(translation)
+    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
+end
\ No newline at end of file
diff --git a/app/models/language.rb b/app/models/language.rb
index 047a01e1ac38ade22b87e265ec4989127079d4ca..36cf5338a0bc6d3f337c79dcac2fdaacb195158f 100644
--- a/app/models/language.rb
+++ b/app/models/language.rb
@@ -20,7 +20,7 @@ class Language < ApplicationRecord
   validates_presence_of :iso_code
   validates_uniqueness_of :iso_code
 
-  default_scope { order(name: :asc) }
+  scope :ordered, -> { order(name: :asc) }
 
   def to_s
     "#{name}"
diff --git a/app/models/university.rb b/app/models/university.rb
index 96f44ec4a8cf892d7a063d72b354b6ab28550507..a255b9591fbbebd2ae9cfa51713ec0b251dd891c 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 7da64b342f8c3d74c2d827d49bb3383b43090cb4..32356fa8d7273538dbbe207914888044f4446b80 100644
--- a/app/models/university/person.rb
+++ b/app/models/university/person.rb
@@ -2,47 +2,54 @@
 #
 # Table name: university_people
 #
-#  id                 :uuid             not null, primary key
-#  address            :string
-#  biography          :text
-#  birthdate          :date
-#  city               :string
-#  country            :string
-#  email              :string
-#  first_name         :string
-#  gender             :integer
-#  habilitation       :boolean          default(FALSE)
-#  is_administration  :boolean
-#  is_alumnus         :boolean          default(FALSE)
-#  is_author          :boolean
-#  is_researcher      :boolean
-#  is_teacher         :boolean
-#  last_name          :string
-#  linkedin           :string
-#  mastodon           :string
-#  meta_description   :text
-#  name               :string
-#  phone_mobile       :string
-#  phone_personal     :string
-#  phone_professional :string
-#  slug               :string
-#  summary            :text
-#  tenure             :boolean          default(FALSE)
-#  twitter            :string
-#  url                :string
-#  zipcode            :string
-#  created_at         :datetime         not null
-#  updated_at         :datetime         not null
-#  university_id      :uuid             not null, indexed
-#  user_id            :uuid             indexed
+#  id                    :uuid             not null, primary key
+#  address               :string
+#  biography             :text
+#  birthdate             :date
+#  city                  :string
+#  country               :string
+#  email                 :string
+#  first_name            :string
+#  gender                :integer
+#  habilitation          :boolean          default(FALSE)
+#  hal_person_identifier :string
+#  is_administration     :boolean
+#  is_alumnus            :boolean          default(FALSE)
+#  is_author             :boolean
+#  is_researcher         :boolean
+#  is_teacher            :boolean
+#  last_name             :string
+#  linkedin              :string
+#  mastodon              :string
+#  meta_description      :text
+#  name                  :string
+#  phone_mobile          :string
+#  phone_personal        :string
+#  phone_professional    :string
+#  slug                  :string
+#  summary               :text
+#  tenure                :boolean          default(FALSE)
+#  twitter               :string
+#  url                   :string
+#  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 +66,7 @@ class University::Person < ApplicationRecord
   include WithBlocks
   include WithPermalink
   include WithResearch
+  include WithTranslations
 
   LIST_OF_ROLES = [
     :administration,
@@ -112,7 +120,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 +248,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 bac4deebe718a2416d90e51c40693d7e3c8c8339..14c3f802b06c8aa3f388e645e69a1fe777db5074 100644
--- a/app/models/university/person/administrator.rb
+++ b/app/models/university/person/administrator.rb
@@ -2,47 +2,54 @@
 #
 # Table name: university_people
 #
-#  id                 :uuid             not null, primary key
-#  address            :string
-#  biography          :text
-#  birthdate          :date
-#  city               :string
-#  country            :string
-#  email              :string
-#  first_name         :string
-#  gender             :integer
-#  habilitation       :boolean          default(FALSE)
-#  is_administration  :boolean
-#  is_alumnus         :boolean          default(FALSE)
-#  is_author          :boolean
-#  is_researcher      :boolean
-#  is_teacher         :boolean
-#  last_name          :string
-#  linkedin           :string
-#  mastodon           :string
-#  meta_description   :text
-#  name               :string
-#  phone_mobile       :string
-#  phone_personal     :string
-#  phone_professional :string
-#  slug               :string
-#  summary            :text
-#  tenure             :boolean          default(FALSE)
-#  twitter            :string
-#  url                :string
-#  zipcode            :string
-#  created_at         :datetime         not null
-#  updated_at         :datetime         not null
-#  university_id      :uuid             not null, indexed
-#  user_id            :uuid             indexed
+#  id                    :uuid             not null, primary key
+#  address               :string
+#  biography             :text
+#  birthdate             :date
+#  city                  :string
+#  country               :string
+#  email                 :string
+#  first_name            :string
+#  gender                :integer
+#  habilitation          :boolean          default(FALSE)
+#  hal_person_identifier :string
+#  is_administration     :boolean
+#  is_alumnus            :boolean          default(FALSE)
+#  is_author             :boolean
+#  is_researcher         :boolean
+#  is_teacher            :boolean
+#  last_name             :string
+#  linkedin              :string
+#  mastodon              :string
+#  meta_description      :text
+#  name                  :string
+#  phone_mobile          :string
+#  phone_personal        :string
+#  phone_professional    :string
+#  slug                  :string
+#  summary               :text
+#  tenure                :boolean          default(FALSE)
+#  twitter               :string
+#  url                   :string
+#  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 cecc421cd2f061798f2cfc03a20c9fa04c20ef6b..0208d188ba5d6a699f8abaa9f912492b51344b31 100644
--- a/app/models/university/person/alumnus.rb
+++ b/app/models/university/person/alumnus.rb
@@ -2,47 +2,54 @@
 #
 # Table name: university_people
 #
-#  id                 :uuid             not null, primary key
-#  address            :string
-#  biography          :text
-#  birthdate          :date
-#  city               :string
-#  country            :string
-#  email              :string
-#  first_name         :string
-#  gender             :integer
-#  habilitation       :boolean          default(FALSE)
-#  is_administration  :boolean
-#  is_alumnus         :boolean          default(FALSE)
-#  is_author          :boolean
-#  is_researcher      :boolean
-#  is_teacher         :boolean
-#  last_name          :string
-#  linkedin           :string
-#  mastodon           :string
-#  meta_description   :text
-#  name               :string
-#  phone_mobile       :string
-#  phone_personal     :string
-#  phone_professional :string
-#  slug               :string
-#  summary            :text
-#  tenure             :boolean          default(FALSE)
-#  twitter            :string
-#  url                :string
-#  zipcode            :string
-#  created_at         :datetime         not null
-#  updated_at         :datetime         not null
-#  university_id      :uuid             not null, indexed
-#  user_id            :uuid             indexed
+#  id                    :uuid             not null, primary key
+#  address               :string
+#  biography             :text
+#  birthdate             :date
+#  city                  :string
+#  country               :string
+#  email                 :string
+#  first_name            :string
+#  gender                :integer
+#  habilitation          :boolean          default(FALSE)
+#  hal_person_identifier :string
+#  is_administration     :boolean
+#  is_alumnus            :boolean          default(FALSE)
+#  is_author             :boolean
+#  is_researcher         :boolean
+#  is_teacher            :boolean
+#  last_name             :string
+#  linkedin              :string
+#  mastodon              :string
+#  meta_description      :text
+#  name                  :string
+#  phone_mobile          :string
+#  phone_personal        :string
+#  phone_professional    :string
+#  slug                  :string
+#  summary               :text
+#  tenure                :boolean          default(FALSE)
+#  twitter               :string
+#  url                   :string
+#  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 ce3bad531fb8019ade2136e103b17bdf299d4128..496f29babdd7ef9353b0ae1bb7f7a99b36be63a0 100644
--- a/app/models/university/person/author.rb
+++ b/app/models/university/person/author.rb
@@ -2,47 +2,54 @@
 #
 # Table name: university_people
 #
-#  id                 :uuid             not null, primary key
-#  address            :string
-#  biography          :text
-#  birthdate          :date
-#  city               :string
-#  country            :string
-#  email              :string
-#  first_name         :string
-#  gender             :integer
-#  habilitation       :boolean          default(FALSE)
-#  is_administration  :boolean
-#  is_alumnus         :boolean          default(FALSE)
-#  is_author          :boolean
-#  is_researcher      :boolean
-#  is_teacher         :boolean
-#  last_name          :string
-#  linkedin           :string
-#  mastodon           :string
-#  meta_description   :text
-#  name               :string
-#  phone_mobile       :string
-#  phone_personal     :string
-#  phone_professional :string
-#  slug               :string
-#  summary            :text
-#  tenure             :boolean          default(FALSE)
-#  twitter            :string
-#  url                :string
-#  zipcode            :string
-#  created_at         :datetime         not null
-#  updated_at         :datetime         not null
-#  university_id      :uuid             not null, indexed
-#  user_id            :uuid             indexed
+#  id                    :uuid             not null, primary key
+#  address               :string
+#  biography             :text
+#  birthdate             :date
+#  city                  :string
+#  country               :string
+#  email                 :string
+#  first_name            :string
+#  gender                :integer
+#  habilitation          :boolean          default(FALSE)
+#  hal_person_identifier :string
+#  is_administration     :boolean
+#  is_alumnus            :boolean          default(FALSE)
+#  is_author             :boolean
+#  is_researcher         :boolean
+#  is_teacher            :boolean
+#  last_name             :string
+#  linkedin              :string
+#  mastodon              :string
+#  meta_description      :text
+#  name                  :string
+#  phone_mobile          :string
+#  phone_personal        :string
+#  phone_professional    :string
+#  slug                  :string
+#  summary               :text
+#  tenure                :boolean          default(FALSE)
+#  twitter               :string
+#  url                   :string
+#  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 aa2415f64931c64dab1857e259cff3b0d4e75ed1..72e633ff0e632f4732154e7071d8b29ced18bb28 100644
--- a/app/models/university/person/researcher.rb
+++ b/app/models/university/person/researcher.rb
@@ -2,47 +2,54 @@
 #
 # Table name: university_people
 #
-#  id                 :uuid             not null, primary key
-#  address            :string
-#  biography          :text
-#  birthdate          :date
-#  city               :string
-#  country            :string
-#  email              :string
-#  first_name         :string
-#  gender             :integer
-#  habilitation       :boolean          default(FALSE)
-#  is_administration  :boolean
-#  is_alumnus         :boolean          default(FALSE)
-#  is_author          :boolean
-#  is_researcher      :boolean
-#  is_teacher         :boolean
-#  last_name          :string
-#  linkedin           :string
-#  mastodon           :string
-#  meta_description   :text
-#  name               :string
-#  phone_mobile       :string
-#  phone_personal     :string
-#  phone_professional :string
-#  slug               :string
-#  summary            :text
-#  tenure             :boolean          default(FALSE)
-#  twitter            :string
-#  url                :string
-#  zipcode            :string
-#  created_at         :datetime         not null
-#  updated_at         :datetime         not null
-#  university_id      :uuid             not null, indexed
-#  user_id            :uuid             indexed
+#  id                    :uuid             not null, primary key
+#  address               :string
+#  biography             :text
+#  birthdate             :date
+#  city                  :string
+#  country               :string
+#  email                 :string
+#  first_name            :string
+#  gender                :integer
+#  habilitation          :boolean          default(FALSE)
+#  hal_person_identifier :string
+#  is_administration     :boolean
+#  is_alumnus            :boolean          default(FALSE)
+#  is_author             :boolean
+#  is_researcher         :boolean
+#  is_teacher            :boolean
+#  last_name             :string
+#  linkedin              :string
+#  mastodon              :string
+#  meta_description      :text
+#  name                  :string
+#  phone_mobile          :string
+#  phone_personal        :string
+#  phone_professional    :string
+#  slug                  :string
+#  summary               :text
+#  tenure                :boolean          default(FALSE)
+#  twitter               :string
+#  url                   :string
+#  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 66d8f2af271b6524093d1648bd4710861269a039..00f43010043f8e5c854091dbc9fcdc1c0e80bc84 100644
--- a/app/models/university/person/teacher.rb
+++ b/app/models/university/person/teacher.rb
@@ -2,47 +2,54 @@
 #
 # Table name: university_people
 #
-#  id                 :uuid             not null, primary key
-#  address            :string
-#  biography          :text
-#  birthdate          :date
-#  city               :string
-#  country            :string
-#  email              :string
-#  first_name         :string
-#  gender             :integer
-#  habilitation       :boolean          default(FALSE)
-#  is_administration  :boolean
-#  is_alumnus         :boolean          default(FALSE)
-#  is_author          :boolean
-#  is_researcher      :boolean
-#  is_teacher         :boolean
-#  last_name          :string
-#  linkedin           :string
-#  mastodon           :string
-#  meta_description   :text
-#  name               :string
-#  phone_mobile       :string
-#  phone_personal     :string
-#  phone_professional :string
-#  slug               :string
-#  summary            :text
-#  tenure             :boolean          default(FALSE)
-#  twitter            :string
-#  url                :string
-#  zipcode            :string
-#  created_at         :datetime         not null
-#  updated_at         :datetime         not null
-#  university_id      :uuid             not null, indexed
-#  user_id            :uuid             indexed
+#  id                    :uuid             not null, primary key
+#  address               :string
+#  biography             :text
+#  birthdate             :date
+#  city                  :string
+#  country               :string
+#  email                 :string
+#  first_name            :string
+#  gender                :integer
+#  habilitation          :boolean          default(FALSE)
+#  hal_person_identifier :string
+#  is_administration     :boolean
+#  is_alumnus            :boolean          default(FALSE)
+#  is_author             :boolean
+#  is_researcher         :boolean
+#  is_teacher            :boolean
+#  last_name             :string
+#  linkedin              :string
+#  mastodon              :string
+#  meta_description      :text
+#  name                  :string
+#  phone_mobile          :string
+#  phone_personal        :string
+#  phone_professional    :string
+#  slug                  :string
+#  summary               :text
+#  tenure                :boolean          default(FALSE)
+#  twitter               :string
+#  url                   :string
+#  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 0b6a4a000c386beb42461c34793b0375a121d29b..58fdb16dc840ff8d831b46ef71c9de8059e55777 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
+    # Original person
+    has_one :person, -> { where(original_id: nil) }, class_name: 'University::Person', dependent: :nullify
 
     delegate :experiences, to: :person
 
diff --git a/app/services/appstack/simple_navigation_renderer.rb b/app/services/appstack/simple_navigation_renderer.rb
index 65bb6925629ba9315633311bcc6a77c7deff6538..3e856e3e4c41ef6bc422af6cd6db7131141845a1 100644
--- a/app/services/appstack/simple_navigation_renderer.rb
+++ b/app/services/appstack/simple_navigation_renderer.rb
@@ -27,7 +27,7 @@ class Appstack::SimpleNavigationRenderer < SimpleNavigation::Renderer::Base
   def make_header(item)
     icon = item.send(:options)[:icon]
     header = '<li class="sidebar-header">'
-    header += "<i class=\"fas fa-#{ icon }\"></i>" if icon
+    header += "<i class=\"#{ icon }\"></i>" if icon
     header += item.name
     header += '</li>'
     header
@@ -38,7 +38,7 @@ class Appstack::SimpleNavigationRenderer < SimpleNavigation::Renderer::Base
     a = "<a href=\"#{ item.url }\" class=\"sidebar-link#{ item.selected? ? '' : ' collapsed' }\""
     a += " data-bs-target=\"##{ item.key }\" data-bs-toggle=\"collapse\"" if consider_sub_navigation?(item)
     a += ">"
-    a += "<i class=\"fas fa-#{ icon }\"></i>" if icon
+    a += "<i class=\"#{ icon }\"></i>" if icon
     a += "<span class=\"align-middle\">#{ item.name }</span></a>"
     a
   end
diff --git a/app/services/icon.rb b/app/services/icon.rb
index ab63f35e4bfdf5af4b97a3e8d2c9289eb7eb26e4..7ae224e339763bf2a9758f9d1b325ee5846b585c 100644
--- a/app/services/icon.rb
+++ b/app/services/icon.rb
@@ -1,32 +1,64 @@
 # used in menu items and in admin navigation
 class Icon
-  DASHBOARD = 'chart-line'
+  DASHBOARD = 'fas fa-chart-line'
 
-  COMMUNICATION_EXTRANET = 'project-diagram'
-  COMMUNICATION_WEBSITE = 'sitemap'
-  COMMUNICATION_WEBSITE_HOME = 'home'
-  COMMUNICATION_WEBSITE_POST = 'newspaper'
-  COMMUNICATION_WEBSITE_PAGE = 'file'
-  COMMUNICATION_WEBSITE_PAGES = 'sitemap'
-  COMMUNICATION_WEBSITE_MENUS = 'bars'
-  COMMUNICATION_WEBSITE_ANALYTICS = 'chart-pie'
-  COMMUNICATION_WEBSITE_PREVIEW_MOBILE = 'mobile-alt'
-  COMMUNICATION_WEBSITE_PREVIEW_TABLET = 'tablet-alt'
-  COMMUNICATION_WEBSITE_PREVIEW_DESKTOP = 'laptop'
+  COMMUNICATION_EXTRANET = 'fas fa-project-diagram'
+  COMMUNICATION_WEBSITE = 'fas fa-sitemap'
+  COMMUNICATION_WEBSITE_HOME = 'fas fa-home'
+  COMMUNICATION_WEBSITE_POST = 'fas fa-newspaper'
+  COMMUNICATION_WEBSITE_PAGE = 'fas fa-file'
+  COMMUNICATION_WEBSITE_PAGES = 'fas fa-sitemap'
+  COMMUNICATION_WEBSITE_MENUS = 'fas fa-bars'
+  COMMUNICATION_WEBSITE_ANALYTICS = 'fas fa-chart-pie'
+  COMMUNICATION_WEBSITE_PREVIEW_MOBILE = 'fas fa-mobile-alt'
+  COMMUNICATION_WEBSITE_PREVIEW_TABLET = 'fas fa-tablet-alt'
+  COMMUNICATION_WEBSITE_PREVIEW_DESKTOP = 'fas fa-laptop'
+  COMMUNICATION_WEBSITE_MENU_BLANK = 'fas fa-font'
+  COMMUNICATION_WEBSITE_MENU_URL = 'fas fa-globe'
+  COMMUNICATION_NEWSLETTERS = 'fas fa-envelope'
 
-  EDUCATION_DIPLOMA = 'graduation-cap'
-  EDUCATION_PROGRAM = 'chalkboard-teacher'
-  EDUCATION_SCHOOL = 'university'
-  EDUCATION_TEACHER = 'user-graduate'
+  EDUCATION_DIPLOMA = 'fas fa-graduation-cap'
+  EDUCATION_PROGRAM = 'fas fa-chalkboard-teacher'
+  EDUCATION_SCHOOL = 'fas fa-university'
+  EDUCATION_TEACHER = 'fas fa-user-graduate'
+  EDUCATION_RESOURCES = 'fas fa-laptop'
+  EDUCATION_FEEDBACKS = 'fas fa-comments'
 
-  RESEARCH_JOURNAL = 'newspaper'
-  RESEARCH_LABORATORY = 'flask'
-  RESEARCH_PUBLICATION = 'book'
-  RESEARCH_RESEARCHER = 'microscope'
-  RESEARCH_THESE = 'scroll'
+  RESEARCH_JOURNAL = 'fas fa-newspaper'
+  RESEARCH_LABORATORY = 'fas fa-flask'
+  RESEARCH_RESEARCHER = 'fas fa-microscope'
+  RESEARCH_PUBLICATION = 'fas fa-book'
+  RESEARCH_THESE = 'fas fa-scroll'
+  RESEARCH_WATCH = 'fas fa-eye'
 
-  UNIVERSITY_ORGANIZATION = 'building'
-  UNIVERSITY_PERSON = 'users'
-  UNIVERSITY_PERSON_ADMINISTRATORS = 'users-cog'
-  UNIVERSITY_PERSON_ALUMNUS = 'user-graduate'
+  ADMINISTRATION_CAMPUS = 'fas fa-map-marker-alt'
+  ADMINISTRATION_ADMISSIONS = 'fas fa-door-open'
+  ADMINISTRATION_INTERNSHIPS = 'fas fa-hands-helping'
+  ADMINISTRATION_STATISTICS = 'fas fa-chart-bar'
+  ADMINISTRATION_QUALITY = 'fas fa-tasks'
+
+  UNIVERSITY_ORGANIZATION = 'fas fa-building'
+  UNIVERSITY_PERSON = 'fas fa-users'
+  UNIVERSITY_PERSON_ADMINISTRATORS = 'fas fa-users-cog'
+  UNIVERSITY_PERSON_ALUMNUS = 'fas fa-user-graduate'
+
+  OSUNY_USER = 'fas fa-user'
+  
+  ADD = 'fas fa-plus'
+  ARROW_RIGHT = 'fas fa-arrow-right'
+  A11Y = 'fas fa-universal-access'
+  CHECK_OK = 'fas fa-check'
+  CHECK_KO = 'fas fa-times'
+  FOLDER_CLOSED = 'far fa-folder'
+  FOLDER_OPENED = 'far fa-folder-open'
+  FOLDER_CLOSED_FULL = 'fas fa-folder'
+  FOLDER_OPENED_FULL = 'fas fa-folder-open'
+  DRAG = 'fas fa-bars'
+  DELETE = 'fas fa-times'
+  FILE = 'fas fa-file'
+  FILTERS = 'fas fa-filter'
+  MOVE = 'fas fa-arrows-alt'
+  SETTINGS = 'fas fa-gear'
+  SORT = 'fas fa-sort'
+  WARNING = 'fas fa-exclamation-circle'
 end
diff --git a/app/views/admin/administration/qualiopi/evaluations/_list.html.erb b/app/views/admin/administration/qualiopi/evaluations/_list.html.erb
index 1d15f6796c7018ef9b52e8a7739ca05f3ec01cf6..376cd32fd783bfdbc8ed1c07f183bf4f717d1a51 100644
--- a/app/views/admin/administration/qualiopi/evaluations/_list.html.erb
+++ b/app/views/admin/administration/qualiopi/evaluations/_list.html.erb
@@ -25,9 +25,9 @@
             <% valid = !program.public_send("best_#{check}").blank? %>
             <td>
               <% if valid %>
-                <span class="fas fa-check text-success"></span>
+                <span class="<%= Icon::CHECK_OK %> text-success"></span>
               <% else %>
-                <span class="fas fa-times text-danger"></span>
+                <span class="<%= Icon::CHECK_KO %> text-danger"></span>
               <% end %>
             </td>
           <% end %>
diff --git a/app/views/admin/application/_dependencies.html.erb b/app/views/admin/application/_dependencies.html.erb
index b9aa06804d5bd5bd433980ac994ea4777059d4d9..8724d1fb9edcd9d8e533450c0557e59030f78c0a 100644
--- a/app/views/admin/application/_dependencies.html.erb
+++ b/app/views/admin/application/_dependencies.html.erb
@@ -2,7 +2,7 @@
 expanded ||= false
 %>
 <% if current_user.server_admin? %>
-<i  class="fas fa-gear text-muted"
+<i  class="<%= Icon::SETTINGS %> text-muted"
     data-bs-toggle="collapse"
     data-bs-target="#dependencies"
     aria-expanded="<%= expanded %>"
diff --git a/app/views/admin/application/_filters.html.erb b/app/views/admin/application/_filters.html.erb
index 5378f14976bac2f134b718abb4d9ac259dd67477..72d9a5cfa9d9ea998d5c992f42d14ef99dcabb61 100644
--- a/app/views/admin/application/_filters.html.erb
+++ b/app/views/admin/application/_filters.html.erb
@@ -12,7 +12,7 @@ filters.each { |filter| should_be_open = true if params.has_key?(filter[:scope_n
         role="button"
         aria-expanded="false"
         aria-controls="collapseFilters">
-      <i class="fas fa-filter"></i>
+      <i class="<%= Icon::FILTERS %>"></i>
       <%= t('filters.buttons.expand') %>
     </a>
   <% end %>
diff --git a/app/views/admin/application/_preview.html.erb b/app/views/admin/application/_preview.html.erb
index 621ee83f372b1055cb0863c26b0e446afcdabdf3..25e4e28ba6fad24a33752846a0069c308f9f7b75 100644
--- a/app/views/admin/application/_preview.html.erb
+++ b/app/views/admin/application/_preview.html.erb
@@ -8,15 +8,15 @@
         <h5 class="modal-title h4"><%= t 'preview.title' %></h5>
         <div class="btn-group m-auto" role="group" aria-label="Basic example">
           <button type="button" class="btn btn-primary preview__button" data-mode="mobile">
-            <i class="fas fa-<%= Icon::COMMUNICATION_WEBSITE_PREVIEW_MOBILE %>"></i>
+            <i class="<%= Icon::COMMUNICATION_WEBSITE_PREVIEW_MOBILE %>"></i>
             <%= t 'preview.mobile' %>
           </button>
           <button type="button" class="btn btn-light preview__button" data-mode="tablet">
-            <i class="fas fa-<%= Icon::COMMUNICATION_WEBSITE_PREVIEW_TABLET %>"></i>
+            <i class="<%= Icon::COMMUNICATION_WEBSITE_PREVIEW_TABLET %>"></i>
             <%= t 'preview.tablet' %>
           </button>
           <button type="button" class="btn btn-light preview__button" data-mode="desktop">
-            <i class="fas fa-<%= Icon::COMMUNICATION_WEBSITE_PREVIEW_DESKTOP %>"></i>
+            <i class="<%= Icon::COMMUNICATION_WEBSITE_PREVIEW_DESKTOP %>"></i>
             <%= t 'preview.desktop' %>
           </button>
         </div>
diff --git a/app/views/admin/application/a11y/_widget.html.erb b/app/views/admin/application/a11y/_widget.html.erb
index 6da09712b7044b74ed0fea3db2e4710d6b8e77aa..db26884e8fade959e33833d574f80b53d5ed1e35 100644
--- a/app/views/admin/application/a11y/_widget.html.erb
+++ b/app/views/admin/application/a11y/_widget.html.erb
@@ -1,6 +1,6 @@
 <%
 color = about.accessible? ? 'text-success' : 'text-danger'
-action = "<i class=\"fas fa-universal-access fa-2x float-end #{ color}\"></i>"
+action = "<i class=\"#{ Icon::A11Y } fa-2x float-end #{ color}\"></i>"
 %>
 <%= osuny_panel t('accessibility.label'), action: action do %>
   <% if about.accessibility_errors.any? %>
diff --git a/app/views/admin/application/i18n/_static.html.erb b/app/views/admin/application/i18n/_static.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..fa2ffb096321e9dcc5024e674a951095da44d952
--- /dev/null
+++ b/app/views/admin/application/i18n/_static.html.erb
@@ -0,0 +1 @@
+translation_key: <%= @about.static_translation_key %>
diff --git a/app/views/admin/application/i18n/_widget.html.erb b/app/views/admin/application/i18n/_widget.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..716cb26bd4dec9596b66ebe2b08cae08a2833487
--- /dev/null
+++ b/app/views/admin/application/i18n/_widget.html.erb
@@ -0,0 +1,17 @@
+<% if about.available_languages.many? %>
+  <%
+    route_args = about.respond_to?(:website)  ? [:admin, about.becomes(about.class.base_class)]
+                                              : [:show_in_language, :admin, about.becomes(about.class.base_class)]
+  %>
+  <%= osuny_panel t('internationalization.label') do %>
+    <ol class="list-unstyled">
+      <% about.available_languages.each do |language| %>
+        <%
+        label = t(language.iso_code, scope: :languages)
+        current = language.id == about.language_id
+        %>
+        <li><%= link_to_unless current, label, [*route_args, lang: language.iso_code] %></li>
+      <% end %>
+    </ol>
+  <% end %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/admin/application/property/_summernote_embeds.html.erb b/app/views/admin/application/property/_summernote_embeds.html.erb
index 22dac78fd23104c78bc46305abc352641ffac0a9..a714433bb7206bedbfd181746949d5b0a1852a95 100644
--- a/app/views/admin/application/property/_summernote_embeds.html.erb
+++ b/app/views/admin/application/property/_summernote_embeds.html.erb
@@ -7,7 +7,7 @@
           <% if embed.variable? %>
             <%= kamifusen_tag embed, class: 'img-fluid' %>
           <% else %>
-            <i class="fas fa-file p-3 fa-2x"></i>
+            <i class="<%= Icon::FILE %> p-3 fa-2x"></i>
           <% end %>
         </div>
       </div>
diff --git a/app/views/admin/application/property/_text.html.erb b/app/views/admin/application/property/_text.html.erb
index 9e05d1f266b8618588b8eb6c45059f7a93705b43..48fe15966b78b015fc890fd8cc27fa5eb18e6060 100644
--- a/app/views/admin/application/property/_text.html.erb
+++ b/app/views/admin/application/property/_text.html.erb
@@ -14,7 +14,7 @@ end
 <%= osuny_label title %>
 <p>
   <% if Static.blank?(value) %>
-    <i class="fa fa-exclamation-circle text-danger"></i>
+    <i class="<%= Icon::WARNING %> text-danger"></i>
     <%= t 'properties.text.missing' %>
   <% else %>
     <%= strip_tags(value).truncate(200).html_safe %>
diff --git a/app/views/admin/application/tree/_folder.html.erb b/app/views/admin/application/tree/_folder.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..6efc55efafa7695598897d07113e724dcfcd7773
--- /dev/null
+++ b/app/views/admin/application/tree/_folder.html.erb
@@ -0,0 +1,8 @@
+<span class="open_btn">
+  <i class="open_btn--without_children <%= Icon::FOLDER_CLOSED %>"></i>
+  <i class="open_btn--with_children <%= Icon::FOLDER_CLOSED_FULL %>"></i>
+</span>
+<span class="close_btn">
+  <i class="close_btn--without_children <%= Icon::FOLDER_OPENED %>"></i>
+  <i class="close_btn--with_children <%= Icon::FOLDER_OPENED_FULL %>"></i>
+</span>
\ No newline at end of file
diff --git a/app/views/admin/application/tree/_sort.html.erb b/app/views/admin/application/tree/_sort.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..0cb428a30bfa94937b08bbaceb7673dde0a7a7ab
--- /dev/null
+++ b/app/views/admin/application/tree/_sort.html.erb
@@ -0,0 +1,3 @@
+<span class="move_btn py-2 ps-2">
+  <i class="<%= Icon::SORT %>"></i>
+</span>
\ No newline at end of file
diff --git a/app/views/admin/communication/blocks/_list.html.erb b/app/views/admin/communication/blocks/_list.html.erb
index 856b5fdc9089a1da4942f664d80d35c5e4149908..346144498f2cd9fbbaf7b5115b575156020eda32 100644
--- a/app/views/admin/communication/blocks/_list.html.erb
+++ b/app/views/admin/communication/blocks/_list.html.erb
@@ -12,7 +12,7 @@ action += link_to t('admin.communication.blocks.add'),
           <% about.blocks.ordered.each do |block| %>
             <tr data-id="<%= block.id %>" class="<%= 'draft' unless block.published? %>">
               <% if can? :reorder, Communication::Block %>
-                <td class="ps-0 blocks__list__handle"><i class="fa fa-bars handle"></i></td>
+                <td class="ps-0 blocks__list__handle"><i class="<%= Icon::DRAG %> handle"></i></td>
               <% end %>
               <td class="blocks__list__name">
                 <%= block.to_s.truncate(50) %><br>
diff --git a/app/views/admin/communication/blocks/components/category/_edit.html.erb b/app/views/admin/communication/blocks/components/category/_edit.html.erb
index 48937b1750fa19448b8de5404f43570c3ae28db0..81468e51d217ba7c09c13fe351488a2cbd055ad3 100644
--- a/app/views/admin/communication/blocks/components/category/_edit.html.erb
+++ b/app/views/admin/communication/blocks/components/category/_edit.html.erb
@@ -1,6 +1,4 @@
-<%
-categories = collection_tree(@block.about&.website.categories)
-%>
+<% categories = collection_tree(@block.about&.website.categories.for_language(@block.language)) %>
 <label  class="form-label"
         :for="<%= dom_id.html_safe %>">
   <%= label %>
diff --git a/app/views/admin/communication/blocks/components/file/_edit.html.erb b/app/views/admin/communication/blocks/components/file/_edit.html.erb
index ad296973efa93cf54e6be4fd95c6ecc343d09035..1161795efd45f16a8dc6f776c79fcb8ddf87c665 100644
--- a/app/views/admin/communication/blocks/components/file/_edit.html.erb
+++ b/app/views/admin/communication/blocks/components/file/_edit.html.erb
@@ -19,7 +19,7 @@ remove = t 'admin.communication.blocks.components.file.input.remove'
     <p><b>{{ element.file.filename }}</b></p>
     <a  class="btn btn-sm text-danger"
         v-on:click="<%= model %>.<%= property %>={}">
-        <i class="fas fa-times"></i>
+        <i class="<%= Icon::DELETE %>"></i>
       <%= remove %>
     </a>
   </div>
diff --git a/app/views/admin/communication/blocks/components/image/_edit.html.erb b/app/views/admin/communication/blocks/components/image/_edit.html.erb
index 7a48179ec35d0ce7db0351289734be1fdcb09943..7691d20af29607b2f71806faa14b137f63fea9ef 100644
--- a/app/views/admin/communication/blocks/components/image/_edit.html.erb
+++ b/app/views/admin/communication/blocks/components/image/_edit.html.erb
@@ -20,7 +20,7 @@ remove = t 'admin.communication.blocks.components.image.input.remove'
   <a  class="btn btn-sm text-danger"
       v-on:click="<%= model %>.<%= property %>={}"
       v-if="<%= model %>.<%= property %>.id">
-      <i class="fas fa-times"></i>
+      <i class="<%= Icon::DELETE %>"></i>
     <%= remove %>
   </a>
 </div>
diff --git a/app/views/admin/communication/blocks/components/organization/_edit.html.erb b/app/views/admin/communication/blocks/components/organization/_edit.html.erb
index 5657ea94db25c75d1ed1dabb148f0328abbb6989..48fb8775666bb52de24429db995dcbdfaeca9622 100644
--- a/app/views/admin/communication/blocks/components/organization/_edit.html.erb
+++ b/app/views/admin/communication/blocks/components/organization/_edit.html.erb
@@ -1,6 +1,5 @@
-<%
-organizations = current_university.organizations.ordered
-%>
+<%# TODO: Traduire les organisations %>
+<% organizations = current_university.organizations.ordered %>
 <% unless label.blank? %>
   <label  class="form-label"
           :for="<%= dom_id.html_safe %>">
diff --git a/app/views/admin/communication/blocks/components/page/_edit.html.erb b/app/views/admin/communication/blocks/components/page/_edit.html.erb
index 3f922d8f45ae918c776f8142d5accd98e7945aff..fa13fccaeae883a19bc4c627ba95131c7eeee46e 100644
--- a/app/views/admin/communication/blocks/components/page/_edit.html.erb
+++ b/app/views/admin/communication/blocks/components/page/_edit.html.erb
@@ -1,5 +1,5 @@
 <%
-pages = collection_tree(@block.about&.website.pages)
+pages = collection_tree(@block.about&.website.pages.for_language(@block.language))
 %>
 <% unless label.blank? %>
   <label  class="form-label"
diff --git a/app/views/admin/communication/blocks/components/person/_edit.html.erb b/app/views/admin/communication/blocks/components/person/_edit.html.erb
index 33c7acb4adcb78e50925d78773f924057d5bbb95..ec05a677b040edbfae07cf6994270e53b44dab47 100644
--- a/app/views/admin/communication/blocks/components/person/_edit.html.erb
+++ b/app/views/admin/communication/blocks/components/person/_edit.html.erb
@@ -1,6 +1,4 @@
-<%
-people = current_university.people.ordered
-%>
+<% people = current_university.people.for_language(@block.language).ordered %>
 <% unless label.blank? %>
   <label  class="form-label"
           :for="<%= dom_id.html_safe %>">
diff --git a/app/views/admin/communication/blocks/components/post/_edit.html.erb b/app/views/admin/communication/blocks/components/post/_edit.html.erb
index e90073157dcdd06c29c5ac8e083e25f7adfe2809..594ad08b394a3748bfd3f4c81ccb19be12b60974 100644
--- a/app/views/admin/communication/blocks/components/post/_edit.html.erb
+++ b/app/views/admin/communication/blocks/components/post/_edit.html.erb
@@ -1,6 +1,4 @@
-<%
-posts = @block.about&.website.posts.ordered
-%>
+<% posts = @block.about&.website.posts.for_language(@block.language).ordered %>
 <label  class="form-label <%= 'visually-hidden' if label.blank? %>"
         :for="<%= dom_id.html_safe %>">
   <%= label %>
diff --git a/app/views/admin/communication/blocks/components/program/_edit.html.erb b/app/views/admin/communication/blocks/components/program/_edit.html.erb
index f3fcbd342401853547249cb3b97dd056318658e5..efb38295f9a51c4d5954f67722d33dd25c3c4ec9 100644
--- a/app/views/admin/communication/blocks/components/program/_edit.html.erb
+++ b/app/views/admin/communication/blocks/components/program/_edit.html.erb
@@ -1,6 +1,5 @@
-<%
-program = current_university.programs.ordered
-%>
+<%# TODO: Traduire les formations %>
+<% programs = current_university.programs.ordered %>
 <% if label.present? %>
 <label  class="form-label"
         :for="<%= dom_id.html_safe %>">
@@ -13,7 +12,7 @@ program = current_university.programs.ordered
   <% if placeholder %>
     <option value="" disabled><%= placeholder %></option>
   <% end %>
-  <% program.each do |program| %>
+  <% programs.each do |program| %>
     <option value="<%= program.id %>">
       <%= program %>
     </option>
diff --git a/app/views/admin/communication/blocks/templates/call_to_action/_edit.html.erb b/app/views/admin/communication/blocks/templates/call_to_action/_edit.html.erb
index ba5e9f19bba7a030b0b957c60759615011020c57..b97988ebbe1ce48196c1b787ea90e09449260c7b 100644
--- a/app/views/admin/communication/blocks/templates/call_to_action/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/call_to_action/_edit.html.erb
@@ -25,7 +25,7 @@
   <div v-for="(element, index) in data.elements" class="d-flex draggable-item <%= if_appstack 'list-group-item' %>">
     <div>
       <a class="btn ps-0 pt-0 dragHandle">
-        <i class="fa fa-bars handle"></i>
+        <i class="<%= Icon::DRAG %> handle"></i>
       </a>
     </div>
     <div class="flex-fill">
@@ -45,7 +45,7 @@
     <div class="text-end">
       <a  class="btn btn-sm text-danger"
           v-on:click="data.elements.splice(data.elements.indexOf(element), 1)">
-        <i class="fas fa-times"></i>
+        <i class="<%= Icon::DELETE %>"></i>
       </a>
     </div>
   </div>
diff --git a/app/views/admin/communication/blocks/templates/contact/_edit.html.erb b/app/views/admin/communication/blocks/templates/contact/_edit.html.erb
index 78408b9bf45f58ed50d11651e127a5591013c287..5619e739c9f045de7ba42e57cf56530e8d333ff4 100644
--- a/app/views/admin/communication/blocks/templates/contact/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/contact/_edit.html.erb
@@ -40,7 +40,7 @@
                     placeholder="<%= t '.phones.placeholder' %>"
                     v-model="data.phone_numbers[index]">
             <a class="btn text-danger" v-on:click="data.phone_numbers.splice(index, 1)">
-              <i class="fas fa-times"></i>
+              <i class="<%= Icon::DELETE %>"></i>
             </a>
           </div>
         </div>
@@ -61,7 +61,7 @@
                     placeholder="<%= t '.mails.placeholder' %>"
                     v-model="data.emails[index]">
             <a class="btn text-danger" v-on:click="data.emails.splice(index, 1)">
-              <i class="fas fa-times"></i>
+              <i class="<%= Icon::DELETE %>"></i>
             </a>
           </div>
         </div>
@@ -76,7 +76,7 @@
   <div v-for="(element, index) in data.elements" class="d-flex draggable-item <%= if_appstack 'list-group-item' %>">
     <div>
       <a class="btn ps-0 pt-0 dragHandle">
-        <i class="fa fa-bars handle"></i>
+        <i class="<%= Icon::DRAG %> handle"></i>
       </a>
     </div>
     <div class="flex-fill">
@@ -95,7 +95,7 @@
     <div>
       <a  class="btn text-danger position-absolute top-0 end-0"
           v-on:click="data.elements.splice(index, 1)">
-          <i class="fas fa-times"></i>
+          <i class="<%= Icon::DELETE %>"></i>
       </a>
     </div>
   </div>
diff --git a/app/views/admin/communication/blocks/templates/datatable/_edit.html.erb b/app/views/admin/communication/blocks/templates/datatable/_edit.html.erb
index 3656e86ffbeedd89b234d51454e802c17deee1fc..3c759cf3db758537c6b9aedcb20109e9041ac3d3 100644
--- a/app/views/admin/communication/blocks/templates/datatable/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/datatable/_edit.html.erb
@@ -19,14 +19,14 @@
                     v-model="data.columns[index]">
             <a  class="btn text-danger"
                 v-on:click="data.columns.splice(index, 1); data.elements.forEach(row => row.cells.splice(index, 1));">
-                <i class="fas fa-times"></i>
+                <i class="<%= Icon::DELETE %>"></i>
             </a>
           </div>
         </div>
         <div class="td">
           <a  class="btn btn-primary mt-n1"
               v-on:click="data.columns.push('')">
-              <i class="fas fa-plus"></i> colonne
+              <i class="<%= Icon::ADD %>"></i> colonne
           </a>
         </div>
       </div>
@@ -42,10 +42,10 @@
           <div class="d-flex">
             <a  class="btn text-danger"
                 v-on:click="data.elements.splice(index, 1)">
-                <i class="fas fa-times"></i>
+                <i class="<%= Icon::DELETE %>"></i>
             </a>
             <a class="btn dragHandle">
-              <i class="fa fa-bars handle"></i>
+              <i class="<%= Icon::DRAG %> handle"></i>
             </a>
           </div>
         </div>
@@ -54,7 +54,7 @@
   </div>
   <a  class="btn btn-primary"
       v-on:click="data.elements.push({cells: []})">
-      <i class="fas fa-plus"></i> ligne
+      <i class="<%= Icon::ADD %>"></i> ligne
   </a>
 </div>
 
diff --git a/app/views/admin/communication/blocks/templates/definitions/_edit.html.erb b/app/views/admin/communication/blocks/templates/definitions/_edit.html.erb
index 209859a3f6ffdfeea38d99fd5d555c0fb0dd18ce..577a6a16e063a1fa894d22e2f1f2650cc7ce5274 100644
--- a/app/views/admin/communication/blocks/templates/definitions/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/definitions/_edit.html.erb
@@ -8,7 +8,7 @@
   <div v-for="(element, index) in data.elements" class="d-flex draggable-item <%= if_appstack 'list-group-item' %>">
     <div>
       <a class="btn ps-0 pt-0 dragHandle">
-        <i class="fa fa-bars handle"></i>
+        <i class="<%= Icon::DRAG %> handle"></i>
       </a>
     </div>
     <div class="flex-fill">
@@ -25,7 +25,7 @@
       <a  class="btn btn-sm text-danger ms-3"
           v-on:click="data.elements.splice(data.elements.indexOf(element), 1)"
           title="<%= t '.remove_definition' %>">
-          <i class="fas fa-times"></i>
+          <i class="<%= Icon::DELETE %>"></i>
       </a>
     </div>
   </div>
diff --git a/app/views/admin/communication/blocks/templates/files/_edit.html.erb b/app/views/admin/communication/blocks/templates/files/_edit.html.erb
index 918d8db735d04eb8caacb4d4053bfad6f081dee3..0fe565559132d3f320b5cc1b3f030e5d40cf687b 100644
--- a/app/views/admin/communication/blocks/templates/files/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/files/_edit.html.erb
@@ -11,13 +11,13 @@
     <div class="card">
       <div class="card-header border-bottom">
         <a class="btn ps-0 pt-0 dragHandle">
-          <i class="fa fa-bars handle"></i>
+          <i class="<%= Icon::DRAG %> handle"></i>
         </a>
         <div class="float-end">
           <a  class="btn btn-sm text-danger"
               v-on:click="data.elements.splice(data.elements.indexOf(element), 1)"
               title="<%= t '.remove_file' %>">
-              <i class="fas fa-times"></i>
+              <i class="<%= Icon::DELETE %>"></i>
           </a>
         </div>
       </div>
diff --git a/app/views/admin/communication/blocks/templates/gallery/_edit.html.erb b/app/views/admin/communication/blocks/templates/gallery/_edit.html.erb
index 20a8b508ac8c1e5cd25a10cbb5b7c57036977616..9e77ce4f5cc31ede8c7154a39bb48d2ae6f692b5 100644
--- a/app/views/admin/communication/blocks/templates/gallery/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/gallery/_edit.html.erb
@@ -11,7 +11,7 @@
   <div v-for="(element, index) in data.elements" class="col-xxl-1 col-lg-2 col-4">
     <div class="card">
       <div class="card-header p-1 text-center">
-        <i class="fas fa-arrows-alt"></i>
+        <i class="<%= Icon::MOVE %>"></i>
       </div>
       <div v-if="element.image.id">
         <img :src="getImageUrl(element.image)" class="img-fluid" />
@@ -30,7 +30,7 @@
       <a  class="btn btn-sm text-danger mt-n4"
           v-on:click="data.elements.splice(data.elements.indexOf(element), 1)"
           title="<%= t 'admin.communication.blocks.templates.gallery.edit.remove_image' %>">
-          <i class="fas fa-times"></i>
+          <i class="<%= Icon::DELETE %>"></i>
       </a>
     </div>
     <div class="row pure__row--small">
diff --git a/app/views/admin/communication/blocks/templates/key_figures/_edit.html.erb b/app/views/admin/communication/blocks/templates/key_figures/_edit.html.erb
index 492cf2199e757e9970d327264bfc10d1778037fd..722deb0fc1a0c94bb0898be715251eae859a04bc 100644
--- a/app/views/admin/communication/blocks/templates/key_figures/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/key_figures/_edit.html.erb
@@ -10,7 +10,7 @@
   <div v-for="(element, index) in data.elements" class="d-flex draggable-item <%= if_appstack 'list-group-item' %>">
     <div>
       <a class="btn ps-0 dragHandle">
-        <i class="fa fa-bars handle"></i>
+        <i class="<%= Icon::DRAG %> handle"></i>
       </a>
     </div>
     <div class="flex-fill">
@@ -30,7 +30,7 @@
       <a  class="btn btn-sm text-danger ms-3"
           v-on:click="data.elements.splice(data.elements.indexOf(element), 1)"
           title="<%= t '.remove_key' %>">
-          <i class="fas fa-times"></i>
+          <i class="<%= Icon::DELETE %>"></i>
       </a>
     </div>
   </div>
diff --git a/app/views/admin/communication/blocks/templates/organization_chart/_edit.html.erb b/app/views/admin/communication/blocks/templates/organization_chart/_edit.html.erb
index e4ef15e319252aca51f10bb64abb1768588452bc..2262e66e71082619001c1e9e0c822ea8c2355de7 100644
--- a/app/views/admin/communication/blocks/templates/organization_chart/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/organization_chart/_edit.html.erb
@@ -16,7 +16,7 @@
     <div class="d-flex">
       <div>
         <a class="btn ps-0 pt-0 dragHandle" title="<%= t '.drag_title' %>">
-          <i class="fa fa-bars handle"></i>
+          <i class="<%= Icon::DRAG %> handle"></i>
         </a>
       </div>
       <div class="flex-fill">
@@ -33,7 +33,7 @@
         <a  class="btn btn-sm text-danger"
             v-on:click="data.elements.splice(data.elements.indexOf(element), 1)"
             title="<%= t '.delete_title' %>">
-            <i class="fas fa-times"></i>
+            <i class="<%= Icon::DELETE %>"></i>
         </a>
       </div>
     </div>
diff --git a/app/views/admin/communication/blocks/templates/pages/_edit.html.erb b/app/views/admin/communication/blocks/templates/pages/_edit.html.erb
index 5f305eb00677543b3134351492e0020f11ed1835..814d64830a317342fa6a93781fc9d9ecf1790de9 100644
--- a/app/views/admin/communication/blocks/templates/pages/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/pages/_edit.html.erb
@@ -1,5 +1,3 @@
-<% pages = collection_tree(@block.about&.website.pages) %>
-
 <%= block_component_edit :layout %>
 
 <div class="row pure__row--small">
@@ -21,7 +19,7 @@
           <div class="d-flex mb-n3">
             <div>
               <a class="btn ps-0 pt-0 dragHandle" title="Drag and drop">
-                <i class="fa fa-bars handle"></i>
+                <i class="<%= Icon::DRAG %> handle"></i>
               </a>
             </div>
             <div class="flex-fill">
@@ -31,7 +29,7 @@
               <a  class="btn btn-sm text-danger ms-3"
                   v-on:click="data.elements.splice(data.elements.indexOf(page), 1)"
                   title="Supprimer">
-                  <i class="fas fa-times"></i>
+                  <i class="<%= Icon::DELETE %>"></i>
               </a>
             </div>
           </div>
diff --git a/app/views/admin/communication/blocks/templates/partners/_edit.html.erb b/app/views/admin/communication/blocks/templates/partners/_edit.html.erb
index f6385d0273d0c831f4c31bc6b76c11211427a998..0447550d1c475bdcda96dd9c51e6125811b57eff 100644
--- a/app/views/admin/communication/blocks/templates/partners/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/partners/_edit.html.erb
@@ -13,7 +13,7 @@
     <div class="d-flex mb-n3">
       <div>
         <a class="btn ps-0 pt-0 partnerHandle">
-          <i class="fa fa-bars handle"></i>
+          <i class="<%= Icon::DRAG %> handle"></i>
         </a>
       </div>
       <div class="flex-fill">
@@ -34,7 +34,7 @@
         <a  class="btn btn-sm text-danger ms-3"
             v-on:click="data.elements.splice(data.elements.indexOf(element), 1)"
             title="<%= t '.remove_partner' %>">
-            <i class="fas fa-times"></i>
+            <i class="<%= Icon::DELETE %>"></i>
         </a>
       </div>
     </div>
diff --git a/app/views/admin/communication/blocks/templates/posts/_edit.html.erb b/app/views/admin/communication/blocks/templates/posts/_edit.html.erb
index 471f3108d4188179f02696eb3d67593ad8823049..2f4fd1964e3913fcbe8f73bdd80e585c995db30e 100644
--- a/app/views/admin/communication/blocks/templates/posts/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/posts/_edit.html.erb
@@ -1,5 +1,3 @@
-<% categories = collection_tree(@block.about&.website.categories) %>
-
 <%= block_component_edit :layout %>
 
 <div class="mb-3">
@@ -18,7 +16,7 @@
   <draggable :list="data.elements" handle=".dragHandle" class="<%= if_appstack 'list-group' %>">
     <div v-for="(element, index) in data.elements" class="d-flex draggable-item <%= if_appstack 'list-group-item' %>">
       <a class="btn ps-0 dragHandle" title="Drag and drop">
-        <i class="fa fa-bars handle"></i>
+        <i class="<%= Icon::DRAG %> handle"></i>
       </a>
       <div class="flex-fill">
         <%= block_component_edit :id, template: @element %>
@@ -26,7 +24,7 @@
       <a  class="btn text-danger ms-3"
           v-on:click="data.elements.splice(data.elements.indexOf(element), 1)"
           title="Delete">
-          <i class="fas fa-times"></i>
+          <i class="<%= Icon::DELETE %>"></i>
       </a>
     </div>
   </draggable>
diff --git a/app/views/admin/communication/blocks/templates/programs/_edit.html.erb b/app/views/admin/communication/blocks/templates/programs/_edit.html.erb
index a5103398fe0553988fc0e12f918c1a6ffb596e8e..6ad4d0836ded138a48e50149a61920a387169e0e 100644
--- a/app/views/admin/communication/blocks/templates/programs/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/programs/_edit.html.erb
@@ -1,9 +1,8 @@
-<% pages = collection_tree(@block.university.programs) %>
 <draggable :list="data.elements" handle=".dragHandle" class="mb-3 <%= if_appstack 'list-group' %>">
   <div v-for="(element, index) in data.elements" class="d-flex draggable-item <%= if_appstack 'list-group-item' %>">
     <div>
       <a class="btn ps-0 pt-0 dragHandle" title="Drag and drop">
-        <i class="fa fa-bars handle"></i>
+        <i class="<%= Icon::DRAG %> handle"></i>
       </a>
     </div>
     <div class="flex-fill mb-n3">
@@ -13,7 +12,7 @@
       <a  class="btn btn-sm text-danger ms-3"
           v-on:click="data.elements.splice(data.elements.indexOf(page), 1)"
           title="Supprimer">
-          <i class="fas fa-times"></i>
+          <i class="<%= Icon::DELETE %>"></i>
       </a>
     </div>
   </div>
diff --git a/app/views/admin/communication/blocks/templates/testimonials/_edit.html.erb b/app/views/admin/communication/blocks/templates/testimonials/_edit.html.erb
index b5ab2cbd81b527f6f3799096a96f5135815d9ca6..60f9eff62ac7f31cbd1b9d21abe0d9ac3867a002 100644
--- a/app/views/admin/communication/blocks/templates/testimonials/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/testimonials/_edit.html.erb
@@ -7,7 +7,7 @@
     <div class="d-flex mb-5">
       <div>
         <a class="btn ps-0 pt-0 dragHandle">
-          <i class="fa fa-bars handle"></i>
+          <i class="<%= Icon::DRAG %> handle"></i>
         </a>
       </div>
       <div class="flex-fill">
@@ -28,7 +28,7 @@
         <a  class="btn btn-sm text-danger ms-3"
             v-on:click="data.elements.splice(data.elements.indexOf(testimonial), 1)"
             title="<%= t '.remove_testimonial' %>">
-            <i class="fas fa-times"></i>
+            <i class="<%= Icon::DELETE %>"></i>
         </a>
       </div>
     </div>
diff --git a/app/views/admin/communication/blocks/templates/timeline/_edit.html.erb b/app/views/admin/communication/blocks/templates/timeline/_edit.html.erb
index c5bbe37d4fe44b18a21db4f7c65cf86d106ea27e..b640e234beddebd013408a9f2a188c971c485006 100644
--- a/app/views/admin/communication/blocks/templates/timeline/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/timeline/_edit.html.erb
@@ -5,7 +5,7 @@
   <div v-for="(element, index) in data.elements" class="d-flex draggable-item <%= if_appstack 'list-group-item' %>">
     <div>
       <a class="btn ps-0 pt-0 dragHandle">
-        <i class="fa fa-bars handle"></i>
+        <i class="<%= Icon::DRAG %> handle"></i>
       </a>
     </div>
     <div class="flex-fill">
@@ -22,7 +22,7 @@
       <a  class="btn btn-sm text-danger ms-3"
           v-on:click="data.elements.splice(data.elements.indexOf(element), 1)"
           title="<%= t '.remove_event' %>">
-          <i class="fas fa-times"></i>
+          <i class="<%= Icon::DELETE %>"></i>
       </a>
     </div>
   </div>
diff --git a/app/views/admin/communication/photo_imports/_selector.html.erb b/app/views/admin/communication/photo_imports/_selector.html.erb
index 5672edce072c7237eab9920602036369dda4c882..6486def70eaad4f95c67d350aa35791c68e60c8d 100644
--- a/app/views/admin/communication/photo_imports/_selector.html.erb
+++ b/app/views/admin/communication/photo_imports/_selector.html.erb
@@ -12,8 +12,8 @@ about_featured_image_credit = "##{about_identifier}_featured_image_credit"
 # fr, en...
 lang = about&.language&.iso_code if about.respond_to? :language
 # /admin/communication/photo_import.json?query=Page%20de%20test&per_page=12&page=1&lang=fr
-unsplash_path = admin_communication_unsplash_path(website_id: nil, format: :json)
-pexels_path = admin_communication_pexels_path(website_id: nil, format: :json)
+unsplash_path = admin_communication_unsplash_path(website_id: nil, lang: nil, format: :json)
+pexels_path = admin_communication_pexels_path(website_id: nil, lang: nil, format: :json)
 %>
 
 <div id="photo-import-app" v-cloak>
diff --git a/app/views/admin/communication/websites/_form.html.erb b/app/views/admin/communication/websites/_form.html.erb
index df4f1c7090df8f8986b1532e7c7f616c661ffb66..81f091f295b53880dff8aac3d8e12a80f99249a7 100644
--- a/app/views/admin/communication/websites/_form.html.erb
+++ b/app/views/admin/communication/websites/_form.html.erb
@@ -9,7 +9,7 @@
         <%= f.input :url %>
         <%= render 'admin/communication/abouts', f: f, i18n_key: 'activerecord.attributes.communication/website.about_' %>
         <%= f.association :languages, as: :check_boxes, required: true, wrapper_html: { class: "js-languages" } %>
-        <%= f.association :default_language, include_blank: t('simple_form.include_blanks.defaults.language'), input_html: { class: "js-default-language" } %>
+        <%= f.association :default_language, include_blank: t('simple_form.include_blanks.defaults.language'), input_html: (@website.persisted? ? { disabled: true } : { class: "js-default-language" }) %>
         <%= f.input :in_production %>
       <% end %>
     </div>
diff --git a/app/views/admin/communication/websites/_sidebar.html.erb b/app/views/admin/communication/websites/_sidebar.html.erb
index 0698c734baf612a77ad9a3b5ef49f1762dc809e9..e2205ef3eb35121a3d565933628ca3614643d482 100644
--- a/app/views/admin/communication/websites/_sidebar.html.erb
+++ b/app/views/admin/communication/websites/_sidebar.html.erb
@@ -1,6 +1,6 @@
 <div class="row mt-2 website__sidebar">
   <div class="col-lg-3 col-xl-2">
-    <div class="list-group list-group-flush" role="tablist">
+    <ul class="list-unstyled" role="tablist">
       <%
       navigation = [
         {
@@ -32,25 +32,38 @@
       navigation << {
         title: t('communication.website.analytics'),
         path: analytics_admin_communication_website_path(@website.id, website_id: nil),
-        icon:  Icon::COMMUNICATION_WEBSITE_ANALYTICS,
+        icon: Icon::COMMUNICATION_WEBSITE_ANALYTICS,
         ability: can?(:read, @website)
       } if @website.plausible_url.present?
 
       navigation.each_with_index do |object, index|
         next unless object[:ability]
-        active = index.zero?  ? object[:path] == request.path
+        active = index.zero?  ? controller_name == "websites" && action_name == "show"
                               : object[:path].in?(request.path)
       %>
-      <a class="list-group-item bg-transparent px-0 list-group-item-action<%= ' active' if active %>" href="<%= object[:path] %>">
-        <%= object[:title].html_safe %>
-        <span class="float-end">
-          <i class="fas fa-<%= object[:icon] %>"></i>
-        </span>
-      </a>
+        <li class="mb-3">
+          <a class="d-block py-1 <%= active ? 'text-black' : 'text-muted' %>" href="<%= object[:path] %>">
+            <i class="<%= object[:icon] %>" style="min-width: 30px"></i>
+            <%= object[:title].html_safe %>
+          </a>
+        </li>
       <% end %>
-    </div>
+    </ul>
+    <% if @website.languages.many? %>
+      <select class="form-control form-select mt-5"  onchange="if (this.value) window.location.href=this.value">
+        <% @website.languages.each do |language| %>
+          <%
+          label = I18n.t(language.iso_code, scope: :languages)
+          path = url_for request.params.merge(lang: language.iso_code)
+          selected = current_website_language == language
+          %>
+          <option value="<%= path %>"<% if selected %> selected="selected"<% end %>><%= label %></option>
+        <% end %>
+      </select>
+    <% end %>
   </div>
   <div class="col-lg-9 col-xl-10">
     <%= yield %>
   </div>
+
 </div>
diff --git a/app/views/admin/communication/websites/categories/_form.html.erb b/app/views/admin/communication/websites/categories/_form.html.erb
index ad011b1a673c7e86d5318712f76221a90320a2c2..0769ed9a26c14933cbdd34e5b2854e2cfb62e2e1 100644
--- a/app/views/admin/communication/websites/categories/_form.html.erb
+++ b/app/views/admin/communication/websites/categories/_form.html.erb
@@ -28,7 +28,7 @@
                         data: { source: '#communication_website_category_name' }
                       } %>
           <%= f.association :parent,
-                            collection: collection_tree(@website.categories, category),
+                            collection: collection_tree(@website.categories.for_language(current_website_language), category),
                             label_method: ->(p) { sanitize p[:label] },
                             value_method: ->(p) { p[:id] } %>
           <ul>
diff --git a/app/views/admin/communication/websites/categories/_treebranch.html.erb b/app/views/admin/communication/websites/categories/_treebranch.html.erb
index c8c9bcd3a736a80bdef143518fde4bd3d6efc081..b1a0cb7ef8b45f8b28add048009709af01ea671e 100644
--- a/app/views/admin/communication/websites/categories/_treebranch.html.erb
+++ b/app/views/admin/communication/websites/categories/_treebranch.html.erb
@@ -3,17 +3,10 @@
     <div class="d-flex align-items-center treeview__label border-bottom py-1">
       <%= link_to children_admin_communication_website_category_path(website_id: category.website.id, id: category.id),
                   class: 'js-treeview-openzone d-inline-block p-2 ps-0', style: 'width: 22px', remote: true do %>
-        <span class="open_btn">
-          <i class="open_btn--with_children fas fa-folder"></i>
-          <i class="open_btn--without_children far fa-folder"></i>
-        </span>
-        <span class="close_btn">
-          <i class="close_btn--with_children fas fa-folder-open"></i>
-          <i class="close_btn--without_children far fa-folder-open"></i>
-        </span>
+        <%= render 'admin/application/tree/folder' %>
       <% end %>
       <%= link_to category, admin_communication_website_category_path(website_id: category.website.id, id: category.id) %>
-      <span class="move_btn py-2 ps-2"><i class="fas fa-sort"></i></span>
+      <%= render 'admin/application/tree/sort' %>
       <%= link_to children_admin_communication_website_category_path(website_id: category.website.id, id: category.id),
                   class: 'js-treeview-openzone small ps-2', remote: true do %>
         <span class="open_text"><%= t 'folder.open' %></span>
diff --git a/app/views/admin/communication/websites/categories/show.html.erb b/app/views/admin/communication/websites/categories/show.html.erb
index 001de20aeb3bac00bb1a4f3dd9ef15ad0bfce01e..7bf64735680b08f7492a14f7bd5f42a65a8b7489 100644
--- a/app/views/admin/communication/websites/categories/show.html.erb
+++ b/app/views/admin/communication/websites/categories/show.html.erb
@@ -7,6 +7,7 @@
       <%= render 'admin/communication/blocks/list', about: @category %>
     </div>
     <div class="col-md-4">
+      <%= render 'admin/application/i18n/widget', about: @category %>
       <%= osuny_panel t('metadata') do %>
         <%= osuny_label Communication::Website::Category.human_attribute_name('slug') %>
         <p><%= @category.slug %></p>
@@ -38,7 +39,7 @@
     </div>
   </div>
   <% if @posts.total_count > 0 %>
-    <%= osuny_panel Communication::Website::Post.model_name.human(count: 2), 
+    <%= osuny_panel Communication::Website::Post.model_name.human(count: 2),
                     subtitle: "#{@posts.total_count} #{Communication::Website::Post.model_name.human(count: @posts.total_count).downcase }" do %>
       <%= render 'admin/communication/websites/posts/list', posts: @posts, hide_category: true %>
       <%= paginate @posts, theme: 'bootstrap-5' %>
diff --git a/app/views/admin/communication/websites/categories/static.html.erb b/app/views/admin/communication/websites/categories/static.html.erb
index 01c2e54a0f63171fba4f35c1020590bbcd2fb866..708772076a594461fea18fe3983d93105f52ca1e 100644
--- a/app/views/admin/communication/websites/categories/static.html.erb
+++ b/app/views/admin/communication/websites/categories/static.html.erb
@@ -12,6 +12,7 @@ children:
 <% end %>
 <% end %>
 position: <%= @about.position %>
+<%= render 'admin/application/i18n/static' %>
 <%= render 'admin/application/featured_image/static' %>
 <%= render 'admin/application/meta_description/static' %>
 <%= render 'admin/application/summary/static' %>
diff --git a/app/views/admin/communication/websites/configs/default_languages/static.html.erb b/app/views/admin/communication/websites/configs/default_languages/static.html.erb
index 8657a91ea7bbfbd54c57a9aba19d5484276d8b9d..f221d66584ca48a27f353b934d42306431f08bc8 100644
--- a/app/views/admin/communication/websites/configs/default_languages/static.html.erb
+++ b/app/views/admin/communication/websites/configs/default_languages/static.html.erb
@@ -6,4 +6,8 @@
   contentDir: content/<%= language.iso_code %>
   languageCode: <%= language.iso_code %>
   languageName: <%= language.name %>
+  permalinks:
+    <% Communication::Website::Permalink.config_in_website(@website, language).each do |key, value| %>
+    <%= key %>: <%= value %>
+    <% end %>
 <% end %>
\ No newline at end of file
diff --git a/app/views/admin/communication/websites/configs/default_permalinks/static.html.erb b/app/views/admin/communication/websites/configs/default_permalinks/static.html.erb
index 27ed0728ff2dc7ab5e9a9d2cc748e0d2445768cc..ec8135a858f5574a2e1383055dba4c1326282df5 100644
--- a/app/views/admin/communication/websites/configs/default_permalinks/static.html.erb
+++ b/app/views/admin/communication/websites/configs/default_permalinks/static.html.erb
@@ -1,5 +1,2 @@
 # DO NOT EDIT THIS FILE BY HAND - IT WILL BE OVERWRITTEN BY OSUNY
-
-<% Communication::Website::Permalink.config_in_website(@website).each do |key, value| %>
-<%= key %>: <%= value %>
-<% end %>
\ No newline at end of file
+# Moved to languages.yml
diff --git a/app/views/admin/communication/websites/configs/development_config/static.html.erb b/app/views/admin/communication/websites/configs/development_config/static.html.erb
index 4482ef3003872df6c592679af77d3c5ef9ebc50b..5c6b2e4d55496aea0509fdc62052162cae18d92f 100644
--- a/app/views/admin/communication/websites/configs/development_config/static.html.erb
+++ b/app/views/admin/communication/websites/configs/development_config/static.html.erb
@@ -1,8 +1,4 @@
 # DO NOT EDIT THIS FILE BY HAND - IT WILL BE OVERWRITTEN BY OSUNY
 
-<% if @website.languages.any? %>
 defaultContentLanguage: <%= @website.default_language.iso_code %>
-<% else %>
-defaultContentLanguage: fr
-<% end %>
 defaultContentLanguageInSubdir: <%= @website.languages.many? %>
\ No newline at end of file
diff --git a/app/views/admin/communication/websites/configs/production_config/static.html.erb b/app/views/admin/communication/websites/configs/production_config/static.html.erb
index f08667878c33e5b548e62ada8967fe0ae3a1a8b2..4e86dabd96882e994b0e09592409f225d2fcf978 100644
--- a/app/views/admin/communication/websites/configs/production_config/static.html.erb
+++ b/app/views/admin/communication/websites/configs/production_config/static.html.erb
@@ -3,9 +3,5 @@
 baseURL: <%= @website.url %>
 
 ## LANGUAGE
-<% if @website.languages.any? %>
 defaultContentLanguage: <%= @website.default_language.iso_code %>
-<% else %>
-defaultContentLanguage: fr
-<% end %>
 defaultContentLanguageInSubdir: <%= @website.languages.many? %>
diff --git a/app/views/admin/communication/websites/menus/items/_form.html.erb b/app/views/admin/communication/websites/menus/items/_form.html.erb
index b54a740d557edeeab4cb644cfcec6c27bfefe013..ffeacc2c0e3d9baab8f76a64850dd40a106c01e2 100644
--- a/app/views/admin/communication/websites/menus/items/_form.html.erb
+++ b/app/views/admin/communication/websites/menus/items/_form.html.erb
@@ -44,15 +44,15 @@
         <%
           if item.has_about?
             if item.kind_page?
-              about_collection = collection_tree @website.pages
+              about_collection = collection_tree @website.pages.for_language(current_website_language)
             elsif item.kind_diploma?
               about_collection = collection @website.education_diplomas
             elsif item.kind_program?
               about_collection = collection_tree @website.education_programs
             elsif item.kind_category?
-              about_collection = collection_tree @website.categories
+              about_collection = collection_tree @website.categories.for_language(current_website_language)
             elsif item.kind_post?
-              about_collection = collection @website.posts
+              about_collection = collection @website.posts.for_language(current_website_language)
             elsif item.kind_volume?
               about_collection = collection @website.research_volumes
             elsif item.kind_paper?
diff --git a/app/views/admin/communication/websites/menus/items/_treebranch.html.erb b/app/views/admin/communication/websites/menus/items/_treebranch.html.erb
index d986dfb7a333b424c8712302e56161777edff9a0..cd2b795c0114cdfa1c13ac8229c5e1827e42fe7f 100644
--- a/app/views/admin/communication/websites/menus/items/_treebranch.html.erb
+++ b/app/views/admin/communication/websites/menus/items/_treebranch.html.erb
@@ -3,18 +3,10 @@
     <div class="d-flex align-items-center treeview__label border-bottom p-1">
       <%= link_to children_admin_communication_website_menu_item_path(website_id: item.website.id, menu_id: item.menu.id, id: item.id),
                   class: 'js-treeview-openzone d-inline-block p-2 ps-0', style: 'width: 22px', remote: true do %>
-        <% icon_style = item.has_children? ? 'fas' : 'far' %>
-        <span class="open_btn">
-          <i class="open_btn--with_children fas fa-folder"></i>
-          <i class="open_btn--without_children far fa-folder"></i>
-        </span>
-        <span class="close_btn">
-          <i class="close_btn--with_children fas fa-folder-open"></i>
-          <i class="close_btn--without_children far fa-folder-open"></i>
-        </span>
+        <%= render 'admin/application/tree/folder' %>
       <% end %>
       <%= link_to_if can?(:read, item), item, admin_communication_website_menu_item_path(website_id: item.website.id, menu_id: item.menu.id, id: item.id) %>
-      <span class="move_btn py-2 ps-2"><i class="fas fa-sort"></i></span>
+      <%= render 'admin/application/tree/sort' %>
       <%= link_to children_admin_communication_website_menu_item_path(website_id: item.website.id, menu_id: item.menu.id, id: item.id),
                   class: 'js-treeview-openzone small ps-2', remote: true do %>
         <span class="open_text"><%= t 'folder.open' %></span>
diff --git a/app/views/admin/communication/websites/menus/items/kind_switch.js.erb b/app/views/admin/communication/websites/menus/items/kind_switch.js.erb
index 3e7d34011024a1dbf6e07ebd112f6ffa7e7f1971..b5db70accfe1ff1737a3865ca228f4ee16d158f1 100644
--- a/app/views/admin/communication/websites/menus/items/kind_switch.js.erb
+++ b/app/views/admin/communication/websites/menus/items/kind_switch.js.erb
@@ -15,7 +15,7 @@ function hideAbout() {
 <% elsif @kind == 'page' %>
     <%
     options = ['<option value="" label=" "></option>']
-    collection_tree(@website.pages).each do |page|
+    collection_tree(@website.pages.for_language(current_website_language)).each do |page|
         options << "<option value=\"#{page[:id]}\">#{page[:label]}</option>"
     end
     %>
@@ -40,7 +40,7 @@ function hideAbout() {
 <% elsif @kind == 'category' %>
     <%
         options = ['<option value="" label=" "></option>']
-        collection_tree(@website.categories).each do |category|
+        collection_tree(@website.categories.for_language(current_website_language)).each do |category|
             options << "<option value=\"#{category[:id]}\">#{category[:label]}</option>"
         end
     %>
@@ -48,7 +48,7 @@ function hideAbout() {
 <% elsif @kind == 'post' %>
     <%
     options = ['<option value="" label=" "></option>']
-    @website.posts.ordered.each do |post|
+    @website.posts.for_language(current_website_language).ordered.each do |post|
         options << "<option value=\"#{post.id}\">#{post.to_s}</option>"
     end
     %>
diff --git a/app/views/admin/communication/websites/menus/show.html.erb b/app/views/admin/communication/websites/menus/show.html.erb
index 0ac9ed5b1f63d9202130f675512eeb83abb03972..458f9088ac4e363cad0bbb4290ca1da564e29583 100644
--- a/app/views/admin/communication/websites/menus/show.html.erb
+++ b/app/views/admin/communication/websites/menus/show.html.erb
@@ -12,6 +12,7 @@
                   class: button_classes if can?(:create, Communication::Website::Menu::Item) %>
     </div>
     <div class="col-md-4">
+      <%= render 'admin/application/i18n/widget', about: @menu %>
       <%= osuny_panel t('metadata') do %>
         <%= osuny_label Communication::Website::Menu.human_attribute_name('identifier') %>
         <p><%= @menu.identifier %></p>
diff --git a/app/views/admin/communication/websites/pages/_form.html.erb b/app/views/admin/communication/websites/pages/_form.html.erb
index c9b287d2e7343350379e648cacb320c9b1d73ff1..bfc00a5c814e1fd9cc479e66980125431bd8b317 100644
--- a/app/views/admin/communication/websites/pages/_form.html.erb
+++ b/app/views/admin/communication/websites/pages/_form.html.erb
@@ -28,32 +28,14 @@ url = page.new_record?  ? admin_communication_website_pages_path
                         class: 'js-slug-input',
                         data: { source: '#communication_website_page_title' }
                       } unless page.is_home? %>
-          <% if @website.languages.many? %>
-            <%= f.input :language_id, collection: @website.languages, include_blank: false %>
-          <% elsif @website.languages.any? %>
-            <%= f.input :language_id, as: :hidden, input_html: { value: @website.languages.first.id }, wrapper: false %>
-          <% end %>
           <%= f.association :parent,
-                            collection: collection_tree(@website.pages, page),
+                            collection: collection_tree(@website.pages.for_language(current_website_language), page),
                             include_blank: false,
                             label_method: ->(p) { sanitize p[:label] },
                             value_method: ->(p) { p[:id] } unless page.is_home? %>
           <%= f.input :bodyclass if can?(:edit, @website) %>
           <%= f.input :full_width if page.editable_width? %>
         <% end %>
-      <% else %>
-        <% if @website.languages.many? %>
-          <div class="card flex-fill w-100">
-            <div class="card-header">
-              <h5 class="card-title mb-0"><%= t('metadata') %></h5>
-            </div>
-            <div class="card-body">
-              <%= f.input :language_id, collection: @website.languages, include_blank: false %>
-            </div>
-          </div>
-        <% elsif @website.languages.any? %>
-          <%= f.input :language_id, as: :hidden, input_html: { value: @website.languages.first.id }, wrapper: false %>
-        <% end %>
       <% end %>
       <%= render 'admin/application/featured_image/edit', about: @page, f: f %>
     </div>
diff --git a/app/views/admin/communication/websites/pages/_treebranch.html.erb b/app/views/admin/communication/websites/pages/_treebranch.html.erb
index e9df75c218279b0b40e15c268ab749205fb091fa..2a479388880de0b48d54d2cb20e0ec9ef16b91a8 100644
--- a/app/views/admin/communication/websites/pages/_treebranch.html.erb
+++ b/app/views/admin/communication/websites/pages/_treebranch.html.erb
@@ -3,21 +3,12 @@
     <div class="d-flex align-items-center treeview__label border-bottom p-1">
       <%= link_to children_admin_communication_website_page_path(website_id: page.website.id, id: page.id),
                   class: 'js-treeview-openzone d-inline-block p-2 ps-0', style: 'width: 22px', remote: true do %>
-        <span class="open_btn">
-          <i class="open_btn--with_children fas fa-folder"></i>
-          <i class="open_btn--without_children far fa-folder"></i>
-        </span>
-        <span class="close_btn">
-          <i class="close_btn--with_children fas fa-folder-open"></i>
-          <i class="close_btn--without_children far fa-folder-open"></i>
-        </span>
+        <%= render 'admin/application/tree/folder' %>
       <% end %>
       <%= link_to page,
                   admin_communication_website_page_path(website_id: page.website.id, id: page.id),
                   class: "leaf-title" %>
-      <% unless page.is_home? %>
-        <span class="move_btn py-2 ps-2"><i class="fas fa-sort"></i></span>
-      <% end %>
+      <%= render 'admin/application/tree/sort' unless page.is_home? %>
       <%= link_to children_admin_communication_website_page_path(website_id: page.website.id, id: page.id),
                   class: 'js-treeview-openzone small ps-2', remote: true do %>
         <span class="open_text"><%= t 'folder.open' %></span>
diff --git a/app/views/admin/communication/websites/pages/index.html.erb b/app/views/admin/communication/websites/pages/index.html.erb
index 917dda57254cb38b4f9611d2334b4a0a504dc1d7..70de04929cdef98d39e9ca1713b6779a3bf4bfd7 100644
--- a/app/views/admin/communication/websites/pages/index.html.erb
+++ b/app/views/admin/communication/websites/pages/index.html.erb
@@ -1,8 +1,8 @@
 <% content_for :title, t('admin.communication.website.pages.structure') %>
 
 <% content_for :title_right do %>
-  <%= @website.pages.count %>
-  <%= Communication::Website::Page.model_name.human(count: @website.pages.count).downcase %>
+  <%= @pages.size %>
+  <%= Communication::Website::Page.model_name.human(count: @pages.size).downcase %>
 <% end %>
 
 <%= render 'admin/communication/websites/sidebar' do %>
@@ -11,7 +11,7 @@
       <div class="d-flex align-items-center treeview__label border-bottom p-1">
         <div class="d-inline-block p-2 ps-0" style="width: 22px">
           <span class="close_btn text-primary">
-            <i class="close_btn--with_children fas fa-folder-open"></i>
+            <i class="close_btn--with_children <%= Icon::FOLDER_OPENED_FULL %>"></i>
           </span>
         </div>
         <%= link_to @homepage,
diff --git a/app/views/admin/communication/websites/pages/show.html.erb b/app/views/admin/communication/websites/pages/show.html.erb
index 86cc6372c5d1fb195e60abcec5cd3cd49045e7b4..4f5b7bb22130a619635ed068a61cf3fabf6d2f89 100644
--- a/app/views/admin/communication/websites/pages/show.html.erb
+++ b/app/views/admin/communication/websites/pages/show.html.erb
@@ -12,6 +12,7 @@
     </div>
     <div class="col-md-4">
       <%= render 'admin/application/a11y/widget', about: @page %>
+      <%= render 'admin/application/i18n/widget', about: @page %>
       <%= render 'admin/communication/websites/pages/show/metadata' %>
       <%= render 'admin/application/featured_image/show', about: @page %>
       <%= render 'admin/application/meta_description/show', about: @page %>
diff --git a/app/views/admin/communication/websites/pages/static.html.erb b/app/views/admin/communication/websites/pages/static.html.erb
index 84092f13b66caa1dddcf1cf8e7d6910d68b2974b..cddc7e2f86999a9060a5ebd8f2e19d2327c7a43d 100644
--- a/app/views/admin/communication/websites/pages/static.html.erb
+++ b/app/views/admin/communication/websites/pages/static.html.erb
@@ -15,11 +15,12 @@ has:
   teachers: <%= @website.has_teachers? %>
 <% end %>
 position: <%= @about.position %>
+<%= render 'admin/application/i18n/static' %>
 bodyclass: <%= @about.best_bodyclass %>
 <%= render 'admin/application/featured_image/static' %>
 <% if @about.children.published.any? %>
 children:
-<% 
+<%
 @about.children.published.ordered.each do |child|
   next unless child.is_listed_among_children?
 %>
diff --git a/app/views/admin/communication/websites/posts/_form.html.erb b/app/views/admin/communication/websites/posts/_form.html.erb
index fbdcea1d787919b561a77bca600387c51d2ac6a2..929f6f3b56e184797f531734f6d21d04a711d1a1 100644
--- a/app/views/admin/communication/websites/posts/_form.html.erb
+++ b/app/views/admin/communication/websites/posts/_form.html.erb
@@ -10,13 +10,13 @@
         <%= f.input :text, as: :summernote if post.text&.to_plain_text.present? %>
       <% end %>
       <div class="row pure__row--small">
-        <% if @website.categories.any? %>
+        <% if @categories.any? %>
           <div class="col-md-6">
             <%= osuny_panel t('activerecord.attributes.communication/website/post.categories') do %>
               <%= f.association :categories,
                                 label_text: false,
                                 as: :check_boxes,
-                                collection: collection_tree_for_checkboxes(@website.categories) if @website.categories.any? %>
+                                collection: collection_tree_for_checkboxes(@categories) %>
             <% end %>
           </div>
         <% end %>
@@ -38,21 +38,9 @@
           </div>
           <%= f.input :published_at, html5: true, as: :date %>
         <% end %>
-        <% if @website.languages.many? %>
-          <%= f.input :language_id, collection: @website.languages, include_blank: false %>
-        <% elsif @website.languages.any? %>
-          <%= f.input :language_id, as: :hidden, input_html: { value: @website.languages.first.id }, wrapper: false %>
-        <% end %>
-        <% if current_user.author? || current_user.contributor? %>
-          <%= f.input :author_id,
-                      as: :hidden,
-                      input_html: { value: current_user.person&.id },
-                      wrapper: false %>
-        <% else %>
-          <%= f.association :author,
-                            collection: current_university.people.ordered,
+        <%= f.association :author,
+                            collection: current_university.people.for_language(current_website_language).ordered,
                             label_method: :to_s_alphabetical %>
-        <% end %>
         <%= f.input :slug,
                     as: :string,
                     input_html: post.persisted? ? {} : {
diff --git a/app/views/admin/communication/websites/posts/show.html.erb b/app/views/admin/communication/websites/posts/show.html.erb
index fc2206f9ccc424b2061c25c429f5fa249b273afc..f1538493ae0a905a056cfa38baf380cb2ed78ab6 100644
--- a/app/views/admin/communication/websites/posts/show.html.erb
+++ b/app/views/admin/communication/websites/posts/show.html.erb
@@ -7,7 +7,8 @@
       <%= render 'admin/communication/blocks/list', about: @post %>
     </div>
     <div class="col-xl-4">
-      <% 
+      <%= render 'admin/application/i18n/widget', about: @post %>
+      <%
       action = ''
       action += link_to t('open'),
                         @post.url,
diff --git a/app/views/admin/communication/websites/posts/static.html.erb b/app/views/admin/communication/websites/posts/static.html.erb
index e89b9fd4cac31e15f5478a9a45f9680a84c91c29..4a3dd84b08f110417a3def554c4a6b007057a696 100644
--- a/app/views/admin/communication/websites/posts/static.html.erb
+++ b/app/views/admin/communication/websites/posts/static.html.erb
@@ -8,7 +8,7 @@ weight: 1
 <% end %>
 <% if @about.author %>
 authors:
-  - "<%= @about.author.slug %>"
+  - "<%= @about.translated_author.slug %>"
 <% end %>
 <% if @about.categories.any? %>
 categories:
@@ -16,6 +16,7 @@ categories:
   - "<%= category.slug_with_ancestors_slugs %>"
   <% end %>
 <% end %>
+<%= render 'admin/application/i18n/static' %>
 <%= render 'admin/application/featured_image/static' %>
 <%= render 'admin/application/meta_description/static' %>
 <%= render 'admin/application/summary/static' %>
diff --git a/app/views/admin/communication/websites/show/_pages.html.erb b/app/views/admin/communication/websites/show/_pages.html.erb
index fe28e0e4a8bdaeb31267d2c6d9b9dcf61337d2d2..daa78bf0cf6521d7989438aac1626cb5b171e336 100644
--- a/app/views/admin/communication/websites/show/_pages.html.erb
+++ b/app/views/admin/communication/websites/show/_pages.html.erb
@@ -3,7 +3,10 @@ action = ''
 action += link_to t('create'),
                   new_admin_communication_website_page_path(website_id: @website),
                   class: button_classes if can?(:create, Communication::Website::Page)
+subtitle = link_to t('communication.website.see_all', number: @all_pages.count), admin_communication_website_pages_path(website_id: @website)
 %>
-<%= osuny_panel t('communication.website.last_pages'), action: action do %>
+<%= osuny_panel t('communication.website.last_pages'),
+                subtitle: subtitle,
+                action: action do %>
   <%= render 'admin/communication/websites/pages/list', pages: @pages %>
-<% end %>
\ No newline at end of file
+<% end %>
diff --git a/app/views/admin/communication/websites/show/_posts.html.erb b/app/views/admin/communication/websites/show/_posts.html.erb
index 17ce17ce71837926dd0dea018fdef41aa198f6de..36b151a328c8a854e5853789a063af40ea904685 100644
--- a/app/views/admin/communication/websites/show/_posts.html.erb
+++ b/app/views/admin/communication/websites/show/_posts.html.erb
@@ -6,10 +6,13 @@ action += link_to t('communication.website.posts.new_curation'),
 action += link_to t('create'),
                   new_admin_communication_website_post_path(website_id: @website),
                   class: button_classes('ms-1') if can?(:create, Communication::Website::Post)
+subtitle = link_to t('communication.website.see_all', number: @all_posts.size), admin_communication_website_posts_path(website_id: @website)
 %>
-<%= osuny_panel t('communication.website.last_posts'), action: action do %>
+<%= osuny_panel t('communication.website.last_posts'),
+                subtitle: subtitle,
+                action: action do %>
   <%= render 'admin/communication/websites/posts/list',
               posts: @posts,
               hide_author: true,
               hide_category: true %>
-<% end %>
\ No newline at end of file
+<% end %>
diff --git a/app/views/admin/dashboard/index.html.erb b/app/views/admin/dashboard/index.html.erb
index ccf96fe7257548ea536c91cd57d4849e12631d1b..9e671e99c446d4af8cb45caf5bd89fae445086ff 100644
--- a/app/views/admin/dashboard/index.html.erb
+++ b/app/views/admin/dashboard/index.html.erb
@@ -43,7 +43,7 @@
       <% next unless can?(:read, website) %>
       <div class="<%= classes %>">
         <%= osuny_panel website, 
-                  action: "<i class=\"fas fa-#{ Icon::COMMUNICATION_WEBSITE }\"></i>" do %>
+                  action: "<i class=\"#{ Icon::COMMUNICATION_WEBSITE }\"></i>" do %>
           <p class="small"><%= website.url %></p>
           <%= link_to t('show'), [:admin, website], class: button_classes('stretched-link') %>
         <% end %>
@@ -59,7 +59,7 @@
       <% next unless can?(:read, extranet) %>
       <div class="<%= classes %>">
         <%= osuny_panel extranet, 
-                  action: "<i class=\"fas fa-#{ Icon::COMMUNICATION_EXTRANET }\"></i>" do %>
+                  action: "<i class=\"#{ Icon::COMMUNICATION_EXTRANET }\"></i>" do %>
           <p class="small"><%= extranet.url %></p>
           <%= link_to t('show'), [:admin, extranet], class: button_classes('stretched-link') %>
         <% end %>
@@ -75,7 +75,7 @@
       <% next unless can?(:read, journal) %>
       <div class="<%= classes %>">
         <%= osuny_panel journal, 
-            action: "<i class=\"fas fa-#{ Icon::RESEARCH_JOURNAL }\"></i>" do %>
+            action: "<i class=\"#{ Icon::RESEARCH_JOURNAL }\"></i>" do %>
           <%= link_to t('show'), [:admin, journal], class: button_classes('stretched-link') %>
         <% end %>
       </div>
diff --git a/app/views/admin/education/diplomas/_programs.html.erb b/app/views/admin/education/diplomas/_programs.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..b7797ebc549728663f1489e58f4841180682a6e9
--- /dev/null
+++ b/app/views/admin/education/diplomas/_programs.html.erb
@@ -0,0 +1,14 @@
+<%
+indentation = ' ' * depth
+%>
+<% programs.each do |program| %>
+<%= indentation %>- label: >
+<%= indentation %>    <%= program.to_s %>
+<%= indentation %>  path: >
+<%= indentation %>    <%= program.current_permalink_in_website(@website)&.path %>
+<% children = program.children.ordered %>
+<% if children.any? %>
+<%= indentation %>  children: 
+<%= render 'programs', programs: program.children.ordered, depth: depth + 4 %>
+<% end %>
+<% end %>
diff --git a/app/views/admin/education/diplomas/static.html.erb b/app/views/admin/education/diplomas/static.html.erb
index a8bc3a230cb7088a33aeefb42c5bf39399454653..75b42d0fb92aed18ecb0a2aff1647a281231b229 100644
--- a/app/views/admin/education/diplomas/static.html.erb
+++ b/app/views/admin/education/diplomas/static.html.erb
@@ -3,6 +3,8 @@ title: >
   <%= prepare_text_for_static @about.name %>
 <%= render 'admin/application/static/permalink' %>
 <%= render 'admin/application/static/design', full_width: true, toc_offcanvas: true %>
+programs:
+<%= render 'programs', programs: @programs, depth: 2 %>
 short_name: >
   <%= prepare_text_for_static @about.short_name %>
 <%= render 'admin/application/summary/static' %>
diff --git a/app/views/admin/education/programs/_involvement_fields.html.erb b/app/views/admin/education/programs/_involvement_fields.html.erb
index a849fd7b4cab1eed4af5e7f4dbda5a2e1992e71c..53b196e684855cb3bfb5d9c76dc93fbf3c13edb3 100644
--- a/app/views/admin/education/programs/_involvement_fields.html.erb
+++ b/app/views/admin/education/programs/_involvement_fields.html.erb
@@ -16,7 +16,7 @@
                   wrapper: false %>
     </div>
     <div class="col-md-1 text-end">
-      <%= link_to_remove_association  '<i class="fas fa-times"></i>'.html_safe,
+      <%= link_to_remove_association  "<i class=\"#{ Icon::DELETE }\"></i>".html_safe,
                                       f,
                                       class: 'btn btn-sm btn-danger' %>
     </div>
diff --git a/app/views/admin/education/programs/_treebranch.html.erb b/app/views/admin/education/programs/_treebranch.html.erb
index fa9062d6f734c8b5eea2d112e6f829456ae0b047..63f6b9de4be87704367bb43d1939f560fcfca98f 100644
--- a/app/views/admin/education/programs/_treebranch.html.erb
+++ b/app/views/admin/education/programs/_treebranch.html.erb
@@ -3,19 +3,12 @@
     <div class="d-flex align-items-center treeview__label border-bottom p-1">
       <%= link_to children_admin_education_program_path(id: program.id),
                   class: 'js-treeview-openzone d-inline-block p-2 ps-0', style: 'width: 22px', remote: true do %>
-        <span class="open_btn">
-          <i class="open_btn--with_children fas fa-folder"></i>
-          <i class="open_btn--without_children far fa-folder"></i>
-        </span>
-        <span class="close_btn">
-          <i class="close_btn--with_children fas fa-folder-open"></i>
-          <i class="close_btn--without_children far fa-folder-open"></i>
-        </span>
+        <%= render 'admin/application/tree/folder' %>
       <% end %>
       <%= link_to program,
                   admin_education_program_path(id: program.id),
                   class: 'leaf-title'  %>
-      <span class="move_btn py-2 ps-2"><i class="fas fa-sort"></i></span>
+      <%= render 'admin/application/tree/sort' %>
       <%= link_to children_admin_education_program_path(id: program.id),
                   class: 'js-treeview-openzone small ps-2', remote: true do %>
         <span class="open_text"><%= t 'folder.open' %></span>
diff --git a/app/views/admin/education/programs/roles/_involvement_fields.html.erb b/app/views/admin/education/programs/roles/_involvement_fields.html.erb
index 87b368af31776cb19491fafb7016abd173b4b051..cb51049ffb7ec1b79333563f0ff8ef16346c13b6 100644
--- a/app/views/admin/education/programs/roles/_involvement_fields.html.erb
+++ b/app/views/admin/education/programs/roles/_involvement_fields.html.erb
@@ -3,7 +3,7 @@
   <div class="card-body">
     <div class="row align-items-center">
       <div class="col-1">
-        <i class="fa fa-bars handle"></i>
+        <i class="<%= Icon::DRAG %> handle"></i>
       </div>
       <div class="col-9">
         <%= f.association :person,
@@ -14,7 +14,9 @@
                           required: true %>
       </div>
       <div class="col-2">
-        <%= link_to_remove_association '<i class="fas fa-times"></i>'.html_safe, f, class: 'btn btn-sm btn-danger' %>
+        <%= link_to_remove_association  "<i class=\"#{ Icon::DELETE }\"></i>".html_safe,
+                                        f,
+                                        class: 'btn btn-sm btn-danger' %>
       </div>
     </div>
   </div>
diff --git a/app/views/admin/education/programs/roles/_list.html.erb b/app/views/admin/education/programs/roles/_list.html.erb
index cbcffad6dd69c965bd9893bd4362c77e030f2f02..301353474b8db3f84c5e9dc1e4d33dc689c08c2b 100644
--- a/app/views/admin/education/programs/roles/_list.html.erb
+++ b/app/views/admin/education/programs/roles/_list.html.erb
@@ -15,7 +15,7 @@
         <% roles.each do |role| %>
           <tr data-id="<%= role.id %>">
             <% if can? :reorder, University::Role %>
-              <td><i class="fa fa-bars handle"></i></td>
+              <td><i class="<%= Icon::DRAG %> handle"></i></td>
             <% end %>
             <td class="ps-0">
               <%= link_to_if  can?(:read, role),
diff --git a/app/views/admin/education/programs/roles/show.html.erb b/app/views/admin/education/programs/roles/show.html.erb
index e19c7471b03bdedbcb7bafa164ca58fc0ab779cf..2214cfcdad476ffe268260cbb7addba160f4cc8c 100644
--- a/app/views/admin/education/programs/roles/show.html.erb
+++ b/app/views/admin/education/programs/roles/show.html.erb
@@ -16,7 +16,7 @@
         <% @involvements.each do |involvement| %>
           <tr data-id="<%= involvement.id %>">
             <% if can? :reorder, University::Role %>
-              <td><i class="fa fa-bars handle"></i></td>
+              <td><i class="<%= Icon::DRAG %> handle"></i></td>
             <% end %>
             <td class="ps-0">
               <%= link_to_if  can?(:read, involvement.person),
diff --git a/app/views/admin/education/schools/roles/_involvement_fields.html.erb b/app/views/admin/education/schools/roles/_involvement_fields.html.erb
index b90a7d1d0f57f1e0a2b60dcec2d5d7f4047ae590..b05e0c57c9c4cc3205a6ce7a2926ae0eaf6a4ef5 100644
--- a/app/views/admin/education/schools/roles/_involvement_fields.html.erb
+++ b/app/views/admin/education/schools/roles/_involvement_fields.html.erb
@@ -2,13 +2,15 @@
 <div class="nested-fields mb-2">
   <div class="row pure__row--small align-items-center">
     <div class="col-1">
-      <i class="fa fa-bars handle"></i>
+      <i class="<%= Icon::DRAG %> handle"></i>
     </div>
     <div class="col-9">
       <%= f.association :person, collection: @administration_people, label: false, include_blank: :translate, wrapper: false, required: true %>
     </div>
     <div class="col-2">
-      <%= link_to_remove_association '<i class="fas fa-times"></i>'.html_safe, f, class: 'btn btn-sm btn-danger' %>
+      <%= link_to_remove_association  "<i class=\"#{ Icon::DELETE }\"></i>".html_safe,
+                                      f,
+                                      class: 'btn btn-sm btn-danger' %>
     </div>
   </div>
   <%= f.hidden_field :position, data: { 'sortable-input': '' } %>
diff --git a/app/views/admin/education/schools/roles/_list.html.erb b/app/views/admin/education/schools/roles/_list.html.erb
index 142980c552c649022e773b436cde2524b6ab97b5..e4bf0ab0e8aa341696bab9011e862cc082e78692 100644
--- a/app/views/admin/education/schools/roles/_list.html.erb
+++ b/app/views/admin/education/schools/roles/_list.html.erb
@@ -15,7 +15,7 @@
         <% roles.each do |role| %>
           <tr data-id="<%= role.id %>">
             <% if can? :reorder, University::Role %>
-              <td><i class="fa fa-bars handle"></i></td>
+              <td><i class="<%= Icon::DRAG %> handle"></i></td>
             <% end %>
             <td class="ps-0">
               <%= link_to_if  can?(:read, role),
diff --git a/app/views/admin/education/schools/roles/show.html.erb b/app/views/admin/education/schools/roles/show.html.erb
index 9bf7f70942a59115fd2db2ce34c352361b87042a..5e7f3be9dbdd63dd19de6447e07ecd6138beea5d 100644
--- a/app/views/admin/education/schools/roles/show.html.erb
+++ b/app/views/admin/education/schools/roles/show.html.erb
@@ -16,7 +16,7 @@
         <% @involvements.each do |involvement| %>
           <tr data-id="<%= involvement.id %>">
             <% if can? :reorder, University::Person::Involvement %>
-              <td><i class="fa fa-bars handle"></i></td>
+              <td><i class="<%= Icon::DRAG %> handle"></i></td>
             <% end %>
             <td class="ps-0">
               <%= link_to_if  can?(:read, involvement.person),
diff --git a/app/views/admin/education/teachers/_involvement_fields.html.erb b/app/views/admin/education/teachers/_involvement_fields.html.erb
index 3cbb4f76aaeeb8f846aedfefc3f280373be52fd8..0419b3e64288d3b51438d89cf9c3c4df944ce251 100644
--- a/app/views/admin/education/teachers/_involvement_fields.html.erb
+++ b/app/views/admin/education/teachers/_involvement_fields.html.erb
@@ -22,9 +22,9 @@
                       wrapper: false %>
         </div>
         <div class="col-md-1 text-end">
-          <%= link_to_remove_association '<i class="fas fa-times"></i>'.html_safe,
-                                         f,
-                                         class: 'btn btn-sm btn-danger' %>
+          <%= link_to_remove_association  "<i class=\"#{ Icon::DELETE }\"></i>".html_safe,
+                                          f,
+                                          class: 'btn btn-sm btn-danger' %>
         </div>
       </div>
     </div>
diff --git a/app/views/admin/research/journals/show.html.erb b/app/views/admin/research/journals/show.html.erb
index 2d80a449b8f1f02d0734bdbf893fab4a566abf20..2998a0a57c424965d093373e723f6eaa8dc012fa 100644
--- a/app/views/admin/research/journals/show.html.erb
+++ b/app/views/admin/research/journals/show.html.erb
@@ -3,7 +3,7 @@
 <% content_for :title_right do %>
   <% if @journal.websites.any? %>
     <%= Communication::Website.model_name.human(count: 2) %>
-    <i class="fas fa-arrow-right small"></i>
+    <i class="<%= Icon::ARROW_RIGHT %> small"></i>
     <% @journal.websites.each do |website| %>
       <%= link_to website, [:admin, website] %><br>
     <% end %>
diff --git a/app/views/admin/research/journals/volumes/show.html.erb b/app/views/admin/research/journals/volumes/show.html.erb
index e2852145b862ab64356aa2110b3be41ba3d5d177..2a46b266508a6f6df8c1512195a56487326b621b 100644
--- a/app/views/admin/research/journals/volumes/show.html.erb
+++ b/app/views/admin/research/journals/volumes/show.html.erb
@@ -21,7 +21,7 @@
               <% @papers.each do |paper| %>
                 <tr data-id="<%= paper.id %>">
                   <% if can? :reorder, Research::Journal::Paper %>
-                    <td><i class="fa fa-bars handle"></i></td>
+                    <td><i class="<%= Icon::DRAG %> handle"></i></td>
                   <% end %>
                   <td>
                     <%= link_to paper,
diff --git a/app/views/admin/research/laboratories/axes/_list.html.erb b/app/views/admin/research/laboratories/axes/_list.html.erb
index 62dec193111ca105a30deec9489b7ffd7399190d..63ee86865438c46fa272a28eb82624884b781412 100644
--- a/app/views/admin/research/laboratories/axes/_list.html.erb
+++ b/app/views/admin/research/laboratories/axes/_list.html.erb
@@ -14,7 +14,7 @@
       <% axes.each do |axis| %>
         <tr data-id="<%= axis.id %>">
           <% if can? :reorder, Research::Laboratory::Axis %>
-            <td><i class="fa fa-bars handle"></i></td>
+            <td><i class="<%= Icon::DRAG %> handle"></i></td>
           <% end %>
           <td>
             <%= link_to axis, admin_research_laboratory_axis_path(laboratory_id: axis.laboratory, id: axis) %>
diff --git a/app/views/admin/university/alumni/cohorts/_cohort_fields.html.erb b/app/views/admin/university/alumni/cohorts/_cohort_fields.html.erb
index b66b73ca7f2bc0df238ae63e3524b2554432b9ce..a59c53d17cd15d985325efed82a58d2d9f6c7ee3 100644
--- a/app/views/admin/university/alumni/cohorts/_cohort_fields.html.erb
+++ b/app/views/admin/university/alumni/cohorts/_cohort_fields.html.erb
@@ -31,7 +31,7 @@
                             wrapper: false %>
         </div>
         <div class="col-md-1 text-end">
-          <%= link_to_remove_association '<i class="fas fa-times"></i>'.html_safe,
+          <%= link_to_remove_association "<i class=\"#{ Icon::DELETE }\"></i>".html_safe,
                                          f,
                                          class: 'btn btn-sm btn-danger' %>
         </div>
diff --git a/app/views/admin/university/alumni/experiences/_experience_fields.html.erb b/app/views/admin/university/alumni/experiences/_experience_fields.html.erb
index fc6a6e5c946a998956ae5cc0af91a397bd37cac3..820d47b06518502b08d69d3a148047b6e9d24450 100644
--- a/app/views/admin/university/alumni/experiences/_experience_fields.html.erb
+++ b/app/views/admin/university/alumni/experiences/_experience_fields.html.erb
@@ -35,9 +35,9 @@ hint = can?(:create, University::Organization) ?  t('university.person.experienc
           </div>
         </div>
         <div>
-          <%= link_to_remove_association '<i class="fas fa-times"></i>'.html_safe,
-                                        f,
-                                        class: 'btn btn-sm btn-danger' %>
+          <%= link_to_remove_association  "<i class=\"#{ Icon::DELETE }\"></i>".html_safe,
+                                          f,
+                                          class: 'btn btn-sm btn-danger' %>
         </div>
       </div>
     </div>
diff --git a/app/views/admin/university/people/_main_infos.html.erb b/app/views/admin/university/people/_main_infos.html.erb
index 477cfae60f5b96433e45674daff4d32113f0d988..46d22c8d98cf1750982300950766d813f789e847 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? %>
         <%= osuny_label University::Person.human_attribute_name('url') %>
@@ -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/admin/university/people/administrators/static.html.erb b/app/views/admin/university/people/administrators/static.html.erb
index 5902a5d2070e642c75f59da2e8dde750634b3fe0..286718254a26cc80a23b5781399c066f0eed9c4d 100644
--- a/app/views/admin/university/people/administrators/static.html.erb
+++ b/app/views/admin/university/people/administrators/static.html.erb
@@ -9,6 +9,7 @@ first_name: >
   <%= @about.first_name %>
 last_name: >
   <%= @about.last_name %>
+<%= render 'admin/application/i18n/static' %>
 <%= render 'admin/application/meta_description/static' %>
 <%= render 'admin/application/summary/static' %>
 ---
diff --git a/app/views/admin/university/people/authors/static.html.erb b/app/views/admin/university/people/authors/static.html.erb
index 5759db851d6dd42b4457ac640fd83fd71dc0decf..e44ee10cea02e1831dbb6bd7ae35b32ea5d56068 100644
--- a/app/views/admin/university/people/authors/static.html.erb
+++ b/app/views/admin/university/people/authors/static.html.erb
@@ -9,6 +9,7 @@ first_name: >
   <%= @about.first_name %>
 last_name: >
   <%= @about.last_name %>
+<%= render 'admin/application/i18n/static' %>
 <%= render 'admin/application/meta_description/static' %>
 <%= render 'admin/application/summary/static' %>
 ---
diff --git a/app/views/admin/university/people/researchers/static.html.erb b/app/views/admin/university/people/researchers/static.html.erb
index 84b7176db352c054aef825fec92ad25d2524ebc6..17d3bdd271425c0c335fb816b77831079caba48f 100644
--- a/app/views/admin/university/people/researchers/static.html.erb
+++ b/app/views/admin/university/people/researchers/static.html.erb
@@ -9,6 +9,7 @@ first_name: >
   <%= @about.first_name %>
 last_name: >
   <%= @about.last_name %>
+<%= render 'admin/application/i18n/static' %>
 <%= render 'admin/application/meta_description/static' %>
 <%= render 'admin/application/summary/static' %>
 ---
diff --git a/app/views/admin/university/people/static.html.erb b/app/views/admin/university/people/static.html.erb
index 47b1216eb8d9cc249b66b93d9b94371c3e3f4691..48da801d9bdcca6973bde9478edb075dadd419f7 100644
--- a/app/views/admin/university/people/static.html.erb
+++ b/app/views/admin/university/people/static.html.erb
@@ -5,6 +5,7 @@ title: >
 <%= render 'admin/application/meta_description/static' %>
 <%= render 'admin/application/summary/static' %>
 <%= render 'admin/application/static/design', full_width: true, toc_offcanvas: true %>
+<%= render 'admin/application/i18n/static' %>
 first_name: >
   <%= @about.first_name %>
 last_name: >
diff --git a/app/views/admin/university/people/teachers/static.html.erb b/app/views/admin/university/people/teachers/static.html.erb
index b2d59e7b9ad73586918679effb5c26e0074e8fd4..1c684711d25b7cf619ba56fcc491ae61f4c07d57 100644
--- a/app/views/admin/university/people/teachers/static.html.erb
+++ b/app/views/admin/university/people/teachers/static.html.erb
@@ -9,6 +9,7 @@ first_name: >
   <%= @about.first_name %>
 last_name: >
   <%= @about.last_name %>
+<%= render 'admin/application/i18n/static' %>
 <%= render 'admin/application/meta_description/static' %>
 <%= render 'admin/application/summary/static' %>
 ---
diff --git a/app/views/server/universities/_form.html.erb b/app/views/server/universities/_form.html.erb
index f55c3a5bbd839f8e9efd9c5ff3fb8246a559234c..792181f1a8112d203de4dabd53eaeb9223404965 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/app/views/server/universities/_sso_mapping.html.erb b/app/views/server/universities/_sso_mapping.html.erb
index 19eee43d893cc54a8c7a160f55db7d0b0e955828..a353ec6e660120312bb8c5de09b2b39f9b13e98a 100644
--- a/app/views/server/universities/_sso_mapping.html.erb
+++ b/app/views/server/universities/_sso_mapping.html.erb
@@ -21,7 +21,7 @@ end
         <div class="card">
           <div class="card-header d-flex justify-content-between">
             <div>
-              <i class="fas fa-arrows-alt dragHandle"></i>
+              <i class="<%= Icon::SORT %> dragHandle"></i>
               &nbsp;
               <a data-bs-toggle="collapse" :href="'#sso_mapping_collapse_' + index ">
                 {{index + 1}}. {{ field.sso_key }} -> {{ keys[field.internal_key]}}
@@ -30,7 +30,7 @@ end
             <a
               v-on:click="fields.splice(fields.indexOf(field), 1)"
               title="Remove field">
-              <i class="far fa-trash-alt"></i>
+              <i class="<%= Icon::DELETE %>"></i>
             </a>
           </div>
           <div class="card-body collapse pt-0" :id="'sso_mapping_collapse_' + index ">
diff --git a/config/admin_navigation.rb b/config/admin_navigation.rb
index 011dd83ee888d0e0166064b9e2f76ecf4fb01a1b..22567dfe2f69f25a7a63350b25e7bb914aa21edb 100644
--- a/config/admin_navigation.rb
+++ b/config/admin_navigation.rb
@@ -48,11 +48,11 @@ SimpleNavigation::Configuration.run do |navigation|
       primary.item :education,
                     'Ressources éducatives',
                     nil,
-                    { icon: 'laptop' }
+                    { icon: Icon::EDUCATION_RESOURCES }
       primary.item :education,
                     'Feedbacks',
                     nil,
-                    { icon: 'comments' }
+                    { icon: Icon::EDUCATION_FEEDBACKS }
     end
 
     if can?(:read, Research::Journal) || can?(:read, Research::Publication) || can?(:read, Research::Laboratory)
@@ -82,7 +82,7 @@ SimpleNavigation::Configuration.run do |navigation|
       primary.item :research_watch,
                     'Veille',
                     nil,
-                    { icon: 'eye' }
+                    { icon: Icon::RESEARCH_WATCH }
     end
 
     if can?(:read, Communication::Website)
@@ -100,7 +100,7 @@ SimpleNavigation::Configuration.run do |navigation|
       primary.item :communication_newsletters,
                     'Lettres d\'information',
                     nil,
-                    { icon: 'envelope' }
+                    { icon: Icon::COMMUNICATION_NEWSLETTERS }
     end
 
     if can?(:read, Administration::Qualiopi::Criterion)
@@ -111,34 +111,34 @@ SimpleNavigation::Configuration.run do |navigation|
       primary.item :administration_campus,
                     'Campus',
                     nil,
-                    { icon: 'map-marker-alt' }
+                    { icon: Icon::ADMINISTRATION_CAMPUS }
       primary.item :administration_admissions,
                     'Admissions',
                     nil,
-                    { icon: 'door-open' }
+                    { icon: Icon::ADMINISTRATION_ADMISSIONS }
       primary.item :administration_internship,
                     'Stages',
                     nil,
-                    { icon: 'hands-helping' }
+                    { icon: Icon::ADMINISTRATION_INTERNSHIPS }
       primary.item :administration_statistics,
                     'Statistiques',
                     nil,
-                    { icon: 'chart-bar' }
+                    { icon: Icon::ADMINISTRATION_STATISTICS }
       primary.item :administration_qualiopi,
                     'Qualité',
                     admin_administration_qualiopi_criterions_path,
-                    { icon: 'tasks' } if can?(:read, Administration::Qualiopi::Criterion)
+                    { icon: Icon::ADMINISTRATION_QUALITY } if can?(:read, Administration::Qualiopi::Criterion)
     end
 
     if can?(:read, User)
-      primary.item :administration,
+      primary.item :osuny,
                     'Osuny',
                     nil,
                     { kind: :header }
-      primary.item :administration_users,
+      primary.item :osuny_users,
                     User.model_name.human(count: 2),
                     admin_users_path,
-                    { icon: 'user' } if can?(:read, User)
+                    { icon: Icon::OSUNY_USER } if can?(:read, User)
     end
   end
 end
diff --git a/config/locales/communication/en.yml b/config/locales/communication/en.yml
index 5882e7ef89c8d4438d974a49406d52d7dec0b627..8d740863212855a6cc6bdd7048667b37ee12d936 100644
--- a/config/locales/communication/en.yml
+++ b/config/locales/communication/en.yml
@@ -97,9 +97,6 @@ en:
       communication/website/menu:
         identifier: Identifier
         items: Items
-        legal: Legal
-        primary: Main menu
-        social: Social
         title: Title
       communication/website/menu/item:
         about: Target
@@ -148,6 +145,7 @@ en:
         communication/website:
           attributes:
             languages:
+              must_include_default: must include at least the default language
               too_short: must include at least one
   admin:
     communication:
@@ -291,6 +289,8 @@ en:
             edit:
               add_definition: Add definition
               remove_definition: Delete definition
+              description:
+                label: Description
               element:
                 title:
                   label: Title
@@ -567,6 +567,11 @@ en:
         pending: Import in progress
       last_pages: Last pages
       last_posts: Last posts
+      menus:
+        default_title:
+          legal: Legal
+          primary: Main menu
+          social: Social
       pages:
         defaults:
           accessibility:
@@ -681,7 +686,10 @@ en:
       communication_website:
         git_branch: 'If blank, default branch will be used'
         git_endpoint: 'If blank, default will be used (https://github.com or https://gitlab.com/api/v4)'
-        languages: 'If you select at least one language the website will be considered as possibly multilingual, and therefore all urls will be prefixed with the language (/fr, /en)'
+        languages: 'If you select one language the website urls will not be prefixed. If you select more than one language the website will then be considered as multilingual, and therefore all urls will be prefixed with the language (/fr, /en)'
       communication_website_page:
         breadcrumb_title: If the field is empty, page title will be used in breadcrumbs.
         full_width: On large screens, a full width page uses all available space for the content. This is good for landing pages, or to make them spectacular. If the page is not full width, the content column will be smaller to make reading easier. The unused space might be used for a table of contents.
+        text: This field is deprecated, content will not be displayed on the website.
+      communication_website_post:
+        text: This field is deprecated, content will not be displayed on the website.
diff --git a/config/locales/communication/fr.yml b/config/locales/communication/fr.yml
index 06742087e3317c21c41e4d2dd07babe81ad45f54..ac35e8f8bc6e65eb53f9662e9d20a97f17637953 100644
--- a/config/locales/communication/fr.yml
+++ b/config/locales/communication/fr.yml
@@ -97,9 +97,6 @@ fr:
       communication/website/menu:
         identifier: Identifiant
         items: Éléments
-        legal: Informations légales
-        primary: Menu principal
-        social: Liens sociaux
         title: Titre
       communication/website/menu/item:
         about: Cible
@@ -148,6 +145,7 @@ fr:
         communication/website:
           attributes:
             languages:
+              must_include_default: doivent contenir la langue par défaut
               too_short: doivent en comporter une minimum
   admin:
     communication:
@@ -569,6 +567,11 @@ fr:
         pending: Import en cours
       last_pages: Dernières pages
       last_posts: Dernières actualités
+      menus:
+        default_title:
+          legal: Informations légales
+          primary: Menu principal
+          social: Liens sociaux
       pages:
         defaults:
           accessibility:
@@ -683,7 +686,7 @@ fr:
       communication_website:
         git_branch: 'Laisser vide pour la branche par défaut'
         git_endpoint: 'Laisser vide pour les valeurs par défaut (https://github.com ou https://gitlab.com/api/v4)'
-        languages: 'Si vous sélectionnez au moins une langue le site sera considéré comme possiblement multilingue et donc toutes les urls seront préfixées avec la langue (/fr, /en)'
+        languages: 'Si vous sélectionnez une seule langue les urls ne seront pas préfixées. Si vous en sélectionnez plusieurs le site sera considéré comme multilingue et donc toutes les urls seront préfixées avec la langue (/fr, /en)'
       communication_website_page:
         breadcrumb_title: Si ce champ est vide le titre de la page sera utilisé dans le fil d'Ariane.
         full_width: Sur de grands écrans, la page en pleine largeur utilisera tout l'espace disponible, ce qui est pertinent pour événementialiser une page. Si la page n'est pas en pleine largeur, l'espace dédié au contenu sera réduit pour faciliter la lecture, et l'espace libre pourra être utilisé pour une table des matières facilitant la navigation.
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 318ac19cf9f1b3422ccfe68abcec04bd4b6c31df..879f9c7eceb75483c1007e8d02d2cece586d8948 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -208,6 +208,8 @@ en:
     still_pending: "Your import has not been processed yet. You will receive an email as soon as it's ready."
   import_btn: Import
   inactivity_alert: "It seems you have been away a little bit too long. Could you please retry?"
+  internationalization:
+    label: Internationalization
   languages:
     en: English
     fr: French
diff --git a/config/locales/extranet/en.yml b/config/locales/extranet/en.yml
index c56a2abc9b31c5e82bd65cf2ff1b10247656b099..f3766ca4dad4a61a8234be50bb186c114372bb09 100644
--- a/config/locales/extranet/en.yml
+++ b/config/locales/extranet/en.yml
@@ -12,6 +12,7 @@ en:
       email_not_allowed_with_contact: is not authorized to access this extranet. Contact %{contact} for more information.
     experiences:
       new: Add experience
+      search_organization: Search by name or SIREN
       title: Path
     home:
       recent_cohorts: Recent cohorts
@@ -22,5 +23,6 @@ en:
     osuny_html:
       <a href="https://www.osuny.org" target="_blank" rel="noreferer">Crafted with ♥ and with Osuny</a>
     personal_data:
+      edit: Edit
       title: My personal data
       updated: Your personal data has been updated!
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 28b2136d766f093d50d237587c102e1fea8fe7c6..d36184e903da04a1c36ff32de7392afd59208990 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -208,6 +208,8 @@ fr:
     still_pending: "Votre import est toujours en cours de traitement. Vous recevrez un email dès qu'il est prêt."
   import_btn: Importer
   inactivity_alert: "Il semble que vous soyez resté sur la page un peu trop longtemps. Pouvez-vous ré-essayer ?"
+  internationalization:
+    label: Internationalisation
   languages:
     en: Anglais
     fr: Français
diff --git a/config/locales/university/en.yml b/config/locales/university/en.yml
index 54a67b638b0ea7df16d0557d342d4cb94ec5987c..63811528f2f16b080c2d7961c26802d58539688f 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 5cc0997b8023cfd1f049992035d8caf9b54d68d3..30f56db57161d30fc3362755cfa2a06c1f73f87e 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/communication.rb b/config/routes/admin/communication.rb
index 6e5ee1727dd78f239763d03da4c2c920ec243a43..c8506d61f3e3abbc471c1e87c34e80700e3d238e 100644
--- a/config/routes/admin/communication.rb
+++ b/config/routes/admin/communication.rb
@@ -5,12 +5,14 @@ namespace :communication do
   end
   resources :websites do
     member do
-      get :import
-      post :import
-      get :style
-      get :analytics
+      scope '/:lang' do
+        get :import
+        post :import
+        get :style
+        get :analytics
+      end
     end
-    resources :pages, controller: 'websites/pages' do
+    resources :pages, controller: 'websites/pages', path: '/:lang/pages' do
       collection do
         post :reorder
       end
@@ -18,10 +20,11 @@ namespace :communication do
         get :children
         get :static
         get :preview
+        get "translate" => "websites/pages#translate", as: :translate
         post :duplicate
       end
     end
-    resources :categories, controller: 'websites/categories' do
+    resources :categories, controller: 'websites/categories', path: '/:lang/categories' do
       collection do
         post :reorder
       end
@@ -30,20 +33,18 @@ namespace :communication do
         get :static
       end
     end
-    resources :authors, controller: 'websites/authors', only: [:index, :show]
-    resources :posts, controller: 'websites/posts' do
-      post :publish, on: :collection
+    resources :authors, controller: 'websites/authors', path: '/:lang/authors', only: [:index, :show]
+    resources :posts, controller: 'websites/posts', path: '/:lang/posts' do
+      collection do
+        resources :curations, as: :post_curations, controller: 'websites/posts/curations', only: [:new, :create]
+        post :publish
+      end
       member do
         get :static
         get :preview
       end
     end
-    resources :curations,
-              path: 'posts/curations',
-              as: :post_curations,
-              controller: 'websites/posts/curations',
-              only: [:new, :create]
-    resources :menus, controller: 'websites/menus' do
+    resources :menus, controller: 'websites/menus', path: '/:lang/menus' do
       resources :items, controller: 'websites/menus/items', except: :index do
         collection do
           get :kind_switch
diff --git a/config/routes/admin/university.rb b/config/routes/admin/university.rb
index 9f4701b020e53948e53488e8dc791f93254913a3..5da1efdf42f07b3679cd5b7331f9e13b7f0c4bff 100644
--- a/config/routes/admin/university.rb
+++ b/config/routes/admin/university.rb
@@ -22,6 +22,7 @@ namespace :university do
   resources :people do
     member do
       get :static
+      get "/translations/:lang" => "people#in_language", as: :show_in_language
     end
   end
   resources :organizations do
diff --git a/db/migrate/20230113160749_add_i18n_infos_to_communication_pages.rb b/db/migrate/20230113160749_add_i18n_infos_to_communication_pages.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2315d1f17e0749496d4de5cdd99e133f00e8f7fd
--- /dev/null
+++ b/db/migrate/20230113160749_add_i18n_infos_to_communication_pages.rb
@@ -0,0 +1,13 @@
+class AddI18nInfosToCommunicationPages < ActiveRecord::Migration[7.0]
+  # communication_website_pages already have language_id
+  def up
+    add_reference :communication_website_pages, :original, foreign_key: {to_table: :communication_website_pages}, type: :uuid
+    Communication::Website::Page.where(language_id: nil).each do |page|
+      page.update_column(:language_id, page.website.default_language_id)
+    end
+  end
+
+  def down
+    remove_reference :communication_website_pages, :original
+  end
+end
diff --git a/db/migrate/20230113164208_add_null_false_to_communication_website_page_language_id.rb b/db/migrate/20230113164208_add_null_false_to_communication_website_page_language_id.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2562195795bd4c66abb2cdbaea56c92a4707307a
--- /dev/null
+++ b/db/migrate/20230113164208_add_null_false_to_communication_website_page_language_id.rb
@@ -0,0 +1,5 @@
+class AddNullFalseToCommunicationWebsitePageLanguageId < ActiveRecord::Migration[7.0]
+  def change
+    change_column :communication_website_pages, :language_id, :uuid, null: false
+  end
+end
diff --git a/db/migrate/20230123150502_set_language_on_communication_website_posts.rb b/db/migrate/20230123150502_set_language_on_communication_website_posts.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ebee8b281180a0c6cf44da6a16590cef7b1082cf
--- /dev/null
+++ b/db/migrate/20230123150502_set_language_on_communication_website_posts.rb
@@ -0,0 +1,11 @@
+class SetLanguageOnCommunicationWebsitePosts < ActiveRecord::Migration[7.0]
+  def change
+    add_reference :communication_website_posts, :original, foreign_key: {to_table: :communication_website_posts}, type: :uuid
+
+    Communication::Website.find_each do |website|
+      website.posts.where(language_id: nil).update_all(language_id: website.default_language_id)
+    end
+
+    change_column_null :communication_website_posts, :language_id, false
+  end
+end
diff --git a/db/migrate/20230123162224_set_communication_website_categories_translatable.rb b/db/migrate/20230123162224_set_communication_website_categories_translatable.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6d4b7bc7fa049ca3f6d61645fc404312816f4b8c
--- /dev/null
+++ b/db/migrate/20230123162224_set_communication_website_categories_translatable.rb
@@ -0,0 +1,12 @@
+class SetCommunicationWebsiteCategoriesTranslatable < ActiveRecord::Migration[7.0]
+  def change
+    add_reference :communication_website_categories, :original, foreign_key: {to_table: :communication_website_categories}, type: :uuid
+    add_reference :communication_website_categories, :language, foreign_key: true, type: :uuid
+
+    Communication::Website.find_each do |website|
+      website.categories.where(language_id: nil).update_all(language_id: website.default_language_id)
+    end
+
+    change_column_null :communication_website_categories, :language_id, false
+  end
+end
diff --git a/db/migrate/20230126163347_set_communication_website_menu_translatable.rb b/db/migrate/20230126163347_set_communication_website_menu_translatable.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8096d244beaeb0023c6f163c485e8cffbf94820a
--- /dev/null
+++ b/db/migrate/20230126163347_set_communication_website_menu_translatable.rb
@@ -0,0 +1,12 @@
+class SetCommunicationWebsiteMenuTranslatable < ActiveRecord::Migration[7.0]
+  def change
+    add_reference :communication_website_menus, :original, foreign_key: {to_table: :communication_website_menus}, type: :uuid
+    add_reference :communication_website_menus, :language, foreign_key: true, type: :uuid
+
+    Communication::Website.find_each do |website|
+      website.menus.where(language_id: nil).update_all(language_id: website.default_language_id)
+    end
+
+    change_column_null :communication_website_menus, :language_id, false
+  end
+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 0000000000000000000000000000000000000000..2f0e8f9223322cd41e4bb032622a98258ad9863e
--- /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 0000000000000000000000000000000000000000..43785a9e4ab569c7a8cb22eec8b69e0f1d0a0b1c
--- /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 50a9de2b78181c49e803e1a72980e5e803480f5a..aae0b24e66c2141859b208683c8c02c248b79239 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,13 +10,13 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) 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"
   enable_extension "unaccent"
 
-  create_table "action_text_rich_texts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "action_text_rich_texts", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.string "name", null: false
     t.text "body"
     t.string "record_type", null: false
@@ -26,7 +26,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true
   end
 
-  create_table "active_storage_attachments", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "active_storage_attachments", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.string "name", null: false
     t.string "record_type", null: false
     t.uuid "record_id", null: false
@@ -36,7 +36,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
   end
 
-  create_table "active_storage_blobs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "active_storage_blobs", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.string "key", null: false
     t.string "filename", null: false
     t.string "content_type"
@@ -50,13 +50,13 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_active_storage_blobs_on_university_id"
   end
 
-  create_table "active_storage_variant_records", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "active_storage_variant_records", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "blob_id", null: false
     t.string "variation_digest", null: false
     t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
   end
 
-  create_table "administration_qualiopi_criterions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "administration_qualiopi_criterions", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.integer "number"
     t.text "name"
     t.text "description"
@@ -64,7 +64,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.datetime "updated_at", null: false
   end
 
-  create_table "administration_qualiopi_indicators", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "administration_qualiopi_indicators", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "criterion_id", null: false
     t.integer "number"
     t.text "name"
@@ -78,7 +78,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["criterion_id"], name: "index_administration_qualiopi_indicators_on_criterion_id"
   end
 
-  create_table "communication_blocks", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_blocks", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "about_type"
     t.uuid "about_id"
@@ -93,7 +93,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_communication_blocks_on_university_id"
   end
 
-  create_table "communication_extranets", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_extranets", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.string "name"
     t.uuid "university_id", null: false
     t.string "host"
@@ -117,7 +117,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_communication_extranets_on_university_id"
   end
 
-  create_table "communication_website_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_categories", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "communication_website_id", null: false
     t.string "name"
@@ -134,7 +134,11 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.string "featured_image_alt"
     t.text "summary"
     t.text "featured_image_credit"
+    t.uuid "original_id"
+    t.uuid "language_id", null: false
     t.index ["communication_website_id"], name: "idx_communication_website_post_cats_on_communication_website_id"
+    t.index ["language_id"], name: "index_communication_website_categories_on_language_id"
+    t.index ["original_id"], name: "index_communication_website_categories_on_original_id"
     t.index ["parent_id"], name: "index_communication_website_categories_on_parent_id"
     t.index ["program_id"], name: "index_communication_website_categories_on_program_id"
     t.index ["university_id"], name: "index_communication_website_categories_on_university_id"
@@ -147,7 +151,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["communication_website_post_id", "communication_website_category_id"], name: "post_category"
   end
 
-  create_table "communication_website_git_files", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_git_files", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.string "previous_path"
     t.string "about_type", null: false
     t.uuid "about_id", null: false
@@ -159,7 +163,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["website_id"], name: "index_communication_website_git_files_on_website_id"
   end
 
-  create_table "communication_website_imported_authors", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_imported_authors", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "website_id", null: false
     t.uuid "author_id"
@@ -175,7 +179,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["website_id"], name: "idx_communication_website_imported_auth_on_website"
   end
 
-  create_table "communication_website_imported_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_imported_categories", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "website_id", null: false
     t.uuid "category_id"
@@ -193,7 +197,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["website_id"], name: "idx_communication_website_imported_cat_on_website"
   end
 
-  create_table "communication_website_imported_media", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_imported_media", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.string "identifier"
     t.jsonb "data"
     t.text "file_url"
@@ -208,7 +212,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["website_id"], name: "index_communication_website_imported_media_on_website_id"
   end
 
-  create_table "communication_website_imported_pages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_imported_pages", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "website_id", null: false
     t.uuid "page_id"
@@ -232,7 +236,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["website_id"], name: "index_communication_website_imported_pages_on_website_id"
   end
 
-  create_table "communication_website_imported_posts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_imported_posts", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "website_id", null: false
     t.uuid "post_id"
@@ -257,7 +261,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["website_id"], name: "index_communication_website_imported_posts_on_website_id"
   end
 
-  create_table "communication_website_imported_websites", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_imported_websites", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "website_id", null: false
     t.integer "status", default: 0
@@ -267,7 +271,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["website_id"], name: "index_communication_website_imported_websites_on_website_id"
   end
 
-  create_table "communication_website_menu_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_menu_items", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "website_id", null: false
     t.uuid "menu_id", null: false
@@ -287,7 +291,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["website_id"], name: "index_communication_website_menu_items_on_website_id"
   end
 
-  create_table "communication_website_menus", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_menus", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "communication_website_id", null: false
     t.string "title"
@@ -295,11 +299,15 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.datetime "created_at", null: false
     t.datetime "updated_at", null: false
     t.text "github_path"
+    t.uuid "original_id"
+    t.uuid "language_id", null: false
     t.index ["communication_website_id"], name: "idx_comm_website_menus_on_communication_website_id"
+    t.index ["language_id"], name: "index_communication_website_menus_on_language_id"
+    t.index ["original_id"], name: "index_communication_website_menus_on_original_id"
     t.index ["university_id"], name: "index_communication_website_menus_on_university_id"
   end
 
-  create_table "communication_website_pages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_pages", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "communication_website_id", null: false
     t.string "title"
@@ -314,17 +322,19 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.text "github_path"
     t.string "featured_image_alt"
     t.text "text"
+    t.text "summary"
     t.string "breadcrumb_title"
     t.text "header_text"
     t.integer "kind"
-    t.text "summary"
     t.string "bodyclass"
-    t.uuid "language_id"
+    t.uuid "language_id", null: false
     t.text "featured_image_credit"
     t.boolean "full_width", default: false
     t.string "type"
+    t.uuid "original_id"
     t.index ["communication_website_id"], name: "index_communication_website_pages_on_communication_website_id"
     t.index ["language_id"], name: "index_communication_website_pages_on_language_id"
+    t.index ["original_id"], name: "index_communication_website_pages_on_original_id"
     t.index ["parent_id"], name: "index_communication_website_pages_on_parent_id"
     t.index ["university_id"], name: "index_communication_website_pages_on_university_id"
   end
@@ -343,7 +353,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["website_id"], name: "index_communication_website_permalinks_on_website_id"
   end
 
-  create_table "communication_website_posts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_posts", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "communication_website_id", null: false
     t.string "title"
@@ -359,15 +369,17 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.string "featured_image_alt"
     t.text "text"
     t.text "summary"
-    t.uuid "language_id"
+    t.uuid "language_id", null: false
     t.text "featured_image_credit"
+    t.uuid "original_id"
     t.index ["author_id"], name: "index_communication_website_posts_on_author_id"
     t.index ["communication_website_id"], name: "index_communication_website_posts_on_communication_website_id"
     t.index ["language_id"], name: "index_communication_website_posts_on_language_id"
+    t.index ["original_id"], name: "index_communication_website_posts_on_original_id"
     t.index ["university_id"], name: "index_communication_website_posts_on_university_id"
   end
 
-  create_table "communication_websites", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_websites", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "name"
     t.string "url"
@@ -419,7 +431,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["priority", "run_at"], name: "delayed_jobs_priority"
   end
 
-  create_table "education_academic_years", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "education_academic_years", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.integer "year"
     t.datetime "created_at", null: false
@@ -434,7 +446,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_person_id", "education_academic_year_id"], name: "index_person_academic_year"
   end
 
-  create_table "education_cohorts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "education_cohorts", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "program_id", null: false
     t.uuid "academic_year_id", null: false
@@ -455,7 +467,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_person_id", "education_cohort_id"], name: "index_person_cohort"
   end
 
-  create_table "education_diplomas", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "education_diplomas", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.string "name"
     t.string "short_name"
     t.integer "level", default: 0
@@ -469,7 +481,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_education_diplomas_on_university_id"
   end
 
-  create_table "education_programs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "education_programs", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "name"
     t.integer "capacity"
@@ -529,7 +541,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["education_program_id", "user_id"], name: "index_education_programs_users_on_program_id_and_user_id"
   end
 
-  create_table "education_schools", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "education_schools", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "name"
     t.string "address"
@@ -544,7 +556,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_education_schools_on_university_id"
   end
 
-  create_table "imports", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "imports", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.integer "number_of_lines"
     t.jsonb "processing_errors"
     t.integer "kind"
@@ -557,7 +569,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["user_id"], name: "index_imports_on_user_id"
   end
 
-  create_table "languages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "languages", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.string "name"
     t.string "iso_code"
     t.datetime "created_at", null: false
@@ -565,6 +577,20 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.string "summernote_locale"
   end
 
+  create_table "research_documents", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+    t.uuid "university_id", null: false
+    t.uuid "university_person_id", null: false
+    t.string "docid"
+    t.jsonb "data"
+    t.string "title"
+    t.string "url"
+    t.string "ref"
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+    t.index ["university_id"], name: "index_research_documents_on_university_id"
+    t.index ["university_person_id"], name: "index_research_documents_on_university_person_id"
+  end
+
   create_table "research_journal_paper_kinds", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "journal_id", null: false
@@ -576,7 +602,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_research_journal_paper_kinds_on_university_id"
   end
 
-  create_table "research_journal_papers", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "research_journal_papers", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.string "title"
     t.datetime "published_at", precision: nil
     t.uuid "university_id", null: false
@@ -609,7 +635,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["researcher_id"], name: "index_research_journal_papers_researchers_on_researcher_id"
   end
 
-  create_table "research_journal_volumes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "research_journal_volumes", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "research_journal_id", null: false
     t.string "title"
@@ -629,7 +655,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_research_journal_volumes_on_university_id"
   end
 
-  create_table "research_journals", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "research_journals", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "title"
     t.text "meta_description"
@@ -640,7 +666,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_research_journals_on_university_id"
   end
 
-  create_table "research_laboratories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "research_laboratories", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "name"
     t.string "address"
@@ -652,7 +678,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_research_laboratories_on_university_id"
   end
 
-  create_table "research_laboratory_axes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "research_laboratory_axes", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "research_laboratory_id", null: false
     t.string "name"
@@ -666,7 +692,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_research_laboratory_axes_on_university_id"
   end
 
-  create_table "research_theses", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "research_theses", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "research_laboratory_id", null: false
     t.uuid "author_id", null: false
@@ -684,7 +710,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_research_theses_on_university_id"
   end
 
-  create_table "universities", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "universities", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.string "name"
     t.string "identifier"
     t.string "address"
@@ -707,9 +733,11 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) 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: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "university_organizations", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "name"
     t.string "long_name"
@@ -736,7 +764,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_university_organizations_on_university_id"
   end
 
-  create_table "university_people", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "university_people", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "user_id"
     t.string "last_name"
@@ -768,12 +796,17 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.string "zipcode"
     t.string "city"
     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
 
-  create_table "university_person_experiences", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "university_person_experiences", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "person_id", null: false
     t.uuid "organization_id", null: false
@@ -787,7 +820,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_university_person_experiences_on_university_id"
   end
 
-  create_table "university_person_involvements", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "university_person_involvements", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "person_id", null: false
     t.integer "kind"
@@ -802,7 +835,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_university_person_involvements_on_university_id"
   end
 
-  create_table "university_roles", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "university_roles", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "target_type"
     t.uuid "target_id"
@@ -814,7 +847,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
     t.index ["university_id"], name: "index_university_roles_on_university_id"
   end
 
-  create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "users", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "first_name"
     t.string "last_name"
@@ -865,9 +898,11 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
   add_foreign_key "administration_qualiopi_indicators", "administration_qualiopi_criterions", column: "criterion_id"
   add_foreign_key "communication_blocks", "universities"
   add_foreign_key "communication_extranets", "universities"
+  add_foreign_key "communication_website_categories", "communication_website_categories", column: "original_id"
   add_foreign_key "communication_website_categories", "communication_website_categories", column: "parent_id"
   add_foreign_key "communication_website_categories", "communication_websites"
   add_foreign_key "communication_website_categories", "education_programs", column: "program_id"
+  add_foreign_key "communication_website_categories", "languages"
   add_foreign_key "communication_website_categories", "universities"
   add_foreign_key "communication_website_git_files", "communication_websites", column: "website_id"
   add_foreign_key "communication_website_imported_authors", "communication_website_imported_websites", column: "website_id"
@@ -892,13 +927,17 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
   add_foreign_key "communication_website_menu_items", "communication_website_menus", column: "menu_id"
   add_foreign_key "communication_website_menu_items", "communication_websites", column: "website_id"
   add_foreign_key "communication_website_menu_items", "universities"
+  add_foreign_key "communication_website_menus", "communication_website_menus", column: "original_id"
   add_foreign_key "communication_website_menus", "communication_websites"
+  add_foreign_key "communication_website_menus", "languages"
   add_foreign_key "communication_website_menus", "universities"
+  add_foreign_key "communication_website_pages", "communication_website_pages", column: "original_id"
   add_foreign_key "communication_website_pages", "communication_website_pages", column: "parent_id"
   add_foreign_key "communication_website_pages", "communication_websites"
   add_foreign_key "communication_website_pages", "universities"
   add_foreign_key "communication_website_permalinks", "communication_websites", column: "website_id"
   add_foreign_key "communication_website_permalinks", "universities"
+  add_foreign_key "communication_website_posts", "communication_website_posts", column: "original_id"
   add_foreign_key "communication_website_posts", "communication_websites"
   add_foreign_key "communication_website_posts", "universities"
   add_foreign_key "communication_website_posts", "university_people", column: "author_id"
@@ -915,6 +954,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) do
   add_foreign_key "education_schools", "universities"
   add_foreign_key "imports", "universities"
   add_foreign_key "imports", "users"
+  add_foreign_key "research_documents", "universities"
+  add_foreign_key "research_documents", "university_people"
   add_foreign_key "research_journal_paper_kinds", "research_journals", column: "journal_id"
   add_foreign_key "research_journal_paper_kinds", "universities"
   add_foreign_key "research_journal_papers", "research_journal_paper_kinds", column: "kind_id"
@@ -934,8 +975,11 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_19_164205) 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/controllers/server/universities_controller_test.rb b/test/controllers/server/universities_controller_test.rb
index b68f98caf705d19e40061400faa38353b04c893e..b512593f49e9a760ee8d1730dd9bd975f04175e8 100644
--- a/test/controllers/server/universities_controller_test.rb
+++ b/test/controllers/server/universities_controller_test.rb
@@ -29,7 +29,8 @@ class Server::UniversitiesControllerTest < ActionDispatch::IntegrationTest
         university: {
           name: "Nouvelle université",
           identifier: "my-second-university",
-          sms_sender_name: "unitest2"
+          sms_sender_name: "unitest2",
+          default_language_id: languages(:fr).id
         }
       }
       university = University.find_by(identifier: "my-second-university")
diff --git a/test/fixtures/communication/website/posts.yml b/test/fixtures/communication/website/posts.yml
index 8a2cbd33b616566e8787a71c542a9dacb2e2fa01..55fd7a17790f2a24f5eeecd8b142bb48b63463fe 100644
--- a/test/fixtures/communication/website/posts.yml
+++ b/test/fixtures/communication/website/posts.yml
@@ -18,7 +18,8 @@
 #  updated_at               :datetime         not null
 #  author_id                :uuid             indexed
 #  communication_website_id :uuid             not null, indexed
-#  language_id              :uuid             indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
 #  university_id            :uuid             not null, indexed
 #
 # Indexes
@@ -26,11 +27,13 @@
 #  index_communication_website_posts_on_author_id                 (author_id)
 #  index_communication_website_posts_on_communication_website_id  (communication_website_id)
 #  index_communication_website_posts_on_language_id               (language_id)
+#  index_communication_website_posts_on_original_id               (original_id)
 #  index_communication_website_posts_on_university_id             (university_id)
 #
 # Foreign Keys
 #
 #  fk_rails_1e0d058a25  (university_id => universities.id)
+#  fk_rails_bbbef3b1e9  (original_id => communication_website_posts.id)
 #  fk_rails_d1c1a10946  (communication_website_id => communication_websites.id)
 #  fk_rails_e0eec447b0  (author_id => university_people.id)
 #
@@ -39,6 +42,7 @@ test_post:
   website: website_with_github
   title: Test
   slug: test
+  language: fr
   published: true
   published_at: 2010-11-28 00:00:00
 test_post_2:
@@ -46,5 +50,6 @@ test_post_2:
   website: website_with_github
   title: Test 2
   slug: test-2
+  language: fr
   published: true
   published_at: 2010-11-28 00:00:00
diff --git a/test/fixtures/universities.yml b/test/fixtures/universities.yml
index a472c793d596ee8d5ebee66ac613a32d6c9ff02d..ecf1de669721a7a781d0118dcdc90a50f71960c9 100644
--- a/test/fixtures/universities.yml
+++ b/test/fixtures/universities.yml
@@ -25,13 +25,24 @@
 #  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
   identifier: my-university
   sms_sender_name: "unitest"
+  default_language: fr
 
 stale_university:
   name: Université obsolète
   identifier: stale-university
-  sms_sender_name: "unistale"
\ No newline at end of file
+  sms_sender_name: "unistale"
+  default_language: fr
diff --git a/test/fixtures/university/people.yml b/test/fixtures/university/people.yml
index 49c568d5195415d1b2944fba5c22dc17997a8f7f..4c0df963a5feedca53b12340d9d1b509ab65d3bd 100644
--- a/test/fixtures/university/people.yml
+++ b/test/fixtures/university/people.yml
@@ -2,47 +2,54 @@
 #
 # Table name: university_people
 #
-#  id                 :uuid             not null, primary key
-#  address            :string
-#  biography          :text
-#  birthdate          :date
-#  city               :string
-#  country            :string
-#  email              :string
-#  first_name         :string
-#  gender             :integer
-#  habilitation       :boolean          default(FALSE)
-#  is_administration  :boolean
-#  is_alumnus         :boolean          default(FALSE)
-#  is_author          :boolean
-#  is_researcher      :boolean
-#  is_teacher         :boolean
-#  last_name          :string
-#  linkedin           :string
-#  mastodon           :string
-#  meta_description   :text
-#  name               :string
-#  phone_mobile       :string
-#  phone_personal     :string
-#  phone_professional :string
-#  slug               :string
-#  summary            :text
-#  tenure             :boolean          default(FALSE)
-#  twitter            :string
-#  url                :string
-#  zipcode            :string
-#  created_at         :datetime         not null
-#  updated_at         :datetime         not null
-#  university_id      :uuid             not null, indexed
-#  user_id            :uuid             indexed
+#  id                    :uuid             not null, primary key
+#  address               :string
+#  biography             :text
+#  birthdate             :date
+#  city                  :string
+#  country               :string
+#  email                 :string
+#  first_name            :string
+#  gender                :integer
+#  habilitation          :boolean          default(FALSE)
+#  hal_person_identifier :string
+#  is_administration     :boolean
+#  is_alumnus            :boolean          default(FALSE)
+#  is_author             :boolean
+#  is_researcher         :boolean
+#  is_teacher            :boolean
+#  last_name             :string
+#  linkedin              :string
+#  mastodon              :string
+#  meta_description      :text
+#  name                  :string
+#  phone_mobile          :string
+#  phone_personal        :string
+#  phone_professional    :string
+#  slug                  :string
+#  summary               :text
+#  tenure                :boolean          default(FALSE)
+#  twitter               :string
+#  url                   :string
+#  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)
 #
@@ -57,3 +64,4 @@ alumnus:
     - default_cohort
   university: default_university
   user: alumnus
+  language: fr
diff --git a/test/models/communication/website/git_file_test.rb b/test/models/communication/website/git_file_test.rb
index 64e24c479b3d2b982d521c6e4903f8822e5f576f..82338ef8bbdddd952b0c8728e4cee628d181f954 100644
--- a/test/models/communication/website/git_file_test.rb
+++ b/test/models/communication/website/git_file_test.rb
@@ -38,7 +38,6 @@ class Communication::Website::GitFileTest < ActiveSupport::TestCase
       # file = communication_website_git_files(:git_file_2)
       # file.website.git_repository.add_git_file file
       # file.website.git_repository.sync!
-      # byebug
       # Then i got the sha and path, pasted it in the fixtures,
       # changed the text so the content would need an update.
       file = communication_website_git_files(:git_file_2)