Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to translate page URLs #55

Merged
merged 7 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@

## Unreleased

* Adds the ability to translate page URLs
* See [Docs > Translating URLs](https://rosey.app/docs/urls/)
* Fixes an issue where some internal links with query strings or hash fragments would not be rewritten

## v2.0.5 (March 19, 2024)

* Fixes an issue where `<template>` elements were being ignored by generate and build.
Expand Down
1 change: 1 addition & 0 deletions docs/content/docs/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ _site/
index.liquid
+rosey/
+>> base.json
+>> base.urls.json
{{< /tree >}}

This `base.json` file contains all text that needs to be translated. For the layout we tagged above, this will look like the following:
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: "Rosey Checks File"
nav_title: "Rosey Checks File"
nav_section: Workflow
weight: 6
weight: 7
---

The Rosey checks file highlights any discrepancies between your base translation file and your translated locale files, such as out of date or missing translations.
Expand Down
8 changes: 8 additions & 0 deletions docs/content/docs/generate.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ The path to generate the Rosey base locale file to. Defaults to `rosey/base.json
|-----------------|--------------|------------|
| `--base <FILE>` | `ROSEY_BASE` | `base` |

### Base URLs

The path to generate the Rosey base urls file to. Defaults to `rosey/base.urls.json`

| CLI Flag | ENV Variable | Config Key |
|----------------------|-------------------|-------------|
| `--base-urls <FILE>` | `ROSEY_BASE_URLS` | `base_urls` |

### Separator

The separator to use between Rosey namespaces when generating keys. Defaults to `:`
Expand Down
58 changes: 58 additions & 0 deletions docs/content/docs/urls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
title: "Translated URLs"
nav_title: "Translated URLs"
nav_section: Workflow
weight: 6
---

Rosey URL locale files can contain translated URLs for your website in a given language.

## Creating translated URL locale files

Creating URL locale files is not a step performed by Rosey. This part of the translation workflow is left open ended, usually integrating into an existing translation workflow for a company, or being programmatically created by transforming the input URLs.

Rosey will look for URL locale files alongside the standard locale files. For a file at `rosey/locales/es.json`, Rosey will look for URLs at `rosey/locales/es.urls.json`.

Locale files should be created based on the base URL file output from the [Rosey generate command](/docs/generate/). For the example base URL file:

```json
{
"version": 2,
"keys": {
"index.html": {
"original": "index.html"
},
"home/index.html": {
"original": "home/index.html"
},
"posts/hello-world.html": {
"original": "posts/hello-world.html"
}
}
}
```

The `rosey/locales/ja-jp.urls.json` locale file should match the structure:

```json
{
"index.html": {
"original": "index.html",
"value": "index.html"
},
"home/index.html": {
"original": "home/index.html",
"value": "家/index.html"
},
"posts/hello-world.html": {
"original": "posts/hello-world.html",
"value": "投稿/こんにちは世界.html"
}
}
```

Each of these keys is an object with `original` and `value` strings. The `value` string should contain the translated destination file, and will be used by Rosey when building your final multilingual site.

The output should always include the `.html` extension. Rosey will remove any trailing `index.html` filename where able.

All internal links to these files will be updated within the target locale.
8 changes: 8 additions & 0 deletions rosey/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions rosey/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ actix-files = "0.6"
tokio = { version = "1.36", features = ["macros", "rt-multi-thread", "time"] }
notify = "5.0.0"
charabia = "0.6.0"
path-slash = "0.2"
url = "2"
8 changes: 8 additions & 0 deletions rosey/features/build/rosey-build-links.feature
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Feature: Rosey Links
<h4><a href="/posts/hello-world.png">Hello World Asset</a></h4>
<h5><a href="posts/hello-world/">Hello World Relative</a></h5>
<h6><a href="/posts/hello-world.html">Hello World Extension</a></h6>
<h7><a href="/posts/hello-world/#title">Hello World Anchor</a></h7>
<h8><a href="/posts/hello-world.html?q=a">Hello World Query</a></h8>
</body>
</html>
"""
Expand Down Expand Up @@ -42,6 +44,12 @@ Feature: Rosey Links
Then I should see a selector 'h6>a' in "dist/translated_site/blank/index.html" with the attributes:
| href | /blank/posts/hello-world.html |
| innerText | Hello World Extension |
Then I should see a selector 'h7>a' in "dist/translated_site/blank/index.html" with the attributes:
| href | /blank/posts/hello-world/#title |
| innerText | Hello World Anchor |
Then I should see a selector 'h8>a' in "dist/translated_site/blank/index.html" with the attributes:
| href | /blank/posts/hello-world.html?q=a |
| innerText | Hello World Query |


Scenario: Rosey doesn't update links already pointing to a locale
Expand Down
119 changes: 119 additions & 0 deletions rosey/features/build/rosey-build-translated-links.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
Feature: Rosey Translated Links
Background:
Given I have the environment variables:
| ROSEY_SOURCE | dist/site |
| ROSEY_DEST | dist/translated_site |
Given I have a "dist/site/index.html" file with the content:
"""
<html>
<body>
<h1><a href="/posts/hello-world">Hello World</a></h1>
<h2><a href="/posts/hello-world/">Hello World Trailing Slash</a></h2>
<h3><a href="/posts/hello-world/index.html">Hello World Direct Index Link</a></h3>
<h4><a href="posts/hello-world/">Hello World Relative</a></h4>
<h5><a href="/posts/foobar">Foobar</a></h5>
<h6><a href="/posts/foobar/">Foobar Trailing Slash</a></h6>
<h7><a href="/posts/foobar/index.html">Foobar Direct Index Link</a></h7>
</body>
</html>
"""
And I have a "dist/site/posts/hello-world/index.html" file with the content:
"""
<html>
<body>
<h1>Hello</h1>
<h2><a href="/posts/foobar/">Foobar Trailing Slash</a></h2>
</body>
</html>
"""
And I have a "dist/site/posts/foobar/index.html" file with the content:
"""
<html>
<body>
<h1>Hello</h1>
</body>
</html>
"""
And I have a "rosey/locales/cool.json" file with the content:
"""
{}
"""
And I have a "rosey/locales/cool.urls.json" file with the content:
"""
{
"index.html": {
"original": "index.html",
"value": "index.html"
},
"posts/hello-world/index.html": {
"original": "posts/hello-world/index.html",
"value": "radical-articles/sup-world/index.html"
},
"posts/foobar/index.html": {
"original": "posts/foobar/index.html",
"value": "radical-articles/foobar/foobar.html"
}
}
"""

Scenario: Rosey updates internal links using translation keys
When I run my program with the flags:
| build |
Then I should see a selector 'h1>a' in "dist/translated_site/cool/index.html" with the attributes:
| href | /cool/radical-articles/sup-world/ |
| innerText | Hello World |
Then I should see a selector 'h2>a' in "dist/translated_site/cool/index.html" with the attributes:
| href | /cool/radical-articles/sup-world/ |
| innerText | Hello World Trailing Slash |
Then I should see a selector 'h3>a' in "dist/translated_site/cool/index.html" with the attributes:
| href | /cool/radical-articles/sup-world/ |
| innerText | Hello World Direct Index Link |
Then I should see a selector 'h4>a' in "dist/translated_site/cool/index.html" with the attributes:
| href | posts/hello-world/ |
| innerText | Hello World Relative |
Then I should see a selector 'h5>a' in "dist/translated_site/cool/index.html" with the attributes:
| href | /cool/radical-articles/foobar/foobar.html |
| innerText | Foobar |
Then I should see a selector 'h6>a' in "dist/translated_site/cool/index.html" with the attributes:
| href | /cool/radical-articles/foobar/foobar.html |
| innerText | Foobar Trailing Slash |
Then I should see a selector 'h7>a' in "dist/translated_site/cool/index.html" with the attributes:
| href | /cool/radical-articles/foobar/foobar.html |
| innerText | Foobar Direct Index Link |
Then I should see a selector 'h2>a' in "dist/translated_site/cool/radical-articles/sup-world/index.html" with the attributes:
| href | /cool/radical-articles/foobar/foobar.html |
| innerText | Foobar Trailing Slash |

Scenario: Rosey updates meta urls using translation keys
When I run my program with the flags:
| build |
Then I should see a selector 'link' in "dist/translated_site/posts/hello-world/index.html" with the attributes:
| rel | alternate |
| href | /cool/radical-articles/sup-world/ |
| hreflang | cool |

Then I should see a selector 'link' in "dist/translated_site/en/posts/hello-world/index.html" with the attributes:
| rel | alternate |
| href | /cool/radical-articles/sup-world/ |
| hreflang | cool |
Then I should see a selector 'link' in "dist/translated_site/cool/radical-articles/sup-world/index.html" with the attributes:
| rel | alternate |
| href | /en/posts/hello-world/ |
| hreflang | en |

Scenario: Rosey generates redirects with translation keys
When I run my program with the flags:
| build |
| --default-language "cool" |
Then I should see a selector 'a' in "dist/translated_site/posts/hello-world/index.html" with the attributes:
| href | /cool/radical-articles/sup-world/ |
| innerText | Click here if you are not redirected. |

Scenario: Rosey generates canonicals with translation keys
When I run my program with the flags:
| build |
| --default-language "cool" |
Then I should see a selector 'link' in "dist/translated_site/posts/hello-world/index.html" with the attributes:
| rel | canonical |
| href | /cool/radical-articles/sup-world/ |
| hreflang | cool |
31 changes: 31 additions & 0 deletions rosey/features/generate/rosey-generate-urls.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Feature: Rosey Generate URLs
Background:
Given I have the environment variables:
| ROSEY_SOURCE | dist/site |
| ROSEY_DEST | dist/translated_site |

Scenario: Rosey generates a URLs file
Given I have a "dist/site/index.html" file with the content:
"""
<html>
<body>
<p data-rosey="seal">Kiss From A Rose</p>
<p data-rosey="sting">Desert Rose</p>
</body>
</html>
"""
And I have a "dist/site/about/index.html" file with the content:
"""
<html>
<body>
<p data-rosey="seal">Kiss From A Rose</p>
<p data-rosey="seal">Kiss From A Rose</p>
</body>
</html>
"""
When I run my program with the flags:
| generate |
Then I should see "rosey/base.urls.json" containing the values:
| version | int:2 |
| keys.index\.html.original | index.html |
| keys.about/index\.html.original | about/index.html |
18 changes: 18 additions & 0 deletions rosey/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ impl RoseyOptions {
separator: matches.get("separator", base.separator),
locales: working_dir.join(matches.get("locales", base.locales)),
base: working_dir.join(matches.get("base", base.base)),
base_urls: working_dir.join(matches.get("base-urls", base.base_urls)),
default_language: matches.get("default-language", base.default_language),
redirect_page: matches
.get_opt("redirect-page", base.redirect_page)
Expand Down Expand Up @@ -200,6 +201,19 @@ impl RoseyTranslation {
}
}

pub fn insert_uncounted(&mut self, key: String, value: String) {
match self {
RoseyTranslation::V1(keys) => {
keys.insert(key, value);
}
RoseyTranslation::V2(keys) => {
let translation = keys
.entry(key)
.or_insert_with(|| RoseyTranslationEntry::new(value));
}
}
}

pub fn get(&self, key: &str) -> Option<&String> {
match self {
RoseyTranslation::V1(keys) => keys.get(key),
Expand Down Expand Up @@ -302,6 +316,10 @@ impl RoseyLocale {
self.keys.insert(key, value, page);
}

pub fn insert_uncounted(&mut self, key: String, value: String) {
self.keys.insert_uncounted(key, value);
}

pub fn output(&mut self, version: u8) -> String {
match version {
2 => serde_json::to_string_pretty(self).unwrap(),
Expand Down
9 changes: 9 additions & 0 deletions rosey/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ async fn main() {
example_defaults.base.display()
)),
)
.arg(
Arg::with_name("base-urls")
.long("base-urls")
.value_name("PATH")
.help(&format!(
"The file to generate the Rosey base URLs file to. \n ─ Defaults to '{}'",
example_defaults.base_urls.display()
)),
)
.arg(
Arg::with_name("separator")
.long("separator")
Expand Down
3 changes: 3 additions & 0 deletions rosey/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub struct RoseyPublicConfig {
pub tag: String,
pub separator: String,
pub base: PathBuf,
pub base_urls: PathBuf,
pub locales: PathBuf,
pub languages: Option<Vec<String>>,
pub exclusions: String,
Expand All @@ -35,6 +36,7 @@ impl Default for RoseyPublicConfig {
tag: "data-rosey".into(),
separator: ":".into(),
base: "rosey/base.json".into(),
base_urls: "rosey/base.urls.json".into(),
locales: "rosey/locales".into(),
languages: None,
exclusions: r#"\.(html?|json)$"#.into(),
Expand Down Expand Up @@ -69,6 +71,7 @@ impl Display for RoseyPublicConfig {
writeln!(f, " - Source: {}", self.source.display())?;
writeln!(f, " - Destination: {}", self.dest.display())?;
writeln!(f, " - Base locale file: {}", self.base.display())?;
writeln!(f, " - Base urls file: {}", self.base_urls.display())?;
writeln!(f, " - Locales directory: {}", self.locales.display())?;
match &self.images_source {
Some(s) => writeln!(f, " - Images source: {}", s.display())?,
Expand Down
Loading
Loading