Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 0 additions & 32 deletions .docker/README.md

This file was deleted.

2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.docker
compose
.dockerignore
.gitignore
.git
Expand Down
41 changes: 40 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,9 +1,48 @@
# Copy this file to .env and fill in SECRET_KEY.
# Generate one with:
# python -c "import secrets; print(secrets.token_urlsafe(50))"

# === Required ===
SECRET_KEY=

# Optional overrides (defaults shown):
# === Core Django ===
# DEBUG=True
# ALLOWED_HOSTS=*
# CSRF_TRUSTED_ORIGINS=http://*

# === Database (defaults to SQLite at BASE_DIR/db.sqlite3) ===
# DB_ENGINE=django.db.backends.postgresql
# DB_NAME=
# DB_USER=
# DB_PASSWORD=
# DB_HOST=
# DB_PORT=

# === Storage backends ===
# FILE_STORAGE=django.core.files.storage.FileSystemStorage
# STATICFILES_STORAGE=django.contrib.staticfiles.storage.StaticFilesStorage

# === S3 (only read when FILE_STORAGE or STATICFILES_STORAGE points at the S3 backend) ===
# S3_ACCESS_KEY_ID=
# S3_SECRET_ACCESS_KEY=
# S3_REGION_NAME=
# S3_PUBLIC_BUCKET=
# S3_PRIVATE_BUCKET=
# S3_ENDPOINT_URL=
# S3_CUSTOM_DOMAIN=
# S3_DEFAULT_ACL=
# S3_USE_SSL=True
# S3_PUBLIC_BUCKET_LOCATION=static
# S3_PRIVATE_BUCKET_LOCATION=
# AWS_QUERYSTRING_AUTH=False

# === Google Cloud Storage (only read when FILE_STORAGE or STATICFILES_STORAGE
# points at storages.backends.gcloud.GoogleCloudStorage; also set
# GOOGLE_APPLICATION_CREDENTIALS to the key file path) ===
# GS_PUBLIC_BUCKET_NAME=
# GS_PRIVATE_BUCKET_NAME=
# GS_DEFAULT_ACL=
# GS_CUSTOM_ENDPOINT=
# GS_PUBLIC_BUCKET_LOCATION=static
# GS_PRIVATE_BUCKET_LOCATION=
# GS_QUERYSTRING_AUTH=False
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ python manage.py runserver 0.0.0.0:8080
For container-based dev use **podman compose** (not `docker compose`):

```bash
cd .docker
cd compose
cp .env.template .env && cp .env.web.template .env.web
podman compose up -d
podman compose exec app python manage.py createsuperuser
Expand Down
51 changes: 32 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,45 @@
# Kaplan Cloud

Kaplan Cloud is a cloud-based translation management system.
Kaplan Cloud is a cloud-based translation management system. It handles
translation projects, source and bilingual files, translation memories,
terminology databases, and team collaboration between project managers,
translators, and reviewers.

The official documentation is available at https://docs.kaplan.pro/projects/kaplan-cloud
User-facing documentation is published at
<https://docs.kaplan.pro/projects/kaplan-cloud>.

## Local installation with Docker
## Quick start (local development)

Please see [here](https://docs.kaplan.pro/projects/kaplan-cloud/en/latest/installation.html#local-installation-with-docker)
for instructions; however, for testing purposes, all you need to do is first
start a [Kaplan Cloud container](https://hub.docker.com/r/kaplanpro/cloud):
Requires Python 3.12+ and [`uv`](https://docs.astral.sh/uv/). SQLite is
used by default, so no separate database setup is needed.

```
docker run -d \
-p 8080:8080 \
--restart always \
--name kaplan-cloud \
kaplanpro/cloud
```bash
cp .env.example .env # then fill in SECRET_KEY
uv sync
python manage.py migrate
python manage.py runserver 0.0.0.0:8080
```

And then create a superuser account:
`.env.example` documents every environment variable `settings.py`
reads — uncomment the ones you need (Postgres, S3, GCS, etc.).

