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/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 26b29796fa17ff99cefa919e2fad89fe984d1e67..f5111244d3ac6ba18697d14938e0ae8550d29ae3 100644
--- a/app/controllers/admin/communication/websites/application_controller.rb
+++ b/app/controllers/admin/communication/websites/application_controller.rb
@@ -6,6 +6,11 @@ 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
@@ -13,9 +18,11 @@ class Admin::Communication::Websites::ApplicationController < Admin::Communicati
   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 e6b565e8d1758df272ca30763b8dec0b434f012d..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
 
@@ -86,9 +84,18 @@ class Admin::Communication::WebsitesController < Admin::Communication::Applicati
   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/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 f7d58bdccd106973d4b40671646f213feedb9f6f..455019b4d887f55d39f1e430f915e710d50316e4 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/researchers_controller.rb b/app/controllers/admin/research/researchers_controller.rb
index a4b26e7f2c7bea02d3f4fbf4c34915689eac736a..3d5a8b239b92d8c99255f28f800b5956918d597e 100644
--- a/app/controllers/admin/research/researchers_controller.rb
+++ b/app/controllers/admin/research/researchers_controller.rb
@@ -3,12 +3,21 @@ 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
 
   def show
-    @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])
     @papers = @researcher.research_journal_papers.ordered.page(params[:page])
     breadcrumb
     add_breadcrumb @researcher
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 0fde20c47221f4f77130ce72b000508af047e904..620d359ca4109970d3e5576310fa51a8abc639e2 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/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/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 d9a85ca2e6ceac30a2bfd37cbcc414c28dfc45c9..3de1e44b111d506e1db1b9f75e6fb574a44b3f1a 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/papers/
   # FIXME
-  def self.pattern_in_website(website)
-    "/#{website.special_page(Communication::Website::Page::Person).slug_with_ancestors}/:slug/papers/"
+  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 326f68426044f70f43cce7bf882831760632e405..15691d83123a5909d84f87a8c0616201f32a9c23 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 b96f64957dee83814daa09f1b6c209befe379112..5706c62d7ee1d8248beaa12e04a015804be123f9 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)
 #
@@ -58,6 +65,7 @@ class University::Person < ApplicationRecord
   include WithRoles
   include WithBlocks
   include WithPermalink
+  include WithTranslations
 
   LIST_OF_ROLES = [
     :administration,
@@ -111,7 +119,7 @@ class University::Person < ApplicationRecord
 
   validates_presence_of   :first_name, :last_name
   validates_uniqueness_of :email,
-                          scope: :university_id,
+                          scope: [:university_id, :language_id],
                           allow_blank: true,
                           if: :will_save_change_to_email?
   validates_format_of     :email,
@@ -239,4 +247,8 @@ class University::Person < ApplicationRecord
   def prepare_name
     self.name = to_s
   end
+
+  def translate_additional_data!(translation)
+    translate_attachment(translation, :picture) if picture.attached?
+  end
 end
diff --git a/app/models/university/person/administrator.rb b/app/models/university/person/administrator.rb
index 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/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/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/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/pages/_edit.html.erb b/app/views/admin/communication/blocks/templates/pages/_edit.html.erb
index e7181f93937ef85e4342a34a15224bc21344c36b..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">
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 c333410bdf42fd73159f62d9ec1026f3cf0ed39c..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">
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 c5ef71907efd33452c7532dfa2915d1ecbaf77fe..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,4 +1,3 @@
-<% 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>
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 3b129e59b248f51c3d464e17748e1e592994dc2e..e2205ef3eb35121a3d565933628ca3614643d482 100644
--- a/app/views/admin/communication/websites/_sidebar.html.erb
+++ b/app/views/admin/communication/websites/_sidebar.html.erb
@@ -32,13 +32,13 @@
       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)
       %>
         <li class="mb-3">
@@ -48,9 +48,22 @@
           </a>
         </li>
       <% end %>
-    </li>
+    </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/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/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/index.html.erb b/app/views/admin/communication/websites/pages/index.html.erb
index 7768cfcf89e7b338399f4ebcc7e008cc42b47429..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 %>
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 186d36c2e7e1e2a667fad77ca9164433f3557f15..1194a061bddafbb9f21a478a9035d0d4008f1ae7 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, hide_buttons: true %>
-<% 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 68bbffae44340b8eb0f560fd5a2b10b012cfd1ae..6a58f65738b84b34f86beb9b9b45bde7993cb46e 100644
--- a/app/views/admin/communication/websites/show/_posts.html.erb
+++ b/app/views/admin/communication/websites/show/_posts.html.erb
@@ -6,11 +6,14 @@ 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,
               hide_buttons: true %>
-<% end %>
\ No newline at end of file
+<% end %>
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 5c3cb9a62b35b7277bcaae1a9817a1384050b6f0..704e0df4a895bbc61dda4e4bb4808f30728f96b4 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/config/locales/communication/en.yml b/config/locales/communication/en.yml
index 8b3c6c9874e0ba259000e5777139a70615a17274..db55b72350b224452bfbc183c5e738891507d6bf 100644
--- a/config/locales/communication/en.yml
+++ b/config/locales/communication/en.yml
@@ -99,9 +99,6 @@ en:
       communication/website/menu:
         identifier: Identifier
         items: Items
-        legal: Legal
-        primary: Main menu
-        social: Social
         title: Title
       communication/website/menu/item:
         about: Target
@@ -150,6 +147,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:
@@ -293,6 +291,8 @@ en:
             edit:
               add_definition: Add definition
               remove_definition: Delete definition
+              description:
+                label: Description
               element:
                 title:
                   label: Title
@@ -572,6 +572,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:
@@ -686,7 +691,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 a5ccd2316ed9dd86bbde2ba5196845117f1796ec..827b5f231f299cb081e470dc4055329de9e73113 100644
--- a/config/locales/communication/fr.yml
+++ b/config/locales/communication/fr.yml
@@ -99,9 +99,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
@@ -150,6 +147,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:
@@ -574,6 +572,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:
@@ -688,7 +691,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 7e789754d22ec4055d1322cc6638ca27c125c10d..532844c10cdc8e5f195020193a84ec7ca872bc66 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -209,6 +209,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 9ffe5f4f0b27733d7ff9cc3cfd46b6c99e43e8c8..ba028427c365e4174f854e6f32431cb116fc07e8 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -209,6 +209,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 7d60884784347120ce28dc615a27ca4dbd55fc48..4441cc810328ea0b72498f37cb40e27e58531e3a 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 c78ba4ec06d95f91e4dc9e3fd54fd64e1d7c9160..9afa52af03634f7da6af40ab02059394a9218edf 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 7c0d2f7d6db5c7e6624a9450d33495abdd6aa5b9..2d90dd91f79497c2f83a990a057e3efd0561ac03 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 88481572fbefc983ce3615b6c660b5bfd82feaf5..100ea0fbecb2ab3d07ece8de6f52753857f751b0 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)