diff --git a/data/tutorials/getting-started/1_00_install_OCaml.ja.md b/data/tutorials/getting-started/1_00_install_OCaml.ja.md
new file mode 100644
index 0000000000..19c2754471
--- /dev/null
+++ b/data/tutorials/getting-started/1_00_install_OCaml.ja.md
@@ -0,0 +1,169 @@
+---
+id: installing-ocaml
+title: OCamlのインストール
+description: |
+ このページでは、OCamlとOCaml Platformツールのインストール方法を説明します。 |
+ この手順は、Windows、およびLinuxやmacOSのようなUnix系システムで動作します。
+category: "First Steps"
+language: Japanese
+---
+
+このガイドでは、OCamlの最小限のインストールについて説明します。これには、パッケージマネージャーと[コンパイラ](#installation-on-unix-and-macos)自体のインストールが含まれます。また、ビルドシステム、エディタのサポート、その他いくつかの重要なプラットフォームツールもインストールします。
+
+このページでは、Linux、macOS、Windows、および\*BSD向けの最近のOCamlバージョンのインストール手順を説明します。Dockerについては、opamの設定時を除き、Linuxの手順が適用されます。
+
+**注**: OCamlとそのツールは、[コマンドラインインターフェース(CLI)またはシェル](https://www.youtube.com/watch?v=0PxTAn4g20U)を介してインストールします。
+
+## opamのインストール
+
+OCamlには公式のパッケージマネージャー [opam](https://opam.ocaml.org/) があり、ユーザーはOCamlのツールやライブラリをダウンロードしてインストールできます。opamはまた、異なるバージョンのOCamlを必要とする様々なプロジェクトを扱うことを容易にします。
+
+opamはOCamlコンパイラもインストールします。他の選択肢も存在しますが、OCamlをインストールする最良の方法はopamです。OCamlはほとんどのLinuxディストリビューションでパッケージとして利用可能ですが、しばしばバージョンが古いことがあります。
+
+opamをインストールするには、[システムのパッケージマネージャーを使用する](https://opam.ocaml.org/doc/Install.html#Using-your-distribution-39-s-package-system)か、[バイナリディストリビューション](https://opam.ocaml.org/doc/Install.html#Binary-distribution)をダウンロードします。詳細はこれらのリンクにありますが、便宜上、ここではパッケージディストリビューションを使用します。
+
+**macOSの場合**
+
+[Homebrew](https://brew.sh/) を使ってインストールする場合:
+
+```shell
+brew install opam
+```
+
+または[MacPorts](https://www.macports.org/) を使っている場合:
+
+```shell
+port install opam
+```
+
+**注**: macOSでopamをインストールするのはかなり簡単ですが、Homebrewのインストールの仕組みが変更されたため、後で問題が発生する可能性があります。新しいMacで使われているM1プロセッサーなどのARM64環境では、実行ファイルが見つからない場合があります。これに対処するのはかなり複雑な手順になる可能性があるため、このインストールガイドの妨げにならないように、[短いARM64修正ドキュメント](/docs/arm64-fix)(英語)を作成しました。
+
+**Linuxの場合**
+
+Linuxでは、システムのパッケージマネージャーを使用してスーパーユーザーとしてopamをインストールすることが望ましいです。opamのサイトで、[すべてのインストール方法の詳細](https://opam.ocaml.org/doc/Install.html)を確認できます。opamのバージョン2.0以上が、サポートされているすべてのLinuxディストリビューションでパッケージ化されています。サポートされていないLinuxディストリビューションを使用している場合は、コンパイル済みのバイナリをダウンロードするか、ソースからopamをビルドしてください。
+
+DebianまたはUbuntuにインストールする場合:
+
+```shell
+sudo apt-get install opam
+```
+
+Arch Linuxにインストールする場合:
+
+```shell
+sudo pacman -S opam
+```
+
+**注**: Ubuntuでも使用されているDebianのopamパッケージは、OCamlコンパイラを推奨依存関係としています。デフォルトでは、そのような依存関係はインストールされます。OCamlなしでopamのみをインストールしたい場合は、次のようなコマンドを実行する必要があります:
+
+```shell
+sudo apt-get install --no-install-recommends opam
+```
+
+**Windowsの場合**
+
+[WinGet](https://github.com/microsoft/winget-cli) を使ってopamをインストールするのが最も簡単です:
+
+```shell
+PS C:\> winget install Git.Git OCaml.opam
+```
+
+**バイナリディストリビューション**
+
+opamの最新リリースが必要な場合は、バイナリディストリビューションを介してインストールしてください。UnixやmacOSでは、まず `gcc`、`build-essential`、`curl`、`bubblewrap`、`unzip` といったシステムパッケージをインストールする必要があります。これらのパッケージ名は、お使いのオペレーティングシステムやディストリビューションによって異なる場合があることに注意してください。また、このスクリプトは内部で `sudo` を呼び出すことにも注意してください。
+
+次のコマンドは、お使いのシステムに適用される最新バージョンのopamをインストールします:
+
+```shell
+bash -c "sh <(curl -fsSL https://opam.ocaml.org/install.sh)"
+```
+
+Windowsでは、wingetパッケージはopamの開発者によってメンテナンスされており、[GitHubでリリースされているバイナリ](https://github.com/ocaml/opam/releases)を使用しますが、同等のPowerShellスクリプトを使用してインストールすることもできます:
+
+```powershell
+Invoke-Expression "& { $(Invoke-RestMethod https://opam.ocaml.org/install.ps1) }"
+```
+
+**上級のWindowsユーザー向け**: CygwinやWSL2に詳しい場合は、[OCaml on Windows](/docs/ocaml-on-windows)(英語)ページに記載されている他のインストール方法があります。
+
+## opamの初期化
+
+opamをインストールしたら、初期化する必要があります。そのためには、一般ユーザーとして次のコマンドを実行してください。完了するまでに数分かかることがあります。
+
+```shell
+opam init -y
+```
+
+**注**: Dockerコンテナ内で `opam init` を実行している場合は、サンドボックス機能を無効にする必要があります。これは `opam init --disable-sandboxing -y` を実行することで行えます。特権付きDockerコンテナを実行しない限り、これが必要です。
+
+`opam init` の出力の最後に表示される指示に従って、初期化を完了させてください。通常、これは以下のようになります:
+
+Unixの場合:
+```
+eval $(opam env)
+```
+
+Windowsのコマンドプロンプトの場合:
+```
+for /f "tokens=*" %i in ('opam env') do @%i
+```
+
+PowerShellの場合:
+```powershell
+(& opam env) -split '\r?\n' | ForEach-Object { Invoke-Expression $_ }
+```
+
+opamの初期化には数分かかることがあります。インストールと設定が完了するのを待つ間、[A Tour of OCaml](/docs/tour-of-ocaml)(英語)を読み始めてください。
+
+**注**: opamは_スイッチ_と呼ばれるものを管理できます。これは複数のOCamlプロジェクトを切り替える際に重要です。しかし、この「はじめの一歩」シリーズのチュートリアルでは、スイッチは必要ありません。興味があれば、[opamスイッチの紹介](/docs/opam-switch-introduction)(英語)を読むことができます。
+
+**インストールで問題がありましたか?** 必ず[最新のリリースノート](https://opam.ocaml.org/blog/opam-2-2-0/)(英語)を読んでください。問題は または で報告できます。
+
+## Platformツールのインストール
+
+OCamlコンパイラとopamパッケージマネージャーのインストールに成功したので、次はOCamlでの完全な開発体験を得るために必要な[OCaml Platformツール](https://ocaml.org/docs/platform)(英語)をいくつかインストールしましょう。
+
+- [UTop](https://github.com/ocaml-community/utop): モダンな対話的トップレベル (REPL: Read-Eval-Print Loop)
+- [Dune](https://dune.build): 高速で多機能なビルドシステム
+- [`ocaml-lsp-server`](https://github.com/ocaml/ocaml-lsp): Language Server Protocolを実装し、VS Code、Vim、EmacsなどでOCamlのエディタサポートを可能にします。
+- [`odoc`](https://github.com/ocaml/odoc): OCamlコードからドキュメントを生成します。
+- [OCamlFormat](https://opam.ocaml.org/packages/ocamlformat/): OCamlコードを自動的にフォーマットします。
+
+これらのツールはすべて、単一のコマンドでインストールできます:
+
+```shell
+opam install ocaml-lsp-server odoc ocamlformat utop
+```
+
+これで準備は万端、コーディングを始める準備ができました。
+
+## インストールの確認
+
+すべてが正しく動作しているか確認するために、UTopトップレベルを起動してみましょう:
+
+```shell
+$ utop
+────────┬─────────────────────────────────────────────────────────────┬─────────
+ │ Welcome to utop version 2.13.1 (using OCaml version 5.1.0)! │
+ └─────────────────────────────────────────────────────────────┘
+
+Type #utop_help for help about using utop.
+
+─( 00:00:00 )─< command 0 >──────────────────────────────────────{ counter: 0 }─
+utop #
+```
+
+これでOCamlのトップレベルに入りました。OCamlの式を入力し始めることができます。例えば、`#` プロンプトで `21 * 2;;` と入力し、`Enter`キーを押してみてください。次のように表示されます:
+
+```ocaml
+# 21 * 2;;
+- : int = 42
+```
+
+**おめでとうございます**! OCamlのインストールが完了しました! 🎉
+
+UTopを終了するには、`#quit;;`(ここでの`#`はプロンプトではなく、入力する必要があります)と入力するか、`Ctrl+D`を押してください。
+
+## コミュニティに参加する
+
+ぜひ[OCamlコミュニティ](/community)に参加してください。[Discuss](https://discuss.ocaml.org/)や[Discord](https://discord.com/invite/cCYQbqN)で多くのコミュニティメンバーを見つけることができます。これらは何か問題があった場合に助けを求めるのに最適な場所です。
\ No newline at end of file
diff --git a/data/tutorials/getting-started/1_00_install_OCaml.md b/data/tutorials/getting-started/1_00_install_OCaml.md
index caa4ed093f..c1f5158737 100644
--- a/data/tutorials/getting-started/1_00_install_OCaml.md
+++ b/data/tutorials/getting-started/1_00_install_OCaml.md
@@ -5,6 +5,7 @@ description: |
This page will help you install OCaml and the OCaml Platform Tools. |
These instructions work on Windows, and Unix systems like Linux, and macOS.
category: "First Steps"
+language: English
---
This guide will walk you through a minimum installation of OCaml. That includes installing a package manager and [the compiler](#installation-on-unix-and-macos) itself. We'll also install some platform tools like a build system, support for your editor, and a few other important ones.
diff --git a/data/tutorials/getting-started/1_01_a_tour_of_ocaml.md b/data/tutorials/getting-started/1_01_a_tour_of_ocaml.md
index 9b0f2fd704..4bbb797b3e 100644
--- a/data/tutorials/getting-started/1_01_a_tour_of_ocaml.md
+++ b/data/tutorials/getting-started/1_01_a_tour_of_ocaml.md
@@ -4,6 +4,7 @@ title: A Tour of OCaml
description: >
Hop on the OCaml sightseeing bus. This absolute beginner tutorial will drive you through the marvels and wonders of OCaml. We'll have a look at the most commonly used language features.
category: "First Steps"
+language: English
recommended_next_tutorials:
- "values-and-functions"
- "basic-data-types"
diff --git a/data/tutorials/getting-started/1_02_your_first_ocaml_program.md b/data/tutorials/getting-started/1_02_your_first_ocaml_program.md
index 3173098a70..69ab9efa3f 100644
--- a/data/tutorials/getting-started/1_02_your_first_ocaml_program.md
+++ b/data/tutorials/getting-started/1_02_your_first_ocaml_program.md
@@ -4,6 +4,7 @@ title: Your First OCaml Program
description: >
Learn how to write your very first OCaml program.
category: "First Steps"
+language: English
recommended_next_tutorials:
- "values-and-functions"
- "basic-data-types"
diff --git a/data/tutorials/getting-started/2_00_editor_setup.md b/data/tutorials/getting-started/2_00_editor_setup.md
index 73c106ab2a..85c232060e 100644
--- a/data/tutorials/getting-started/2_00_editor_setup.md
+++ b/data/tutorials/getting-started/2_00_editor_setup.md
@@ -4,6 +4,7 @@ title: Configuring Your Editor
description: |
This page will show you how to set up your editor for OCaml.
category: "Tooling"
+language: English
---
While the toplevel is great for interactively trying out the language, we will shortly need to write OCaml files in an editor. We already installed the tools required to enhance Merlin, our editor of choice with OCaml support. Merlin provides all features such as "jump to definition," "show type," and `ocaml-lsp-server`, a server that delivers those features to the editor through the LSP server.
OCaml has plugins for many editors, but the most actively maintained are for Visual Studio Code, Emacs, and Vim.
diff --git a/data/tutorials/getting-started/2_01_toplevel.md b/data/tutorials/getting-started/2_01_toplevel.md
index ac10339ca9..e0449c46aa 100644
--- a/data/tutorials/getting-started/2_01_toplevel.md
+++ b/data/tutorials/getting-started/2_01_toplevel.md
@@ -4,6 +4,7 @@ title: Introduction to the OCaml Toplevel
description: |
This page will give you a brief introduction to the OCaml toplevel.
category: "Tooling"
+language: English
---
An OCaml toplevel is a chat between the user and OCaml. The user writes OCaml code, and UTop evaluates it. This is why it is also called a Read-Eval-Print-Loop (REPL). Several OCaml toplevels exist, like `ocaml` and `utop`. We recommend using UTop, which is part of the [OCaml Platform](/docs/platform) toolchain.
diff --git a/data/tutorials/getting-started/2_02_opam_switch.md b/data/tutorials/getting-started/2_02_opam_switch.md
index 940d879f06..df26a2f79f 100644
--- a/data/tutorials/getting-started/2_02_opam_switch.md
+++ b/data/tutorials/getting-started/2_02_opam_switch.md
@@ -4,6 +4,7 @@ title: Introduction to opam Switches
description: |
This page will give you a brief introduction to opam switches, what they're used for, and how to create them.
category: "Tooling"
+language: English
---
OCaml's package manager, opam, introduces the concept of a _switch_, which is an isolated OCaml environment. These switches often cause confusion amongst OCaml newcomers, so this document aims to provide a better understanding of opam switches and their usage for managing dependencies and project-specific configurations.
diff --git a/data/tutorials/getting-started/3_01_ocaml_on_windows.md b/data/tutorials/getting-started/3_01_ocaml_on_windows.md
index 8867e6e078..6a241f5e4d 100644
--- a/data/tutorials/getting-started/3_01_ocaml_on_windows.md
+++ b/data/tutorials/getting-started/3_01_ocaml_on_windows.md
@@ -4,6 +4,7 @@ title: OCaml on Windows
description: >
Read about the state of OCaml on Windows and our roadmap to improve Windows support.
category: "Resources"
+language: English
---
We recommend installing [opam](https://opam.ocaml.org/) for new users. Opam, the OCaml package manager, has full Windows support since version 2.2 and provides the most up-to-date OCaml environment.
diff --git a/data/tutorials/getting-started/3_02_arm_fix.md b/data/tutorials/getting-started/3_02_arm_fix.md
index 42b0755407..89243b17d9 100644
--- a/data/tutorials/getting-started/3_02_arm_fix.md
+++ b/data/tutorials/getting-started/3_02_arm_fix.md
@@ -4,6 +4,7 @@ title: Fix Homebrew Errors on Apple M1
description: |
This page will walk you through the workaround for ARM64 processors on newer Macs.
category: "Resources"
+language: English
---
Since [Homebrew has changed](https://github.com/Homebrew/brew/issues/9177) the way it installs, sometimes the executable files cannot be found on macOS ARM64 M1. This might cause errors as you work through these tutorials. We want Homebrew to install ARM64 by default, so there are a few changes we need to make in order to do this.
diff --git a/data/tutorials/getting-started/3_03_ocaml_playground.md b/data/tutorials/getting-started/3_03_ocaml_playground.md
index eaeaddb461..08ef656219 100644
--- a/data/tutorials/getting-started/3_03_ocaml_playground.md
+++ b/data/tutorials/getting-started/3_03_ocaml_playground.md
@@ -5,6 +5,7 @@ short_title: The OCaml Playground
description: |
This page will walk you through the OCaml Playground
category: "Resources"
+language: English
---
Welcome to OCaml's in-browser playground!
diff --git a/data/tutorials/guides/0tt_00_formatting_text.md b/data/tutorials/guides/0tt_00_formatting_text.md
index 083563283d..602986f45c 100644
--- a/data/tutorials/guides/0tt_00_formatting_text.md
+++ b/data/tutorials/guides/0tt_00_formatting_text.md
@@ -6,6 +6,7 @@ description: >
provides pretty-printing facilities to get a fancy display for printing
routines
category: "Tutorials"
+language: English
---
The `Format` module of Caml Light and OCaml's standard libraries
diff --git a/data/tutorials/guides/0tt_01_command_line_arguments.md b/data/tutorials/guides/0tt_01_command_line_arguments.md
index 08404e8bb2..225627f664 100644
--- a/data/tutorials/guides/0tt_01_command_line_arguments.md
+++ b/data/tutorials/guides/0tt_01_command_line_arguments.md
@@ -4,6 +4,7 @@ title: Command-line Arguments
description: >
The Arg module that comes with the compiler can help you write command line interfaces
category: "Tutorials"
+language: English
---
In this tutorial we learn how to read command line arguments directly, using
diff --git a/data/tutorials/guides/0tt_02_file_manipulation.md b/data/tutorials/guides/0tt_02_file_manipulation.md
index e667a2e7f0..4c0e01235c 100644
--- a/data/tutorials/guides/0tt_02_file_manipulation.md
+++ b/data/tutorials/guides/0tt_02_file_manipulation.md
@@ -4,6 +4,7 @@ title: File Manipulation
description: >
A guide to basic file manipulation in OCaml with the standard library
category: "Tutorials"
+language: English
---
This is a guide to basic file manipulation in OCaml using only the
diff --git a/data/tutorials/guides/0tt_03_calling_c_libraries.md b/data/tutorials/guides/0tt_03_calling_c_libraries.md
index e63629400a..9a474688ca 100644
--- a/data/tutorials/guides/0tt_03_calling_c_libraries.md
+++ b/data/tutorials/guides/0tt_03_calling_c_libraries.md
@@ -4,6 +4,7 @@ title: Calling C Libraries
description: >
Cross the divide and call C code from your OCaml program
category: "Tutorials"
+language: English
---
## MiniGtk
diff --git a/data/tutorials/guides/0tt_04_calling_fortran_libraries.md b/data/tutorials/guides/0tt_04_calling_fortran_libraries.md
index 39f2e11cf3..f4b9eaf5fa 100644
--- a/data/tutorials/guides/0tt_04_calling_fortran_libraries.md
+++ b/data/tutorials/guides/0tt_04_calling_fortran_libraries.md
@@ -4,6 +4,7 @@ title: Calling Fortran Libraries
description: >
Cross the divide and call Fortran code from your OCaml program
category: "Tutorials"
+language: English
---
Fortran isn't a language the many people write new code in but it still
diff --git a/data/tutorials/guides/1wf_00_using_the_ocaml_compiler_toolchain.md b/data/tutorials/guides/1wf_00_using_the_ocaml_compiler_toolchain.md
index 6b544822fb..0a006e4f4e 100644
--- a/data/tutorials/guides/1wf_00_using_the_ocaml_compiler_toolchain.md
+++ b/data/tutorials/guides/1wf_00_using_the_ocaml_compiler_toolchain.md
@@ -4,6 +4,7 @@ title: Using the OCaml Compiler Toolchain
description: >
An introduction to the OCaml compiler tools for building OCaml projects as well as the most common build tools such as Dune
category: "Guides"
+language: English
---
This tutorial explains how to compile your OCaml programs into executable form.
diff --git a/data/tutorials/guides/1wf_01_debugging.md b/data/tutorials/guides/1wf_01_debugging.md
index 87ba99cd55..9a8d42f0de 100644
--- a/data/tutorials/guides/1wf_01_debugging.md
+++ b/data/tutorials/guides/1wf_01_debugging.md
@@ -4,6 +4,7 @@ title: Debugging
description: >
Learn to debug OCaml programs using tracing and ocamldebug
category: "Guides"
+language: English
---
This tutorial presents four techniques for debugging OCaml programs:
diff --git a/data/tutorials/guides/1wf_02_error_handling.md b/data/tutorials/guides/1wf_02_error_handling.md
index 3b6ee3e833..6ed9457265 100644
--- a/data/tutorials/guides/1wf_02_error_handling.md
+++ b/data/tutorials/guides/1wf_02_error_handling.md
@@ -4,6 +4,7 @@ title: Error Handling
description: >
Discover the different ways you can manage errors in your OCaml programs
category: "Guides"
+language: English
---
In OCaml, errors can be handled in several ways. This document presents most of
diff --git a/data/tutorials/guides/1wf_03_profiling.md b/data/tutorials/guides/1wf_03_profiling.md
index 24f44592fd..96e9a1f6a3 100644
--- a/data/tutorials/guides/1wf_03_profiling.md
+++ b/data/tutorials/guides/1wf_03_profiling.md
@@ -4,6 +4,7 @@ title: Profiling
description: >
Understand how to profile your OCaml code to analyse its performance and produce faster programs
category: "Guides"
+language: English
---
## Speed
diff --git a/data/tutorials/guides/1wf_04_multicore_ready.md b/data/tutorials/guides/1wf_04_multicore_ready.md
index 9f461b3fb9..3ea05892e2 100644
--- a/data/tutorials/guides/1wf_04_multicore_ready.md
+++ b/data/tutorials/guides/1wf_04_multicore_ready.md
@@ -5,6 +5,7 @@ short_title: Transitioning to Multicore with TSan
description: >
Learn to make your OCaml code multicore ready with ThreadSanitizer
category: "Guides"
+language: English
---
The 5.0 release brought Multicore, `Domain`-based parallelism to the
diff --git a/data/tutorials/guides/1wf_05_garbage_collection.md b/data/tutorials/guides/1wf_05_garbage_collection.md
index 1e94ddc230..f34dad7694 100644
--- a/data/tutorials/guides/1wf_05_garbage_collection.md
+++ b/data/tutorials/guides/1wf_05_garbage_collection.md
@@ -4,6 +4,7 @@ title: How to Work with the Garbage Collector
description: >
How to use the Gc module in OCaml and how to write your own finalisers.
category: "Guides"
+language: English
---
In [Understanding the Garbage Collector](/docs/garbage-collector), discussed how Garbage Collection in OCaml works.
diff --git a/data/tutorials/guides/rs_00_guidelines.md b/data/tutorials/guides/rs_00_guidelines.md
index a6057d404f..7dd773e859 100644
--- a/data/tutorials/guides/rs_00_guidelines.md
+++ b/data/tutorials/guides/rs_00_guidelines.md
@@ -4,6 +4,7 @@ title: OCaml Programming Guidelines
description: >
Opinionated guidelines for writing OCaml code
category: "Resources"
+language: English
---
This is a set of reasonable guidelines for writing OCaml
diff --git a/data/tutorials/guides/rs_01_common_error_messages.md b/data/tutorials/guides/rs_01_common_error_messages.md
index 269ce02750..95ec2964fc 100644
--- a/data/tutorials/guides/rs_01_common_error_messages.md
+++ b/data/tutorials/guides/rs_01_common_error_messages.md
@@ -4,6 +4,7 @@ title: Common Error Messages
description: >
Understand the most common error messages the OCaml compiler can throw at you
category: "Resources"
+language: English
---
This page gives a list of quick explanations for some error or warning
diff --git a/data/tutorials/guides/rs_02_comparison_of_standard_containers.md b/data/tutorials/guides/rs_02_comparison_of_standard_containers.md
index ff39f0d2a1..5a4cbf1d09 100644
--- a/data/tutorials/guides/rs_02_comparison_of_standard_containers.md
+++ b/data/tutorials/guides/rs_02_comparison_of_standard_containers.md
@@ -4,6 +4,7 @@ title: Comparison of Standard Containers
description: >
Rough comparison of the different container types in OCaml
category: "Resources"
+language: English
---
This is a rough comparison of the different container types
diff --git a/data/tutorials/language/0it_00_values_functions.md b/data/tutorials/language/0it_00_values_functions.md
index 8d374ff9ad..d0671b0e6b 100644
--- a/data/tutorials/language/0it_00_values_functions.md
+++ b/data/tutorials/language/0it_00_values_functions.md
@@ -4,6 +4,7 @@ title: Values and Functions
description: |
Functions, values, definitions, environments, scopes, closures, and shadowing. This tutorial will help you master the fundamentals.
category: "Introduction"
+language: English
prerequisite_tutorials:
- "toplevel-introduction"
- "installing-ocaml"
diff --git a/data/tutorials/language/0it_01_basic_datatypes.md b/data/tutorials/language/0it_01_basic_datatypes.md
index 0d775399ba..ebf56d5301 100644
--- a/data/tutorials/language/0it_01_basic_datatypes.md
+++ b/data/tutorials/language/0it_01_basic_datatypes.md
@@ -4,6 +4,7 @@ title: Basic Data Types and Pattern Matching
description: |
Predefined Types, Variants, Records, and Pattern Matching
category: "Introduction"
+language: English
prerequisite_tutorials:
- "tour-of-ocaml"
- "values-and-functions"
diff --git a/data/tutorials/language/0it_02_loops_and_recursion.md b/data/tutorials/language/0it_02_loops_and_recursion.md
index aed6145030..71d30e52b8 100644
--- a/data/tutorials/language/0it_02_loops_and_recursion.md
+++ b/data/tutorials/language/0it_02_loops_and_recursion.md
@@ -4,6 +4,7 @@ title: Loops and Recursions
description: >
Learn basic control-flow and recursion in OCaml
category: "Introduction"
+language: English
---
As in other OCaml.org documentation, the code examples will either be something you can test or
diff --git a/data/tutorials/language/0it_03_lists.md b/data/tutorials/language/0it_03_lists.md
index bb05f3da9c..d199ce5833 100644
--- a/data/tutorials/language/0it_03_lists.md
+++ b/data/tutorials/language/0it_03_lists.md
@@ -4,6 +4,7 @@ title: Lists
description: >
Learn about one of OCaml's must used, built-in data types
category: "Introduction"
+language: English
---
A list is an ordered sequence of elements. All elements of a list in OCaml must
diff --git a/data/tutorials/language/0it_04_higher_order_functions.md b/data/tutorials/language/0it_04_higher_order_functions.md
index c47b35e7d2..4af20b255f 100644
--- a/data/tutorials/language/0it_04_higher_order_functions.md
+++ b/data/tutorials/language/0it_04_higher_order_functions.md
@@ -4,6 +4,7 @@ title: Higher Order Functions
description: >
Functions describe the world; higher-order functions take functions as parameters. Learn how to manipulate and leverage functions to write composable and reusable code.
category: "Introduction"
+language: English
prerequisite_tutorials:
- "values-and-functions"
- "loops-recursion"
diff --git a/data/tutorials/language/0it_05_labels.md b/data/tutorials/language/0it_05_labels.md
index 7218e02a79..eb8e90406e 100644
--- a/data/tutorials/language/0it_05_labels.md
+++ b/data/tutorials/language/0it_05_labels.md
@@ -4,6 +4,7 @@ title: Labelled and Optional Arguments
description: >
Provide labels to your functions arguments
category: "Introduction"
+language: English
prerequisite_tutorials:
- "values-and-functions"
---
diff --git a/data/tutorials/language/0it_06_imperative.md b/data/tutorials/language/0it_06_imperative.md
index 30b77c12a0..3375aa6851 100644
--- a/data/tutorials/language/0it_06_imperative.md
+++ b/data/tutorials/language/0it_06_imperative.md
@@ -4,6 +4,7 @@ title: Mutability and Imperative Control Flow
description: >
Write stateful programs in OCaml. Use for and while loops, if-then-else, mutable record fields, and references.
category: "Introduction"
+language: English
prerequisite_tutorials:
- "basic-data-types"
- "values-and-functions"
diff --git a/data/tutorials/language/1ms_00_modules.md b/data/tutorials/language/1ms_00_modules.md
index 7a03810d5c..3be3c76530 100644
--- a/data/tutorials/language/1ms_00_modules.md
+++ b/data/tutorials/language/1ms_00_modules.md
@@ -4,6 +4,7 @@ title: Modules
description: >
Modules are collections of definitions. This is the basic means to organise OCaml software.
category: "Module System"
+language: English
prerequisite_tutorials:
- values-and-functions
- basic-data-types
diff --git a/data/tutorials/language/1ms_01_functors.md b/data/tutorials/language/1ms_01_functors.md
index 9cceaf187a..2fa3513f70 100644
--- a/data/tutorials/language/1ms_01_functors.md
+++ b/data/tutorials/language/1ms_01_functors.md
@@ -4,6 +4,7 @@ title: Functors
description: >
In OCaml, a functor is a function at the module-level. Functors take modules as arguments and return a new module.
category: "Module System"
+language: English
prerequisite_tutorials:
- modules
---
diff --git a/data/tutorials/language/1ms_02_dune.md b/data/tutorials/language/1ms_02_dune.md
index 586679e9e0..4e4a54a192 100644
--- a/data/tutorials/language/1ms_02_dune.md
+++ b/data/tutorials/language/1ms_02_dune.md
@@ -4,6 +4,7 @@ title: Libraries With Dune
description: >
Dune provides several means to arrange modules into libraries. We look at Dune's mechanisms for structuring projects with libraries that contain modules.
category: "Module System"
+language: English
prerequisite_tutorials:
- modules
- functors
diff --git a/data/tutorials/language/3ds_00_options.md b/data/tutorials/language/3ds_00_options.md
index cabd183b23..05740d2c89 100644
--- a/data/tutorials/language/3ds_00_options.md
+++ b/data/tutorials/language/3ds_00_options.md
@@ -4,6 +4,7 @@ title: Options
description: >
Add nothing-as-value to anything to avoid confusion between something and “no such thing“.
category: "Data Structures"
+language: English
---
## Introduction
diff --git a/data/tutorials/language/3ds_01_arrays.md b/data/tutorials/language/3ds_01_arrays.md
index 7a1cfbb5e6..760169f739 100644
--- a/data/tutorials/language/3ds_01_arrays.md
+++ b/data/tutorials/language/3ds_01_arrays.md
@@ -4,6 +4,7 @@ title: Arrays
description: >
The standard library's Array module
category: "Data Structures"
+language: English
---
## Introduction
diff --git a/data/tutorials/language/3ds_02_maps.md b/data/tutorials/language/3ds_02_maps.md
index e24bbf2c71..1a8efc3122 100644
--- a/data/tutorials/language/3ds_02_maps.md
+++ b/data/tutorials/language/3ds_02_maps.md
@@ -4,6 +4,7 @@ title: Maps
description: >
Create a mapping using the standard library's Map module
category: "Data Structures"
+language: English
---
## Introduction
diff --git a/data/tutorials/language/3ds_03_sets.md b/data/tutorials/language/3ds_03_sets.md
index e407723871..b7ba0456b2 100644
--- a/data/tutorials/language/3ds_03_sets.md
+++ b/data/tutorials/language/3ds_03_sets.md
@@ -4,6 +4,7 @@ title: Sets
description: >
The standard library's Set module
category: "Data Structures"
+language: English
---
## Introduction
diff --git a/data/tutorials/language/3ds_04_hashtbl.md b/data/tutorials/language/3ds_04_hashtbl.md
index b6bc9689f1..2616e13600 100644
--- a/data/tutorials/language/3ds_04_hashtbl.md
+++ b/data/tutorials/language/3ds_04_hashtbl.md
@@ -4,6 +4,7 @@ title: Hash Tables
description: >
Discover efficient and mutable lookup tables with OCaml's Hashtbl module
category: "Data Structures"
+language: English
---
## Introduction
diff --git a/data/tutorials/language/3ds_05_seq.md b/data/tutorials/language/3ds_05_seq.md
index e0d3c6167a..b10e0e17a2 100644
--- a/data/tutorials/language/3ds_05_seq.md
+++ b/data/tutorials/language/3ds_05_seq.md
@@ -4,6 +4,7 @@ title: Sequences
description: >
Learn about sequences, of OCaml's most-used, built-in data types
category: "Data Structures"
+language: English
prerequisite_tutorials:
- "lists"
- "options"
diff --git a/data/tutorials/language/3ds_06_memoization.md b/data/tutorials/language/3ds_06_memoization.md
index c05f8d0b9a..3482b88674 100644
--- a/data/tutorials/language/3ds_06_memoization.md
+++ b/data/tutorials/language/3ds_06_memoization.md
@@ -4,6 +4,7 @@ title: Memoization
description: >
Memoization, adapted from OCaml Programming: Correct + Efficient + Beautiful
category: "Data Structures"
+language: English
external_tutorial:
tag: "CS3110"
banner:
diff --git a/data/tutorials/language/3ds_07_monads.md b/data/tutorials/language/3ds_07_monads.md
index 19f4360723..85c64a8914 100644
--- a/data/tutorials/language/3ds_07_monads.md
+++ b/data/tutorials/language/3ds_07_monads.md
@@ -4,6 +4,7 @@ title: Monads
description: >
Monads, adapted from OCaml Programming: Correct + Efficient + Beautiful
category: "Data Structures"
+language: English
external_tutorial:
tag: "CS3110"
banner:
diff --git a/data/tutorials/language/4ad_00_metaprogramming.md b/data/tutorials/language/4ad_00_metaprogramming.md
index 170db04ef1..fd025b018c 100644
--- a/data/tutorials/language/4ad_00_metaprogramming.md
+++ b/data/tutorials/language/4ad_00_metaprogramming.md
@@ -5,6 +5,7 @@ description: |
An introduction to metaprogramming in OCaml, including preprocessors, PPX
extensions and the `ppxlib` library.
category: "Advanced Topics"
+language: English
---
Preprocessors are programs meant to be called at compile time, so that they
diff --git a/data/tutorials/language/4ad_01_operators.md b/data/tutorials/language/4ad_01_operators.md
index d06d428613..45b85e68f9 100644
--- a/data/tutorials/language/4ad_01_operators.md
+++ b/data/tutorials/language/4ad_01_operators.md
@@ -4,6 +4,7 @@ title: Operators
description: |
Binary and prefix operators, how to use and define them, how they are parsed and evaluated.
category: "Advanced Topics"
+language: English
---
## Goals
diff --git a/data/tutorials/language/4ad_02_objects.md b/data/tutorials/language/4ad_02_objects.md
index d304c46d4c..c1e6cb3733 100644
--- a/data/tutorials/language/4ad_02_objects.md
+++ b/data/tutorials/language/4ad_02_objects.md
@@ -4,6 +4,7 @@ title: Objects
description: >
OCaml is an object-oriented, imperative, functional programming language
category: "Advanced Topics"
+language: English
---
## Objects and Classes
diff --git a/data/tutorials/language/5rt_00_memory_representation.md b/data/tutorials/language/5rt_00_memory_representation.md
index 87ab25f644..a4564c2380 100644
--- a/data/tutorials/language/5rt_00_memory_representation.md
+++ b/data/tutorials/language/5rt_00_memory_representation.md
@@ -4,6 +4,7 @@ title: Memory Representation of Values
description: >
Memory Representation of Values, excerpt from Real World OCaml
category: "Runtime & Compiler"
+language: English
external_tutorial:
tag: "RWO"
banner:
diff --git a/data/tutorials/language/5rt_01_garbage-collector.md b/data/tutorials/language/5rt_01_garbage-collector.md
index 5cb0ab3626..2b62a407b0 100644
--- a/data/tutorials/language/5rt_01_garbage-collector.md
+++ b/data/tutorials/language/5rt_01_garbage-collector.md
@@ -4,6 +4,7 @@ title: Understanding the Garbage Collector
description: >
Understanding the Garbage Collector, excerpt from Real World OCaml
category: "Runtime & Compiler"
+language: English
external_tutorial:
tag: "RWO"
banner:
diff --git a/data/tutorials/language/5rt_02_compiler_frontend.md b/data/tutorials/language/5rt_02_compiler_frontend.md
index a5ad1355d7..0d4e4d36c7 100644
--- a/data/tutorials/language/5rt_02_compiler_frontend.md
+++ b/data/tutorials/language/5rt_02_compiler_frontend.md
@@ -5,6 +5,7 @@ short_title: Compiler Frontend
description: >
The Compiler Frontend: Parsing and Type Checking, excerpt from Real World OCaml
category: "Runtime & Compiler"
+language: English
external_tutorial:
tag: "RWO"
banner:
diff --git a/data/tutorials/language/5rt_03_compiler_backend.md b/data/tutorials/language/5rt_03_compiler_backend.md
index 214564f2e6..0829ba2486 100644
--- a/data/tutorials/language/5rt_03_compiler_backend.md
+++ b/data/tutorials/language/5rt_03_compiler_backend.md
@@ -5,6 +5,7 @@ short_title: Compiler Backend
description: >
The Compiler Backend, excerpt from Real World OCaml
category: "Runtime & Compiler"
+language: English
external_tutorial:
tag: "RWO"
banner:
diff --git a/data/tutorials/platform/0_00_bootstrap_project.md b/data/tutorials/platform/0_00_bootstrap_project.md
index 19185ed2a5..d16e2deff9 100644
--- a/data/tutorials/platform/0_00_bootstrap_project.md
+++ b/data/tutorials/platform/0_00_bootstrap_project.md
@@ -5,6 +5,7 @@ short_title: "Bootstrapping a Project"
description: |
How to set up a project with Dune
category: "Projects"
+language: English
---
[Dune](https://dune.readthedocs.io/en/stable/overview.html) is recommended for bootstrapping projects. To install `dune`, please see [the OCaml install page](/install).
diff --git a/data/tutorials/platform/0_01_managing_deps.md b/data/tutorials/platform/0_01_managing_deps.md
index 2e23b0cdd8..ad6a41d466 100644
--- a/data/tutorials/platform/0_01_managing_deps.md
+++ b/data/tutorials/platform/0_01_managing_deps.md
@@ -4,6 +4,7 @@ title: "Managing Dependencies With opam"
description: |
How to manage dependencies with opam
category: "Projects"
+language: English
---
## Installing Existing Dependencies
diff --git a/data/tutorials/platform/0_02_install_compiler.md b/data/tutorials/platform/0_02_install_compiler.md
index f33cd44fe9..a837c9a135 100644
--- a/data/tutorials/platform/0_02_install_compiler.md
+++ b/data/tutorials/platform/0_02_install_compiler.md
@@ -5,6 +5,7 @@ short_title: "Installing a Specific Compiler Version"
description: |
How to install a specific version of OCaml
category: "Projects"
+language: English
---
> **TL;DR**
diff --git a/data/tutorials/platform/0_03_run_executables_and_tests.md b/data/tutorials/platform/0_03_run_executables_and_tests.md
index 29fc54de73..de943b92b3 100644
--- a/data/tutorials/platform/0_03_run_executables_and_tests.md
+++ b/data/tutorials/platform/0_03_run_executables_and_tests.md
@@ -5,6 +5,7 @@ short_title: "Running Executables and Tests"
description: |
How to run executables and tests with Dune
category: "Projects"
+language: English
---
## Running Executables
diff --git a/data/tutorials/platform/0_09_opam_path.md b/data/tutorials/platform/0_09_opam_path.md
index 9d1b50d98d..c49e89579d 100644
--- a/data/tutorials/platform/0_09_opam_path.md
+++ b/data/tutorials/platform/0_09_opam_path.md
@@ -4,6 +4,7 @@ title: "Running Commands in an opam Switch"
description: |
How to use commands installed in an opam switch
category: "Projects"
+language: English
---
Opam is a package manager for OCaml that facilitates the installation and management of OCaml libraries and tools. When working with opam, it's essential to understand how to run commands within a specific opam switch. In this tutorial, we'll explore three methods: `opam env`, `opam exec`, and `direnv`.
diff --git a/data/tutorials/platform/1_07_ocamlformat.md b/data/tutorials/platform/1_07_ocamlformat.md
index 03ad41ad01..7c866c3a93 100644
--- a/data/tutorials/platform/1_07_ocamlformat.md
+++ b/data/tutorials/platform/1_07_ocamlformat.md
@@ -5,6 +5,7 @@ short_title: "Formatting Your Code"
description: |
How to set up OCamlFormat to automatically format your code
category: "Additional Tooling"
+language: English
---
Automatic formatting with OCamlFormat requires an `.ocamlformat` configuration file at the root of the project.
diff --git a/data/tutorials/platform/2_08_odoc.md b/data/tutorials/platform/2_08_odoc.md
index 05d3dd2db5..fb65c2dee9 100644
--- a/data/tutorials/platform/2_08_odoc.md
+++ b/data/tutorials/platform/2_08_odoc.md
@@ -5,6 +5,7 @@ short_title: "Generating Documentation"
description: |
How to use odoc to generate documentation.
category: "Additional Tooling"
+language: English
---
The documentation rendering tool `odoc` generates documentation
diff --git a/data/tutorials/platform/3_04_create_libraries.md b/data/tutorials/platform/3_04_create_libraries.md
index 7365c5dc19..8082f2f64c 100644
--- a/data/tutorials/platform/3_04_create_libraries.md
+++ b/data/tutorials/platform/3_04_create_libraries.md
@@ -5,6 +5,7 @@ short_title: "Creating Libraries"
description: |
How to create libraries with Dune
category: "Libraries & Packages"
+language: English
---
> **TL;DR**
diff --git a/data/tutorials/platform/3_05_publish_packages.md b/data/tutorials/platform/3_05_publish_packages.md
index de6597d7eb..1a16d0f4ba 100644
--- a/data/tutorials/platform/3_05_publish_packages.md
+++ b/data/tutorials/platform/3_05_publish_packages.md
@@ -5,6 +5,7 @@ short_title: "Publishing a Package"
description: |
How to publish a package with Dune
category: "Libraries & Packages"
+language: English
---
> **TL;DR**
diff --git a/src/global/dune b/src/global/dune
index 7317ddc2bd..0477c441a9 100644
--- a/src/global/dune
+++ b/src/global/dune
@@ -1,3 +1,4 @@
(library
(name ocamlorg)
+ (libraries data_intf)
(public_name ocamlorg.global))
diff --git a/src/global/url.ml b/src/global/url.ml
index 96a3f3635b..9de4376681 100644
--- a/src/global/url.ml
+++ b/src/global/url.ml
@@ -82,7 +82,19 @@ let learn_platform = "/docs/tools"
let tools = "/tools"
let platform = "/platform"
let tool_page name = "/tools/" ^ name
-let tutorial name = "/docs/" ^ name
+let tutorial name (language : Data_intf.Tutorial.language option) =
+ let language_query_param =
+ match language with
+ | None -> ""
+ | Some language ->
+ let language =
+ match language with
+ | English -> "en"
+ | Japanese -> "ja"
+ in
+ "?lang=" ^ language
+ in
+ "/docs/" ^ name ^ language_query_param
let tutorial_search = "/docs/search"
let getting_started = "/docs/get-started"
let installing_ocaml = "/docs/installing-ocaml"
diff --git a/src/ocamlorg_data/data.ml b/src/ocamlorg_data/data.ml
index 556c783b0e..0eb561c3bb 100644
--- a/src/ocamlorg_data/data.ml
+++ b/src/ocamlorg_data/data.ml
@@ -210,7 +210,22 @@ end
module Tutorial = struct
include Tutorial
- let get_by_slug slug = List.find_opt (fun x -> String.equal slug x.slug) all
+ let language_of_query_param =
+ function
+ | "en" -> Some English
+ | "ja" -> Some Japanese
+ | _ -> None
+
+ let get_by_slug_and_language slug language =
+ match List.find_opt (fun x ->
+ String.equal slug x.slug &&
+ (match language with
+ | None -> true
+ | Some language -> equal_language language x.language)) all
+ with
+ | Some x -> Some x
+ | None ->
+ List.find_opt (fun x -> String.equal slug x.slug) all
let search_documents q =
let score_document (doc : search_document) =
diff --git a/src/ocamlorg_data/data.mli b/src/ocamlorg_data/data.mli
index 2ec14439a2..e6b667a2f8 100644
--- a/src/ocamlorg_data/data.mli
+++ b/src/ocamlorg_data/data.mli
@@ -192,11 +192,13 @@ module Tool_page : sig
end
module Tutorial : sig
- include module type of Tutorial
+ include module type of struct include Tutorial end
+
+ val language_of_query_param : string -> language option
val all : t list
val all_search_documents : search_document list
- val get_by_slug : string -> t option
+ val get_by_slug_and_language : string -> language option -> t option
val search_documents : string -> search_document list
end
diff --git a/src/ocamlorg_data/data_intf.ml b/src/ocamlorg_data/data_intf.ml
index 08721ebf54..69ef7599f6 100644
--- a/src/ocamlorg_data/data_intf.ml
+++ b/src/ocamlorg_data/data_intf.ml
@@ -446,6 +446,8 @@ module Tutorial = struct
slug : string;
}
+ type language = English | Japanese [@@deriving show, equal, compare]
+
type t = {
title : string;
short_title : string;
@@ -460,6 +462,7 @@ module Tutorial = struct
body_html : string;
recommended_next_tutorials : recommended_next_tutorials;
prerequisite_tutorials : prerequisite_tutorials;
+ language : language;
}
end
diff --git a/src/ocamlorg_data/dune b/src/ocamlorg_data/dune
index befc1ea06d..eefcc046d2 100644
--- a/src/ocamlorg_data/dune
+++ b/src/ocamlorg_data/dune
@@ -4,7 +4,7 @@
(modules data_intf)
(libraries ptime)
(preprocess
- (pps ppx_deriving_yaml ppx_deriving.show)))
+ (pps ppx_deriving_yaml ppx_deriving.show ppx_compare)))
(library
(name data)
diff --git a/src/ocamlorg_frontend/layouts/learn_layout.eml b/src/ocamlorg_frontend/layouts/learn_layout.eml
index c36a052165..f044aa7435 100644
--- a/src/ocamlorg_frontend/layouts/learn_layout.eml
+++ b/src/ocamlorg_frontend/layouts/learn_layout.eml
@@ -34,24 +34,33 @@ let tabs
let render_sidebar
~current_tutorial
+ ~(language : Data.Tutorial.language option)
~tutorials
~(section: section)
=
let render_tutorial_link ~title ~slug =
- Sidebar.sidebar_link ~title ~href:(Url.tutorial slug) ~current:(current_tutorial = Some (slug))
+ Sidebar.sidebar_link ~title ~href:(Url.tutorial slug language) ~current:(current_tutorial = Some (slug))
in
let render_tutorial (tutorial : Data.Tutorial.t) = render_tutorial_link ~title:tutorial.short_title ~slug:tutorial.slug ~tag:(Option.map (fun (et: Data.Tutorial.external_tutorial) -> et.tag) tutorial.external_tutorial) in
+ let unique lst =
+ let rec unique' acc = function
+ | [] -> List.rev acc
+ | h::t -> unique' (if List.mem h acc then acc else h :: acc) t
+ in
+ unique' [] lst
+ in
let tutorial_sidebar_links_by_category category =
- tutorials |> List.filter (fun (x : Data.Tutorial.t) -> x.category = category) |> List.map render_tutorial |> String.concat "\n"
+ tutorials
+ |> List.filter (fun (x : Data.Tutorial.t) -> x.category = category)
+ |> List.map (fun (x : Data.Tutorial.t) -> x.slug)
+ |> unique
+ |> List.map (fun slug ->
+ Data.Tutorial.get_by_slug_and_language slug language
+ |> Option.get)
+ |> List.map render_tutorial
+ |> String.concat "\n"
in
let categories =
- let unique lst =
- let rec unique' acc = function
- | [] -> List.rev acc
- | h::t -> unique' (if List.mem h acc then acc else h :: acc) t
- in
- unique' [] lst
- in
tutorials |> List.map (fun (tutorial : Data.Tutorial.t) -> tutorial.category) |> unique
in
diff --git a/src/ocamlorg_frontend/pages/install.eml b/src/ocamlorg_frontend/pages/install.eml
index 9561b84ba1..64f880a9a8 100644
--- a/src/ocamlorg_frontend/pages/install.eml
+++ b/src/ocamlorg_frontend/pages/install.eml
@@ -73,7 +73,7 @@ Layout.render
Opam needs to be initialised, which will create a default
- ">opam switch.
+ ">opam switch.
An opam switch is an isolated environment for the OCaml compiler
and any packages you install.
@@ -113,7 +113,7 @@ Layout.render
Now you are ready to write some OCaml code!
- ">Take A Tour of OCaml <%s! Icons.arrow_small_right "h-6 w-6" %>
+ ">Take A Tour of OCaml <%s! Icons.arrow_small_right "h-6 w-6" %>
@@ -129,7 +129,7 @@ Layout.render
OCaml's package manager, opam, supports Windows natively since version 2.2.0 and is the recommended way to install OCaml on Windows.
If you are looking for a different installation method, check out the alternative installation instructions
- provided in the ">"OCaml on Windows" guide.
+ provided in the ">"OCaml on Windows" guide.
@@ -145,7 +145,7 @@ Layout.render
Now, we are ready to initialise opam, which will create a default
- ">opam switch.
+ ">opam switch.
An opam switch is an isolated environment for the OCaml compiler
and any packages you install.
@@ -173,7 +173,7 @@ Layout.render
<%s! Copy_to_clipboard.small_code_snippet ~id:"powershell-activate" "(& opam env) -split '\\r?\\n' | ForEach-Object { Invoke-Expression $_ }" %>
- Opam initialisation may take several minutes. Start ">A Tour of OCaml while waiting.
+ Opam initialisation may take several minutes. Start ">A Tour of OCaml while waiting.
@@ -197,7 +197,7 @@ Layout.render
Now you are ready to write some OCaml code!
- ">Take A Tour of OCaml <%s! Icons.arrow_small_right "h-6 w-6" %>
+ ">Take A Tour of OCaml <%s! Icons.arrow_small_right "h-6 w-6" %>
diff --git a/src/ocamlorg_frontend/pages/is_ocaml_yet.eml b/src/ocamlorg_frontend/pages/is_ocaml_yet.eml
index 1ba261882a..945f0224bd 100644
--- a/src/ocamlorg_frontend/pages/is_ocaml_yet.eml
+++ b/src/ocamlorg_frontend/pages/is_ocaml_yet.eml
@@ -1,6 +1,6 @@
let left_sidebar ~tutorials ~current_tutorial =
- <%s! Learn_layout.render_sidebar ~tutorials ~current_tutorial ~section:Learn_layout.Guides %>
+ <%s! Learn_layout.render_sidebar ~tutorials ~current_tutorial ~language:None ~section:Learn_layout.Guides %>
let render
diff --git a/src/ocamlorg_frontend/pages/learn.eml b/src/ocamlorg_frontend/pages/learn.eml
index feec9c9966..e6a4be7771 100644
--- a/src/ocamlorg_frontend/pages/learn.eml
+++ b/src/ocamlorg_frontend/pages/learn.eml
@@ -63,20 +63,20 @@ Learn_layout.single_column_layout
~icon:Learn_components.beginner_section_icon ~title:"GET STARTED" ~heading:"Introduction To OCaml"
~description:"Install OCaml and gain a high-level understanding of the language"
~tutorial_links:[
- {href = Url.tutorial "installing-ocaml"; title = "Installing OCaml"};
- {href = Url.tutorial "tour-of-ocaml"; title = "A Tour of OCaml"};
- {href = Url.tutorial "your-first-program"; title = "Your First OCaml Program"};
- {href = Url.tutorial "opam-switch-introduction"; title = "Introduction to opam Switches"}
+ {href = Url.tutorial "installing-ocaml" None; title = "Installing OCaml"};
+ {href = Url.tutorial "tour-of-ocaml" None; title = "A Tour of OCaml"};
+ {href = Url.tutorial "your-first-program" None; title = "Your First OCaml Program"};
+ {href = Url.tutorial "opam-switch-introduction" None; title = "Introduction to opam Switches"}
]
~see_more:{href = Url.getting_started; title = "Get Started"}
%>
<%s! Learn_components.tutorial_block ~icon:Learn_components.beginner_section_icon ~title:"LANGUAGE" ~heading:"The OCaml Language"
~description:"An in-depth explanation of language features and data structures from the Standard Library"
~tutorial_links:[
- {href = Url.tutorial "values-and-functions"; title = "Values and Functions"};
- {href = Url.tutorial "basic-data-types"; title = "Data Types and Pattern Matching"};
- {href = Url.tutorial "lists"; title = "Lists"};
- {href = Url.tutorial "loops-recursion"; title = "Loops and Recursions"};
+ {href = Url.tutorial "values-and-functions" None; title = "Values and Functions"};
+ {href = Url.tutorial "basic-data-types" None; title = "Data Types and Pattern Matching"};
+ {href = Url.tutorial "lists" None; title = "Lists"};
+ {href = Url.tutorial "loops-recursion" None; title = "Loops and Recursions"};
]
~see_more:{href = Url.learn_language; title = "Language Documentation"}
%>
@@ -132,20 +132,20 @@ Learn_layout.single_column_layout
<%s! Learn_components.tutorial_block ~icon:Learn_components.intermediate_section_icon ~title:"GUIDES" ~heading:"Practical-Minded Tutorials and Guides"
~description:"How to solve real-world problems in OCaml"
~tutorial_links:[
- {href = Url.tutorial "formatting-text"; title = "Formatting and Wrapping Text"};
- {href = Url.tutorial "debugging"; title = "Debugging"};
- {href = Url.tutorial "error-handling"; title = "Error Handling"};
- {href = Url.tutorial "profiling"; title = "Profiling"}
+ {href = Url.tutorial "formatting-text" None; title = "Formatting and Wrapping Text"};
+ {href = Url.tutorial "debugging" None; title = "Debugging"};
+ {href = Url.tutorial "error-handling" None; title = "Error Handling"};
+ {href = Url.tutorial "profiling" None; title = "Profiling"}
]
~see_more:{href = Url.learn_guides; title = "See More Guides"}
%>
<%s! Learn_components.tutorial_block ~icon:Learn_components.intermediate_section_icon ~title:"PLATFORM" ~heading:"The OCaml Platform"
~description:"Learn to leverage the tooling around OCaml and create your own projects and libraries"
~tutorial_links:[
- {href = Url.tutorial "bootstrapping-a-dune-project"; title = "Bootstrapping a Project"};
- {href = Url.tutorial "managing-dependencies"; title = "Managing Dependencies"};
- {href = Url.tutorial "install-a-specific-ocaml-compiler-version"; title = "Install a Specific Compiler Version"};
- {href = Url.tutorial "set-up-editor"; title = "Configuring Your Editor"}
+ {href = Url.tutorial "bootstrapping-a-dune-project" None; title = "Bootstrapping a Project"};
+ {href = Url.tutorial "managing-dependencies" None; title = "Managing Dependencies"};
+ {href = Url.tutorial "install-a-specific-ocaml-compiler-version" None; title = "Install a Specific Compiler Version"};
+ {href = Url.tutorial "set-up-editor" None; title = "Configuring Your Editor"}
]
~see_more:{href = Url.learn_platform; title = "Platform Tools Documentation"}
%>
diff --git a/src/ocamlorg_frontend/pages/tutorial.eml b/src/ocamlorg_frontend/pages/tutorial.eml
index 7584407573..565dd88f85 100644
--- a/src/ocamlorg_frontend/pages/tutorial.eml
+++ b/src/ocamlorg_frontend/pages/tutorial.eml
@@ -13,9 +13,9 @@ let right_sidebar
=
<%s! Toc.render (List.map tutorial_toc_to_toc tutorial.toc) %>
-let left_sidebar ~tutorials ~current_tutorial ~section =
+let left_sidebar ~tutorials ~current_tutorial ~language ~section =
- <%s! Learn_layout.render_sidebar ~tutorials ~current_tutorial ~section %>
+ <%s! Learn_layout.render_sidebar ~tutorials ~current_tutorial ~language ~section %>
let of_tutorial_section (s: Data.Tutorial.section) =
@@ -61,6 +61,7 @@ commit_hash
~recommended_next_tutorials
~prerequisite_tutorials
~canonical
+~language
=
let href, description = match tutorial.external_tutorial with
| None ->
@@ -81,7 +82,7 @@ Learn_layout.three_column_layout
~title:(Printf.sprintf "%s · OCaml Documentation" tutorial.short_title)
~description:tutorial.description
~canonical
-~left_sidebar_html:(Some(left_sidebar ~current_tutorial:(Some tutorial.slug) ~tutorials ~section:(of_tutorial_section tutorial.section)))
+~left_sidebar_html:(Some(left_sidebar ~current_tutorial:(Some tutorial.slug) ~language ~tutorials ~section:(of_tutorial_section tutorial.section)))
~right_sidebar_html:(Some(right_sidebar tutorial))
~current:(of_tutorial_section tutorial.section) @@
diff --git a/src/ocamlorg_web/lib/handler.ml b/src/ocamlorg_web/lib/handler.ml
index aad6ef63b9..6fe0d50738 100644
--- a/src/ocamlorg_web/lib/handler.ml
+++ b/src/ocamlorg_web/lib/handler.ml
@@ -26,28 +26,28 @@ let learn_get_started req =
Data.Tutorial.all
|> List.filter (fun (t : Data.Tutorial.t) -> t.section = GetStarted)
in
- Dream.redirect req (Url.tutorial (List.hd tutorials).slug)
+ Dream.redirect req (Url.tutorial (List.hd tutorials).slug None)
let learn_language req =
let tutorials =
Data.Tutorial.all
|> List.filter (fun (t : Data.Tutorial.t) -> t.section = Language)
in
- Dream.redirect req (Url.tutorial (List.hd tutorials).slug)
+ Dream.redirect req (Url.tutorial (List.hd tutorials).slug None)
let learn_guides req =
let tutorials =
Data.Tutorial.all
|> List.filter (fun (t : Data.Tutorial.t) -> t.section = Guides)
in
- Dream.redirect req (Url.tutorial (List.hd tutorials).slug)
+ Dream.redirect req (Url.tutorial (List.hd tutorials).slug None)
let learn_platform req =
let tutorials =
Data.Tutorial.all
|> List.filter (fun (t : Data.Tutorial.t) -> t.section = Platform)
in
- Dream.redirect req (Url.tutorial (List.hd tutorials).slug)
+ Dream.redirect req (Url.tutorial (List.hd tutorials).slug None)
let community _req =
let query = Dream.query _req "e" in
@@ -718,7 +718,12 @@ let tool_page commit_hash req =
let tutorial commit_hash req =
let slug = Dream.param req "id" in
- let>? tutorial = Data.Tutorial.get_by_slug slug in
+ let language =
+ Option.bind
+ (Dream.query req "lang")
+ Data.Tutorial.language_of_query_param
+ in
+ let>? tutorial = Data.Tutorial.get_by_slug_and_language slug language in
let all_tutorials = Data.Tutorial.all in
let tutorials =
@@ -748,8 +753,9 @@ let tutorial commit_hash req =
Dream.html
(Ocamlorg_frontend.tutorial commit_hash ~tutorials
- ~canonical:(Url.tutorial tutorial.slug)
+ ~canonical:(Url.tutorial tutorial.slug None)
~related_exercises ~recommended_next_tutorials ~prerequisite_tutorials
+ ~language
tutorial)
let exercises req =
diff --git a/src/ocamlorg_web/lib/redirection.ml b/src/ocamlorg_web/lib/redirection.ml
index 3e454be805..85e922c094 100644
--- a/src/ocamlorg_web/lib/redirection.ml
+++ b/src/ocamlorg_web/lib/redirection.ml
@@ -86,59 +86,59 @@ let from_v2 =
("/learn/teaching-ocaml.html", Url.academic_users);
("/learn/tutorials/99problems.html", Url.exercises);
( "/learn/tutorials/a_first_hour_with_ocaml.html",
- Url.tutorial "tour-of-ocaml" );
+ Url.tutorial "tour-of-ocaml" None );
( "/learn/tutorials/calling_c_libraries.html",
- Url.tutorial "calling-c-libraries" );
+ Url.tutorial "calling-c-libraries" None );
( "/learn/tutorials/calling_fortran_libraries.html",
- Url.tutorial "calling-fortran-libraries" );
+ Url.tutorial "calling-fortran-libraries" None );
("/learn/tutorials/camlp5.html", Url.learn);
( "/learn/tutorials/command-line_arguments.ja.html",
- Url.tutorial "cli-arguments" );
+ Url.tutorial "cli-arguments" None );
( "/learn/tutorials/command-line_arguments.html",
- Url.tutorial "cli-arguments" );
+ Url.tutorial "cli-arguments" None );
( "/learn/tutorials/command-line_arguments.zh.html",
- Url.tutorial "cli-arguments" );
+ Url.tutorial "cli-arguments" None );
( "/learn/tutorials/common_error_messages.fr.html",
- Url.tutorial "common-errors" );
+ Url.tutorial "common-errors" None );
( "/learn/tutorials/common_error_messages.ja.html",
- Url.tutorial "common-errors" );
- ("/learn/tutorials/common_error_messages.html", Url.tutorial "common-errors");
+ Url.tutorial "common-errors" None );
+ ("/learn/tutorials/common_error_messages.html", Url.tutorial "common-errors" None);
( "/learn/tutorials/common_error_messages.zh.html",
- Url.tutorial "common-errors" );
+ Url.tutorial "common-errors" None );
( "/learn/tutorials/comparison_of_standard_containers.ja.html",
- Url.tutorial "data-structures-comparison" );
+ Url.tutorial "data-structures-comparison" None );
( "/learn/tutorials/comparison_of_standard_containers.ko.html",
- Url.tutorial "data-structures-comparison" );
+ Url.tutorial "data-structures-comparison" None );
( "/learn/tutorials/comparison_of_standard_containers.html",
- Url.tutorial "data-structures-comparison" );
+ Url.tutorial "data-structures-comparison" None );
( "/learn/tutorials/comparison_of_standard_containers.zh.html",
- Url.tutorial "data-structures-comparison" );
+ Url.tutorial "data-structures-comparison" None );
( "/learn/tutorials/compiling_ocaml_projects.ja.html",
- Url.tutorial "using-the-ocaml-compiler-toolchain" );
- ( Url.tutorial "compiling-ocaml-projects",
- Url.tutorial "using-the-ocaml-compiler-toolchain" );
+ Url.tutorial "using-the-ocaml-compiler-toolchain" None );
+ ( Url.tutorial "compiling-ocaml-projects" None,
+ Url.tutorial "using-the-ocaml-compiler-toolchain" None );
( "/learn/tutorials/compiling_ocaml_projects.html",
- Url.tutorial "using-the-ocaml-compiler-toolchain" );
+ Url.tutorial "using-the-ocaml-compiler-toolchain" None );
( "/learn/tutorials/data_types_and_matching.fr.html",
- Url.tutorial "basic-data-types" );
+ Url.tutorial "basic-data-types" None );
( "/learn/tutorials/data_types_and_matching.it.html",
- Url.tutorial "basic-data-types" );
+ Url.tutorial "basic-data-types" None );
( "/learn/tutorials/data_types_and_matching.ja.html",
- Url.tutorial "basic-data-types" );
+ Url.tutorial "basic-data-types" None );
( "/learn/tutorials/data_types_and_matching.html",
- Url.tutorial "basic-data-types" );
+ Url.tutorial "basic-data-types" None );
( "/learn/tutorials/data_types_and_matching.zh.html",
- Url.tutorial "basic-data-types" );
- (Url.tutorial "data-types", Url.tutorial "basic-data-types");
- ("/learn/tutorials/debug.html", Url.tutorial "debugging");
- ("/learn/tutorials/error_handling.html", Url.tutorial "error-handling");
+ Url.tutorial "basic-data-types" None );
+ (Url.tutorial "data-types" None, Url.tutorial "basic-data-types" None);
+ ("/learn/tutorials/debug.html", Url.tutorial "debugging" None);
+ ("/learn/tutorials/error_handling.html", Url.tutorial "error-handling" None);
( "/learn/tutorials/file_manipulation.ja.html",
- Url.tutorial "file-manipulation" );
- ("/learn/tutorials/file_manipulation.html", Url.tutorial "file-manipulation");
+ Url.tutorial "file-manipulation" None );
+ ("/learn/tutorials/file_manipulation.html", Url.tutorial "file-manipulation" None);
( "/learn/tutorials/file_manipulation.zh.html",
- Url.tutorial "file-manipulation" );
- ("/learn/tutorials/format.fr.html", Url.tutorial "formatting-text");
- ("/learn/tutorials/format.html", Url.tutorial "formatting-text");
+ Url.tutorial "file-manipulation" None );
+ ("/learn/tutorials/format.fr.html", Url.tutorial "formatting-text" None);
+ ("/learn/tutorials/format.html", Url.tutorial "formatting-text" None);
(* FIXME: uncomment when higher-order-functions is merged (
"/learn/tutorials/functional_programming.fr.html", Url.tutorial
"higher-order-functions" ); (
@@ -150,32 +150,32 @@ let from_v2 =
"higher-order-functions" ); (
"/learn/tutorials/functional_programming.zh.html", Url.tutorial
"higher-order-functions" ); *)
- ("/learn/tutorials/functors.html", Url.tutorial "functors");
+ ("/learn/tutorials/functors.html", Url.tutorial "functors" None);
( "/learn/tutorials/garbage_collection.ja.html",
- Url.tutorial "garbage-collection" );
+ Url.tutorial "garbage-collection" None );
( "/learn/tutorials/garbage_collection.html",
- Url.tutorial "garbage-collection" );
+ Url.tutorial "garbage-collection" None );
( "/learn/tutorials/garbage_collection.zh.html",
- Url.tutorial "garbage-collection" );
- ("/learn/tutorials/guidelines.html", Url.tutorial "guidelines");
- ("/learn/tutorials/hashtbl.ja.html", Url.tutorial "hash-tables");
- ("/learn/tutorials/hashtbl.html", Url.tutorial "hash-tables");
- ("/learn/tutorials/hashtbl.zh.html", Url.tutorial "hash-tables");
+ Url.tutorial "garbage-collection" None );
+ ("/learn/tutorials/guidelines.html", Url.tutorial "guidelines" None);
+ ("/learn/tutorials/hashtbl.ja.html", Url.tutorial "hash-tables" None);
+ ("/learn/tutorials/hashtbl.html", Url.tutorial "hash-tables" None);
+ ("/learn/tutorials/hashtbl.zh.html", Url.tutorial "hash-tables" None);
("/learn/tutorials/humor_proof.html", Url.learn);
( "/learn/tutorials/if_statements_loops_and_recursion.fr.html",
- Url.tutorial "loops-recursion" );
+ Url.tutorial "loops-recursion" None );
( "/learn/tutorials/if_statements_loops_and_recursion.it.html",
- Url.tutorial "loops-recursion" );
+ Url.tutorial "loops-recursion" None );
( "/learn/tutorials/if_statements_loops_and_recursion.ja.html",
- Url.tutorial "loops-recursion" );
+ Url.tutorial "loops-recursion" None );
( "/learn/tutorials/if_statements_loops_and_recursion.ko.html",
- Url.tutorial "loops-recursion" );
+ Url.tutorial "loops-recursion" None );
( "/learn/tutorials/if_statements_loops_and_recursion.html",
- Url.tutorial "loops-recursion" );
+ Url.tutorial "loops-recursion" None );
( "/learn/tutorials/if_statements_loops_and_recursion.zh.html",
- Url.tutorial "loops-recursion" );
+ Url.tutorial "loops-recursion" None );
( "/learn/tutorials/mutability-loops-and-imperative",
- Url.tutorial "mutability-imperative-control-flow" );
+ Url.tutorial "mutability-imperative-control-flow" None );
("/learn/tutorials/index.de.html", Url.learn);
("/learn/tutorials/index.fr.html", Url.learn);
("/learn/tutorials/index.it.html", Url.learn);
@@ -185,34 +185,34 @@ let from_v2 =
("/learn/tutorials", Url.learn);
("/learn/tutorials/index.zh.html", Url.learn);
("/learn/tutorials/introduction_to_gtk.html", Url.learn);
- ("/learn/tutorials/labels.ja.html", Url.tutorial "labels");
- ("/learn/tutorials/labels.html", Url.tutorial "labels");
- ("/learn/tutorials/labels.zh.html", Url.tutorial "labels");
- ("/learn/tutorials/lists.html", Url.tutorial "lists");
- ("/learn/tutorials/map.fr.html", Url.tutorial "map");
- ("/learn/tutorials/map.ja.html", Url.tutorial "map");
- ("/learn/tutorials/map.html", Url.tutorial "map");
- ("/learn/tutorials/map.zh.html", Url.tutorial "map");
- ("/learn/tutorials/modules.fr.html", Url.tutorial "modules");
- ("/learn/tutorials/modules.ja.html", Url.tutorial "modules");
- ("/learn/tutorials/modules.ko.html", Url.tutorial "modules");
- ("/learn/tutorials/modules.html", Url.tutorial "modules");
- ("/learn/tutorials/modules.zh.html", Url.tutorial "modules");
- ("/learn/tutorials/objects.ja.html", Url.tutorial "objects");
- ("/learn/tutorials/objects.html", Url.tutorial "objects");
- ("/learn/tutorials/objects.zh.html", Url.tutorial "objects");
+ ("/learn/tutorials/labels.ja.html", Url.tutorial "labels" None);
+ ("/learn/tutorials/labels.html", Url.tutorial "labels" None);
+ ("/learn/tutorials/labels.zh.html", Url.tutorial "labels" None);
+ ("/learn/tutorials/lists.html", Url.tutorial "lists" None);
+ ("/learn/tutorials/map.fr.html", Url.tutorial "map" None);
+ ("/learn/tutorials/map.ja.html", Url.tutorial "map" None);
+ ("/learn/tutorials/map.html", Url.tutorial "map" None);
+ ("/learn/tutorials/map.zh.html", Url.tutorial "map" None);
+ ("/learn/tutorials/modules.fr.html", Url.tutorial "modules" None);
+ ("/learn/tutorials/modules.ja.html", Url.tutorial "modules" None);
+ ("/learn/tutorials/modules.ko.html", Url.tutorial "modules" None);
+ ("/learn/tutorials/modules.html", Url.tutorial "modules" None);
+ ("/learn/tutorials/modules.zh.html", Url.tutorial "modules" None);
+ ("/learn/tutorials/objects.ja.html", Url.tutorial "objects" None);
+ ("/learn/tutorials/objects.html", Url.tutorial "objects" None);
+ ("/learn/tutorials/objects.zh.html", Url.tutorial "objects" None);
( "/learn/tutorials/performance_and_profiling.ja.html",
- Url.tutorial "profiling" );
- ("/learn/tutorials/performance_and_profiling.html", Url.tutorial "profiling");
+ Url.tutorial "profiling" None );
+ ("/learn/tutorials/performance_and_profiling.html", Url.tutorial "profiling" None);
( "/learn/tutorials/performance_and_profiling_discussion.html",
- Url.tutorial "profiling" );
- ("/learn/tutorials/set.fr.html", Url.tutorial "sets");
- ("/learn/tutorials/set.ja.html", Url.tutorial "sets");
- ("/learn/tutorials/set.html", Url.tutorial "sets");
- ("/learn/tutorials/set.zh.html", Url.tutorial "sets");
- ("/learn/tutorials/streams.html", Url.tutorial "sequences");
- ("/learn/tutorials/up_and_running.html", Url.tutorial "up-and-running");
- (Url.tutorial "first-hour", Url.tutorial "tour-of-ocaml");
+ Url.tutorial "profiling" None );
+ ("/learn/tutorials/set.fr.html", Url.tutorial "sets" None);
+ ("/learn/tutorials/set.ja.html", Url.tutorial "sets" None);
+ ("/learn/tutorials/set.html", Url.tutorial "sets" None);
+ ("/learn/tutorials/set.zh.html", Url.tutorial "sets" None);
+ ("/learn/tutorials/streams.html", Url.tutorial "sequences" None);
+ ("/learn/tutorials/up_and_running.html", Url.tutorial "up-and-running" None);
+ (Url.tutorial "first-hour" None, Url.tutorial "tour-of-ocaml" None);
("/meetings/index.fr.html", Url.conferences);
("/meetings/index.html", Url.conferences);
("/meetings", Url.conferences);
@@ -251,7 +251,7 @@ let from_v2 =
("/docs/platform-principles", Url.tool_page "platform-principles");
("/docs/platform-users", Url.tool_page "platform-users");
("/docs/platform-roadmap", Url.tool_page "platform-roadmap");
- ("/docs/configuring-your-editor", Url.tutorial "set-up-editor");
+ ("/docs/configuring-your-editor", Url.tutorial "set-up-editor" None);
]
let make ?(permanent = false) t =
diff --git a/src/ocamlorg_web/lib/router.ml b/src/ocamlorg_web/lib/router.ml
index 4fa9df8910..c9bd41ff88 100644
--- a/src/ocamlorg_web/lib/router.ml
+++ b/src/ocamlorg_web/lib/router.ml
@@ -67,10 +67,10 @@ let page_routes t =
Dream.get Url.tools Handler.tools;
Dream.get Url.platform Handler.tools_platform;
Dream.get (Url.tool_page ":id") (Handler.tool_page Commit.hash);
- Dream.get (Url.tutorial "is-ocaml-web-yet") (Handler.is_ocaml_yet t "web");
- Dream.get (Url.tutorial "is-ocaml-gui-yet") (Handler.is_ocaml_yet t "gui");
+ Dream.get (Url.tutorial "is-ocaml-web-yet" None) (Handler.is_ocaml_yet t "web");
+ Dream.get (Url.tutorial "is-ocaml-gui-yet" None) (Handler.is_ocaml_yet t "gui");
Dream.get Url.tutorial_search Handler.learn_documents_search;
- Dream.get (Url.tutorial ":id") (Handler.tutorial Commit.hash);
+ Dream.get (Url.tutorial ":id" None) (Handler.tutorial Commit.hash);
Dream.get Url.playground Handler.playground;
Dream.get Url.logos Handler.logos;
]
diff --git a/src/ocamlorg_web/lib/sitemap.ml b/src/ocamlorg_web/lib/sitemap.ml
index 1a81ab32ff..7baad4d80e 100644
--- a/src/ocamlorg_web/lib/sitemap.ml
+++ b/src/ocamlorg_web/lib/sitemap.ml
@@ -55,7 +55,7 @@ let urlables =
Urlable (Release.all, fun r -> to_url @@ Url.release r.version);
Urlable (Success_story.all, fun r -> to_url @@ Url.success_story r.slug);
Urlable (Tool_page.all, fun r -> to_url @@ Url.tool_page r.slug);
- Urlable (Tutorial.all, fun r -> to_url @@ Url.tutorial r.slug);
+ Urlable (Tutorial.all, fun r -> to_url @@ Url.tutorial r.slug None);
Urlable (Conference.all, fun r -> to_url @@ Url.conference r.slug);
]
diff --git a/tool/ood-gen/lib/tutorial.ml b/tool/ood-gen/lib/tutorial.ml
index 9ae7239273..864152470a 100644
--- a/tool/ood-gen/lib/tutorial.ml
+++ b/tool/ood-gen/lib/tutorial.ml
@@ -34,6 +34,24 @@ type search_document_section =
type search_document = [%import: Data_intf.Tutorial.search_document]
[@@deriving show]
+type language = [%import: Data_intf.Tutorial.language] [@@deriving show]
+
+(* Deriving of_yaml doesn't seem to work in an intuitive way for regular variant;
+ in order to have a type like [language] to be parsed, we need to write
+ something like:
+
+ {v
+ language:
+ English: []
+ v}
+
+ So we instead write a custom parser to get the behavior we want. *)
+let language_of_yaml : Yaml.value -> (language, _) result =
+ function
+ | `String "English" -> Ok English
+ | `String "Japanese" -> Ok Japanese
+ | value -> Error (`Msg ("Unexpected language value: " ^ Yaml.to_string_exn value))
+
type t = [%import: Data_intf.Tutorial.t] [@@deriving show]
type metadata = {
@@ -45,6 +63,7 @@ type metadata = {
external_tutorial : external_tutorial option;
recommended_next_tutorials : recommended_next_tutorials option;
prerequisite_tutorials : prerequisite_tutorials option;
+ language : language;
}
[@@deriving
of_yaml,
@@ -103,7 +122,12 @@ let decode (fpath, (head, body_md)) =
let all () =
Utils.map_md_files decode "tutorials/*/*.md"
- |> List.sort (fun t1 t2 -> String.compare t1.fpath t2.fpath)
+ |> Base.List.sort_and_group
+ ~compare:(fun t1 t2 -> String.compare t1.slug t2.slug)
+ |> List.map
+ (List.sort (fun (t1 : t) t2 -> Data_intf.Tutorial.compare_language t1.language t2.language))
+ |> List.sort (fun t1 t2 -> String.compare (List.hd t1).fpath (List.hd t2).fpath)
+ |> List.concat
module TutorialSearch = struct
let document_from_section