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

Added translations #153

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
cac56c7
Added translations
asodugf12 Oct 19, 2024
bcd7a44
Use Translations
asodugf12 Oct 20, 2024
3d78196
Use Translations
asodugf12 Oct 20, 2024
d276b14
Use Translations
asodugf12 Oct 20, 2024
c6ee193
Merge branch 'master' into asodugf12/issue120
asodugf12 Oct 22, 2024
13dc63a
Use Translations
asodugf12 Oct 22, 2024
02e53b6
Merge branch 'asodugf12/issue120' of https://github.com/asodugf12/tra…
asodugf12 Oct 22, 2024
403e54c
refactored a bit of code
asodugf12 Oct 22, 2024
f810f02
Update mod.rs
asodugf12 Oct 23, 2024
eb4baa7
Update mod.rs
asodugf12 Oct 23, 2024
af0da6a
Use Translations
asodugf12 Oct 23, 2024
1049ce2
Merge branch 'asodugf12/issue120' of https://github.com/asodugf12/tra…
asodugf12 Oct 23, 2024
6e62b07
Hopefully fixes cargo fmt file diff error
asodugf12 Oct 23, 2024
1b63548
Use Translations
asodugf12 Oct 29, 2024
b83045c
Use Translations
asodugf12 Oct 30, 2024
6a3a988
Use Translations
asodugf12 Nov 8, 2024
d55cbbf
Merge branch 'master' into asodugf12/issue120
asodugf12 Nov 8, 2024
2b81285
Use Translations
asodugf12 Nov 11, 2024
da5a7e8
Merge branch 'asodugf12/issue120' of https://github.com/asodugf12/tra…
asodugf12 Nov 11, 2024
a66db1d
Use Translations
asodugf12 Nov 15, 2024
8b2bb43
Use Translations
asodugf12 Nov 28, 2024
c335a08
Merge branch 'master' into asodugf12/issue120
asodugf12 Nov 28, 2024
b743466
Use Translations
asodugf12 Nov 28, 2024
eb8fd63
Merge branch 'asodugf12/issue120' of https://github.com/asodugf12/tra…
asodugf12 Nov 28, 2024
8863457
Removes serde_inline
asodugf12 Nov 28, 2024
98b800d
Cargo fmt diff fix
asodugf12 Nov 28, 2024
48bbd3b
removed the config from input params
asodugf12 Nov 30, 2024
c7aa51f
LazyStatic for file path
asodugf12 Dec 2, 2024
2650be7
Merge branch 'master' of https://github.com/Snowiiii/Pumpkin into aso…
asodugf12 Dec 2, 2024
5bd9956
updated the repo
asodugf12 Dec 2, 2024
fe4fc02
Changed PATH to EN_US
asodugf12 Dec 9, 2024
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
3 changes: 3 additions & 0 deletions pumpkin-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ pub mod auth;
pub mod logging;
pub mod proxy;
pub mod resource_pack;
pub mod translation;

pub use auth::AuthenticationConfig;
pub use commands::CommandsConfig;
pub use compression::CompressionConfig;
pub use pvp::PVPConfig;
pub use rcon::RCONConfig;
pub use translation::TranslationConfig;

mod commands;
pub mod compression;
Expand Down Expand Up @@ -53,6 +55,7 @@ pub struct AdvancedConfiguration {
pub rcon: RCONConfig,
pub pvp: PVPConfig,
pub logging: LoggingConfig,
pub translation: TranslationConfig,
}

#[serde_inline_default]
Expand Down
26 changes: 26 additions & 0 deletions pumpkin-config/src/translation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::path::PathBuf;

use serde::{Deserialize, Serialize};
use serde_inline_default::serde_inline_default;

#[serde_inline_default]
#[derive(Deserialize, Serialize)]
#[serde(default)]
pub struct TranslationConfig {
#[serde_inline_default(true)]
pub enabled: bool,
#[serde_inline_default(true)]
pub client_translations: bool,
#[serde_inline_default(None)]
pub translation_file_path: Option<PathBuf>,
}

impl Default for TranslationConfig {
fn default() -> Self {
Self {
enabled: false,
client_translations: true,
translation_file_path: None,
}
}
}
1 change: 1 addition & 0 deletions pumpkin/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use crate::{
mod connection_cache;
mod key_store;
pub mod ticker;
mod translation;

pub const CURRENT_MC_VERSION: &str = "1.21.1";

Expand Down
208 changes: 208 additions & 0 deletions pumpkin/src/server/translation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#![expect(dead_code)]

use std::{
collections::HashMap,
fs::File,
io::{BufRead, BufReader},
};

use pumpkin_config::TranslationConfig;
use pumpkin_core::text::{style::Style, TextComponent, TextContent};
use serde_json::Value;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum TranslationError {
#[error("File cannot be opened.")]
InvalidFile,
#[error("Failed to read file. Error: {0}")]
FileRead(std::io::Error),
#[error("Invalid JSON encountered. Use only objects in the translation file.")]
JsonParse,
}

pub enum Translations<'a> {
Untranslated(TextComponent<'a>),
Translated(HashMap<String, TextComponent<'a>>),
asodugf12 marked this conversation as resolved.
Show resolved Hide resolved
}

pub fn translate<'a>(
config: &TranslationConfig,
asodugf12 marked this conversation as resolved.
Show resolved Hide resolved
message: &'a str,
) -> Result<Translations<'a>, TranslationError> {
if !config.enabled {
return Ok(Translations::Untranslated(TextComponent::text(message)));
}
asodugf12 marked this conversation as resolved.
Show resolved Hide resolved

if config.client_translations {
return Ok(Translations::Untranslated(TextComponent {
content: TextContent::Translate {
translate: std::borrow::Cow::Borrowed(message),
with: vec![],
},

style: Style::default(),
}));
}

let path = "";
asodugf12 marked this conversation as resolved.
Show resolved Hide resolved
let translations = get_translations(path, message)?;

Ok(Translations::Translated(translations))
}