```
docker exec -it kaplan-cloud python manage.py createsuperuser
## Container deployment

A reference `podman compose` (or `docker compose`) stack lives in
[`compose/`](compose/README.md). It bundles Postgres, the Gunicorn app
server, and an Nginx static-files sidecar designed to sit behind an
external reverse proxy.

## Tests and linting

```bash
python manage.py test
ruff check .
ruff format .
```

That's it! Head on over to http://0.0.0.0:8080 and explore Kaplan Cloud.
CI runs `ruff check` and the full test suite on every PR.

## Production installation with Docker Compose
## License

Please see [here](https://docs.kaplan.pro/projects/kaplan-cloud/en/latest/installation.html#production-installation-with-docker-compose)
for instructions.
See [`LICENSE`](LICENSE).
File renamed without changes.
File renamed without changes.
47 changes: 47 additions & 0 deletions compose/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Container deployment

A reference `compose` setup for running Kaplan Cloud in production with
Postgres, Gunicorn, and an Nginx static-files sidecar. For local
development, see the root [`README.md`](../README.md) instead.

## Prerequisites

- `podman` (or `docker`) with a compose plugin. The project's canonical
command is `podman compose`; `docker compose` works identically.
- A reverse proxy in front of this stack to terminate TLS and route
traffic to the `nginx` service. The compose file defaults to the
network used by
[`nginxproxy/nginx-proxy`](https://hub.docker.com/r/nginxproxy/nginx-proxy);
override `NETWORK_NAME` in `.env` if you're using something else.

## Setup

From this directory:

```bash
cp .env.template .env # fill in DB_NAME, DB_USER, DB_PASSWORD, etc.
cp .env.web.template .env.web # set VIRTUAL_HOST
podman compose up -d
podman compose exec app python manage.py createsuperuser
```

The Postgres data directory and uploaded project files are bind-mounted
under `./db/data/db` and `./app/projects` respectively, so state persists
across container restarts and image upgrades.

## Inviting teammates

Sign in to `/admin` with the superuser account and create a
`UserRegistrationToken` for each new teammate. The token is shown once
and is single-use — share the registration URL
`https://<your-host>/accounts/register?token=<TOKEN>` with the recipient.
Permissions (translator, reviewer, PM) are assigned automatically based
on the user type chosen when the token was created.

Only admins and members of the `PM` group can create projects, clients,
translation memories, and language profiles.

## Links