fn get_translations<'a>(
path: &str,
message: &'a str,
) -> Result<HashMap<String, TextComponent<'a>>, TranslationError> {
let translation_file = File::open(path).map_err(|_| TranslationError::InvalidFile)?;
let reader = BufReader::new(translation_file);

let json_results = read_translation_file(reader, message)?;
let hashmap_results = make_hashmap(json_results);
let mut text_hashmap = HashMap::new();

for (original, translation) in hashmap_results {
text_hashmap.insert(
original,
TextComponent {
content: TextContent::Text {
text: std::borrow::Cow::Owned(translation),
},
style: Style::default(),
},
);
}
Ok(text_hashmap)
}
///Read a huge object line by line and tricking `serde_json` into thinking they are individual objects
fn read_translation_file(
mut reader: impl BufRead,
message: &str,
) -> Result<Vec<Value>, TranslationError> {
let mut buf = String::new();
let mut results = Vec::new();

loop {
let bytes_read = reader
.read_line(&mut buf)
.map_err(TranslationError::FileRead)?;

if bytes_read == 0 {
break;
}

if buf == "{" || buf == "}" {
continue;
}

if buf.contains(message) {
let mut buf = buf.trim().replace(',', "");
buf.insert(0, '{');
buf.push('}');
let v: Value = serde_json::from_str(&buf).map_err(|_| TranslationError::JsonParse)?;

results.push(v);
}

buf.clear();
}

Ok(results)
}

fn make_hashmap(vec: Vec<Value>) -> HashMap<String, String> {
let mut hashmap: HashMap<String, String> = HashMap::new();

for value in vec {
if let Value::Object(map) = value {
if let Some(text) = map.keys().next() {
if let Some(Value::String(translation)) = map.values().next() {
hashmap.insert(text.to_owned(), translation.to_owned());
}
}
}
}

hashmap
}

#[cfg(test)]
mod test {
use std::collections::HashMap;

use super::{make_hashmap, read_translation_file};

#[test]
fn test_lang_ja_jp() {
let reader = std::io::Cursor::new(
r#"
{
"advancement.advancementNotFound": "\u4e0d\u660e\u306a\u9032\u6357\u3067\u3059\uff1a%s",
"advancements.adventure.bullseye.description": "30m\u4ee5\u4e0a\u96e2\u308c\u305f\u5834\u6240\u304b\u3089\u7684\u306e\u4e2d\u5fc3\u3092\u5c04\u629c\u304f",
"advancements.adventure.bullseye.title": "\u7684\u4e2d",
"commands.advancement.advancementNotFound": "\u300c%s\u300d\u3068\u3044\u3046\u540d\u524d\u306e\u9032\u6357\u306f\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f"
}"#.as_bytes()
);

let intended_result = HashMap::from([
(
"commands.advancement.advancementNotFound".to_owned(),
"「%s」という名前の進捗は見つかりませんでした".to_owned(),
),
(
"advancement.advancementNotFound".to_owned(),
"\u{4e0d}\u{660e}\u{306a}\u{9032}\u{6357}\u{3067}\u{3059}\u{ff1a}%s".to_owned(),
),
]);

assert_eq!(
intended_result,
make_hashmap(read_translation_file(reader, "advancement.advancementNotFound").unwrap())
);
}

#[test]
fn test_lang_it_it() {
let reader = std::io::Cursor::new(
r#"
{
"advMode.type": "Tipo",
"advancement.advancementNotFound": "Progresso sconosciuto: %s",
"advancements.adventure.adventuring_time.description": "Scopri tutti i biomi",
"advancements.adventure.adventuring_time.title": "All'avventura!"
}"#
.as_bytes(),
);

let intended_result = HashMap::from([(
"advancement.advancementNotFound".to_owned(),
"Progresso sconosciuto: %s".to_owned(),
)]);

assert_eq!(
intended_result,
make_hashmap(read_translation_file(reader, "advancement.advancementNotFound").unwrap())
);
}

#[test]
fn no_match() {
let reader = std::io::Cursor::new(
r#"
{
"accessibility.onboarding.accessibility.button": "Accessibilità...",
"accessibility.onboarding.screen.narrator": "Premi Invio per attivare l'assistente vocale",
"accessibility.onboarding.screen.title": "Ti diamo il benvenuto in Minecraft!\n\nVuoi attivare l'assistente vocale o accedere alle impostazioni di accessibilità?",
"addServer.add": "Fatto"
}"#.as_bytes()
);

let intended_result = HashMap::new();

assert_eq!(
intended_result,
make_hashmap(read_translation_file(reader, "advancement.advancementNotFound").unwrap())
);
}
}