- [`kaplanpro/cloud` on Docker Hub](https://hub.docker.com/r/kaplanpro/cloud)
- [kaplan.pro](https://kaplan.pro)
File renamed without changes.
File renamed without changes.
Binary file modified docs/source/_static/img/assign-team-member-form.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/source/_static/img/files-context-menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed docs/source/_static/img/gcp-cloud-shell.png
Binary file not shown.
Binary file modified docs/source/_static/img/project-form.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/source/_static/img/projects-tab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/source/_static/img/team-member-assigned.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/source/_static/img/translation-memories-tab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/source/_static/img/translation-memory-form.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/source/_static/img/user-registration-form.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 61 additions & 0 deletions docs/source/analysis-and-reports.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
Analysis and reports
====================

Project analysis estimates how much work a project contains: total
words, segments, and how many of them are already covered by existing
translation memories (exact and fuzzy matches). PMs trigger analyses
from the project page; results are saved as **reports** that can be
viewed later.

This page is for project managers and administrators.

Run an analysis
---------------

1. Open the project at ``/project/<uuid>``.
2. Right-click the file table (or use the dedicated action button) and
pick **Analyze**. You can analyze the selected files or the whole
project if no files are ticked.
3. A new row appears at the top of the **Reports** section in
**Ready for Processing** state.

The Reports table
-----------------

The Reports section on the project page lists every analysis ever run
for the project, newest first. Each row has a live status badge:

- **Blank** — created but not yet queued.
- **Ready for Processing** — queued, waiting for the worker.
- **Processing** — actively being computed.
- **Complete** — ready to view.

The page polls the server every few seconds and updates the badge in
place as the analysis advances; you don't need to refresh manually.
(New in v0.6.0 — previously the page had to be reloaded.)

When a report reaches **Complete**, click its row to open it at
``/report/<uuid>``.

Reading a report
----------------

A completed report lists, per file:

- Total words and segments.
- Exact-match counts from each attached translation memory.
- Fuzzy-match buckets (e.g. 99–95%, 94–85%, 84–75%) and how many
segments fall into each.
- New words — those without any TM match.

PMs typically use this breakdown to estimate translation effort, quote
clients, and decide which files (if any) warrant pre-translation from
TM.

Downloading files
-----------------

When work is complete, the file table on the project page lets you
download the translated output of each file individually, or export
the entire project as an archive. Choose the export action from the
right-click menu or the action buttons on the file row.
69 changes: 69 additions & 0 deletions docs/source/creating-projects.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
Creating projects
=================

This page is for **project managers** (members of the ``PM`` group) and
administrators. If you're a translator or reviewer, you'll receive
projects from a PM and won't see these screens.

Create a project
----------------

1. From the project list, click the **Create** button to open the new
project form at ``/project/new``.

.. image:: ./_static/img/project-form.png
:alt: New project form

2. Fill in the project metadata:

- **Name** and **due date**.
- **Client** — pick from existing clients, or create one ahead of
time from the Django admin.
- **Source language** and one or more **target languages** — these
are :doc:`language profiles <language-profiles>` that an admin
has set up.
- **Translation memories** to attach to the project. Optional;
attached TMs will surface as matches in the editor.

3. Upload your files. Kaplan Cloud accepts:

- **Source files** in the formats supported by the underlying
``kaplan`` library (e.g. ``.docx``, ``.xlsx``, ``.xliff``).
- **Bilingual files** for projects that already contain translation
work-in-progress (the source-language text is paired with a
partially-filled target).
- **Reference files** — style guides, glossaries, brand assets —
downloadable from the project page but not opened in the editor.

4. Submit. Each uploaded file is processed in the background and the
project moves through **Preparing** → **Ready for Analysis** →
**Analyzing** → **Ready for Translation**. Watch the project page
while this happens; the file rows light up as each file is ready.

Assign linguists
----------------

Once a project is **Ready for Translation**, assign a translator (and
optionally a reviewer) to each file.

1. On the project page, tick the checkboxes for the files you want to
assign. Leaving all rows unticked applies the next action to every
file.

2. Right-click anywhere on the file table and pick **Assign
translator** or **Assign reviewer**.

.. image:: ./_static/img/files-context-menu.png
:alt: Files context menu

3. Enter the username of the linguist and submit.

.. image:: ./_static/img/assign-team-member-form.png
:alt: Assign team member form

.. image:: ./_static/img/team-member-assigned.png
:alt: Team member assigned

Once assigned, the linguist sees the project in their own project list
and can open the file in the :doc:`editor`. The file status moves to
**In Translation** the first time they open it.
56 changes: 56 additions & 0 deletions docs/source/editor.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
The translation editor
======================

The editor at ``/file/<uuid>`` is where translators and reviewers work
through a file segment by segment. You can reach it from the file row
on a project page.

Segments and statuses
---------------------

Each segment has a source-language text and a target-language text you
edit. A segment moves through three statuses:

- **Blank** — untouched.
- **Draft** — you have entered or modified the target text but haven't
finalized it.
- **Translated** — you have confirmed the segment.

Saving a target text marks the segment as Draft. Confirming a segment
moves it to Translated. A file becomes eligible to move to **In
Review** once all of its segments are Translated.

Translation memory matches
--------------------------

When the project's PM attached one or more translation memories to the
project, the editor looks up each source segment against them and
displays match candidates alongside the segment. Matches can be
**exact** (100% identical source text) or **fuzzy** (similar but not
identical, ranked by similarity score). Selecting a match copies its
target text into the current segment, ready for you to confirm or
edit.

For more on how TMs are created and what data they contain, see
:doc:`translation-memories`.

Comments
--------

You can leave a comment on any segment — useful for flagging a tricky
choice for the reviewer, or for the reviewer to push something back to
the translator. Comments stay attached to the segment as the file
moves through the workflow.

Translator vs. reviewer mode
----------------------------

A file in **In Translation** status (after a translator has opened it)
is editable by its assigned translator. Once the translator finishes,
the file moves to **In Review** and the assigned reviewer takes over
in the same editor view; the reviewer can edit segments and either
push them back to Draft (returning the file to translation) or
confirm them as final.

PMs and project owners can open and edit any file at any phase. Other
users cannot edit a file they are not assigned to.
Loading
Loading