mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
fix(i18n): point static catalog translation at weblate (#2619)
This commit is contained in:
1 parent
3e32414849
commit
98cceae59d
3 files changed
+349
-20
No files matched your search
@@ -0,0 +1,291 @@
|
|||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use anyhow::{Context, Result, bail};
|
||||||
|
use serde_json::{Map, Value};
|
||||||
|
|
||||||
|
use crate::po::{Entry, Translation};
|
||||||
|
use crate::ts_catalog::StaticTsCatalogKind;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct StaticJsonCatalogConfig {
|
||||||
|
pub kind: StaticTsCatalogKind,
|
||||||
|
pub source_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_object(content: &str, what: &str) -> Result<Map<String, Value>> {
|
||||||
|
let value: Value =
|
||||||
|
serde_json::from_str(content).with_context(|| format!("failed to parse {what}"))?;
|
||||||
|
match value {
|
||||||
|
Value::Object(map) => Ok(map),
|
||||||
|
_ => bail!("{what} must be a JSON object"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flatten(
|
||||||
|
kind: &StaticTsCatalogKind,
|
||||||
|
map: &Map<String, Value>,
|
||||||
|
) -> Result<BTreeMap<String, String>> {
|
||||||
|
let mut out = BTreeMap::new();
|
||||||
|
for (key, value) in map {
|
||||||
|
match kind {
|
||||||
|
StaticTsCatalogKind::SimpleMessages => match value {
|
||||||
|
Value::String(text) => {
|
||||||
|
out.insert(key.clone(), text.clone());
|
||||||
|
}
|
||||||
|
_ => bail!("{key} must be a string"),
|
||||||
|
},
|
||||||
|
StaticTsCatalogKind::EmailTemplates => match value {
|
||||||
|
Value::Object(fields) => {
|
||||||
|
for (field, text) in fields {
|
||||||
|
match text {
|
||||||
|
Value::String(text) => {
|
||||||
|
out.insert(format!("{key}.{field}"), text.clone());
|
||||||
|
}
|
||||||
|
_ => bail!("{key}.{field} must be a string"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => bail!("{key} must be an object of template fields"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(kind: &StaticTsCatalogKind, values: &BTreeMap<String, String>) -> Result<String> {
|
||||||
|
let mut root = Map::new();
|
||||||
|
match kind {
|
||||||
|
StaticTsCatalogKind::SimpleMessages => {
|
||||||
|
for (key, text) in values {
|
||||||
|
root.insert(key.clone(), Value::String(text.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StaticTsCatalogKind::EmailTemplates => {
|
||||||
|
for (context, text) in values {
|
||||||
|
let Some((template, field)) = context.split_once('.') else {
|
||||||
|
bail!("{context} must name a template field as <template>.<field>");
|
||||||
|
};
|
||||||
|
let entry = root
|
||||||
|
.entry(template.to_string())
|
||||||
|
.or_insert_with(|| Value::Object(Map::new()));
|
||||||
|
let Value::Object(fields) = entry else {
|
||||||
|
bail!("{template} must be an object of template fields");
|
||||||
|
};
|
||||||
|
fields.insert(field.to_string(), Value::String(text.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
let formatter = serde_json::ser::PrettyFormatter::with_indent(b"\t");
|
||||||
|
let mut serializer = serde_json::Serializer::with_formatter(&mut buffer, formatter);
|
||||||
|
serde::Serialize::serialize(&Value::Object(root), &mut serializer)
|
||||||
|
.context("failed to serialize catalog")?;
|
||||||
|
let mut rendered = String::from_utf8(buffer).context("catalog is not valid UTF-8")?;
|
||||||
|
rendered.push('\n');
|
||||||
|
Ok(rendered)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn static_comments(kind: &StaticTsCatalogKind, context: &str) -> Vec<String> {
|
||||||
|
match kind {
|
||||||
|
StaticTsCatalogKind::SimpleMessages => vec![format!("#. Static catalog key: {context}")],
|
||||||
|
StaticTsCatalogKind::EmailTemplates => vec![format!("#. Email template field: {context}")],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_static_json_entries(
|
||||||
|
config: &StaticJsonCatalogConfig,
|
||||||
|
source_content: &str,
|
||||||
|
target_content: &str,
|
||||||
|
reset: bool,
|
||||||
|
) -> Result<Vec<Entry>> {
|
||||||
|
let source = flatten(
|
||||||
|
&config.kind,
|
||||||
|
&parse_object(source_content, "the source catalog")?,
|
||||||
|
)?;
|
||||||
|
let target = flatten(
|
||||||
|
&config.kind,
|
||||||
|
&parse_object(target_content, "the locale catalog")?,
|
||||||
|
)?;
|
||||||
|
Ok(source
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, (context, msgid))| {
|
||||||
|
let msgstr = if reset {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
target.get(&context).cloned().unwrap_or_default()
|
||||||
|
};
|
||||||
|
Entry {
|
||||||
|
comments: static_comments(&config.kind, &context),
|
||||||
|
references: vec![format!("#: {}", config.source_path.display())],
|
||||||
|
msgctxt: Some(context),
|
||||||
|
msgid,
|
||||||
|
msgstr,
|
||||||
|
line_number: index + 1,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rebuild_static_json_allow_replacing(
|
||||||
|
config: &StaticJsonCatalogConfig,
|
||||||
|
source_content: &str,
|
||||||
|
target_content: &str,
|
||||||
|
translations: &[Translation],
|
||||||
|
) -> Result<String> {
|
||||||
|
let source = flatten(
|
||||||
|
&config.kind,
|
||||||
|
&parse_object(source_content, "the source catalog")?,
|
||||||
|
)?;
|
||||||
|
let mut target = flatten(
|
||||||
|
&config.kind,
|
||||||
|
&parse_object(target_content, "the locale catalog")?,
|
||||||
|
)?;
|
||||||
|
for translation in translations {
|
||||||
|
let Some(context) = translation.msgctxt.as_ref() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if !source.contains_key(context) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
target.insert(context.clone(), translation.msgstr.clone());
|
||||||
|
}
|
||||||
|
let merged = source
|
||||||
|
.keys()
|
||||||
|
.map(|context| {
|
||||||
|
let text = target
|
||||||
|
.get(context)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| source[context].clone());
|
||||||
|
(context.clone(), text)
|
||||||
|
})
|
||||||
|
.collect::<BTreeMap<_, _>>();
|
||||||
|
render(&config.kind, &merged)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_static_json_translations(
|
||||||
|
config: &StaticJsonCatalogConfig,
|
||||||
|
target_content: &str,
|
||||||
|
) -> Result<String> {
|
||||||
|
let target = flatten(
|
||||||
|
&config.kind,
|
||||||
|
&parse_object(target_content, "the locale catalog")?,
|
||||||
|
)?;
|
||||||
|
let cleared = target
|
||||||
|
.into_keys()
|
||||||
|
.map(|context| (context, String::new()))
|
||||||
|
.collect::<BTreeMap<_, _>>();
|
||||||
|
render(&config.kind, &cleared)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn simple() -> StaticJsonCatalogConfig {
|
||||||
|
StaticJsonCatalogConfig {
|
||||||
|
kind: StaticTsCatalogKind::SimpleMessages,
|
||||||
|
source_path: PathBuf::from("weblate/messages.json"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn email() -> StaticJsonCatalogConfig {
|
||||||
|
StaticJsonCatalogConfig {
|
||||||
|
kind: StaticTsCatalogKind::EmailTemplates,
|
||||||
|
source_path: PathBuf::from("weblate/messages.json"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reads_simple_entries_pairing_source_with_locale() {
|
||||||
|
let entries = read_static_json_entries(
|
||||||
|
&simple(),
|
||||||
|
"{\"a.b\":\"Hello\",\"c.d\":\"Bye\"}",
|
||||||
|
"{\"a.b\":\"Hallo\"}",
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(entries.len(), 2);
|
||||||
|
assert_eq!(entries[0].msgctxt.as_deref(), Some("a.b"));
|
||||||
|
assert_eq!(entries[0].msgid, "Hello");
|
||||||
|
assert_eq!(entries[0].msgstr, "Hallo");
|
||||||
|
assert_eq!(entries[1].msgstr, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reset_blanks_every_value() {
|
||||||
|
let entries =
|
||||||
|
read_static_json_entries(&simple(), "{\"a\":\"Hello\"}", "{\"a\":\"Hallo\"}", true)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(entries[0].msgstr, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn writes_tab_indented_sorted_json_with_a_trailing_newline() {
|
||||||
|
let rendered = rebuild_static_json_allow_replacing(
|
||||||
|
&simple(),
|
||||||
|
"{\"b\":\"Two\",\"a\":\"One\"}",
|
||||||
|
"{\"a\":\"One\",\"b\":\"Two\"}",
|
||||||
|
&[Translation {
|
||||||
|
msgctxt: Some("a".to_string()),
|
||||||
|
msgid: "One".to_string(),
|
||||||
|
msgstr: "Eins".to_string(),
|
||||||
|
reviewed_unchanged: false,
|
||||||
|
notes: String::new(),
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rendered, "{\n\t\"a\": \"Eins\",\n\t\"b\": \"Two\"\n}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn keeps_email_template_fields_nested() {
|
||||||
|
let entries = read_static_json_entries(
|
||||||
|
&email(),
|
||||||
|
"{\"welcome\":{\"subject\":\"Hi\",\"body\":\"Text\"}}",
|
||||||
|
"{\"welcome\":{\"subject\":\"Hallo\",\"body\":\"\"}}",
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(entries[0].msgctxt.as_deref(), Some("welcome.body"));
|
||||||
|
assert_eq!(entries[1].msgctxt.as_deref(), Some("welcome.subject"));
|
||||||
|
let rendered = rebuild_static_json_allow_replacing(
|
||||||
|
&email(),
|
||||||
|
"{\"welcome\":{\"subject\":\"Hi\",\"body\":\"Text\"}}",
|
||||||
|
"{\"welcome\":{\"subject\":\"Hallo\",\"body\":\"\"}}",
|
||||||
|
&[Translation {
|
||||||
|
msgctxt: Some("welcome.body".to_string()),
|
||||||
|
msgid: "Text".to_string(),
|
||||||
|
msgstr: "Inhalt".to_string(),
|
||||||
|
reviewed_unchanged: false,
|
||||||
|
notes: String::new(),
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
rendered,
|
||||||
|
"{\n\t\"welcome\": {\n\t\t\"body\": \"Inhalt\",\n\t\t\"subject\": \"Hallo\"\n\t}\n}\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ignores_translations_the_source_no_longer_defines() {
|
||||||
|
let rendered = rebuild_static_json_allow_replacing(
|
||||||
|
&simple(),
|
||||||
|
"{\"a\":\"One\"}",
|
||||||
|
"{\"a\":\"Eins\"}",
|
||||||
|
&[Translation {
|
||||||
|
msgctxt: Some("gone".to_string()),
|
||||||
|
msgid: "Gone".to_string(),
|
||||||
|
msgstr: "Weg".to_string(),
|
||||||
|
reviewed_unchanged: false,
|
||||||
|
notes: String::new(),
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rendered, "{\n\t\"a\": \"Eins\"\n}\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
pub mod auto;
|
pub mod auto;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod json_catalog;
|
||||||
pub mod llm;
|
pub mod llm;
|
||||||
pub mod locales;
|
pub mod locales;
|
||||||
pub mod openrouter;
|
pub mod openrouter;
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ use crate::config::{
|
|||||||
EnvOverlay, SOURCE_LOCALE, default_app_dir, env_value, is_auto_i18n_unchanged_comment,
|
EnvOverlay, SOURCE_LOCALE, default_app_dir, env_value, is_auto_i18n_unchanged_comment,
|
||||||
locales_dir, positive_float_env, positive_int_env, trim_trailing_slash,
|
locales_dir, positive_float_env, positive_int_env, trim_trailing_slash,
|
||||||
};
|
};
|
||||||
|
use crate::json_catalog::{
|
||||||
|
StaticJsonCatalogConfig, read_static_json_entries, rebuild_static_json_allow_replacing,
|
||||||
|
reset_static_json_translations,
|
||||||
|
};
|
||||||
use crate::llm::{
|
use crate::llm::{
|
||||||
LocalizationClient, LocalizationResult, base_options, clean_translation_response,
|
LocalizationClient, LocalizationResult, base_options, clean_translation_response,
|
||||||
};
|
};
|
||||||
@@ -66,6 +70,7 @@ pub enum CatalogLayout {
|
|||||||
NestedMessages,
|
NestedMessages,
|
||||||
FlatPo,
|
FlatPo,
|
||||||
StaticTs(StaticTsCatalogConfig),
|
StaticTs(StaticTsCatalogConfig),
|
||||||
|
StaticJson(StaticJsonCatalogConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
|
||||||
@@ -128,17 +133,17 @@ impl RuntimeConfig {
|
|||||||
.join("errors")
|
.join("errors")
|
||||||
.join("src")
|
.join("src")
|
||||||
.join("i18n")
|
.join("i18n")
|
||||||
|
.join("weblate")
|
||||||
.join("locales"),
|
.join("locales"),
|
||||||
CatalogLayout::StaticTs(StaticTsCatalogConfig {
|
CatalogLayout::StaticJson(StaticJsonCatalogConfig {
|
||||||
kind: StaticTsCatalogKind::SimpleMessages,
|
kind: StaticTsCatalogKind::SimpleMessages,
|
||||||
source_path: repo_root
|
source_path: repo_root
|
||||||
.join("packages")
|
.join("packages")
|
||||||
.join("errors")
|
.join("errors")
|
||||||
.join("src")
|
.join("src")
|
||||||
.join("i18n")
|
.join("i18n")
|
||||||
.join("ErrorI18nMessages.ts"),
|
.join("weblate")
|
||||||
source_export: "ERROR_I18N_MESSAGES".to_string(),
|
.join("messages.json"),
|
||||||
locale_function: "defineErrorI18nLocaleMessages".to_string(),
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
CatalogName::ApiContent => (
|
CatalogName::ApiContent => (
|
||||||
@@ -147,17 +152,17 @@ impl RuntimeConfig {
|
|||||||
.join("src")
|
.join("src")
|
||||||
.join("api")
|
.join("api")
|
||||||
.join("content_i18n")
|
.join("content_i18n")
|
||||||
|
.join("weblate")
|
||||||
.join("locales"),
|
.join("locales"),
|
||||||
CatalogLayout::StaticTs(StaticTsCatalogConfig {
|
CatalogLayout::StaticJson(StaticJsonCatalogConfig {
|
||||||
kind: StaticTsCatalogKind::SimpleMessages,
|
kind: StaticTsCatalogKind::SimpleMessages,
|
||||||
source_path: repo_root
|
source_path: repo_root
|
||||||
.join("fluxer_api")
|
.join("fluxer_api")
|
||||||
.join("src")
|
.join("src")
|
||||||
.join("api")
|
.join("api")
|
||||||
.join("content_i18n")
|
.join("content_i18n")
|
||||||
.join("ContentI18nMessages.ts"),
|
.join("weblate")
|
||||||
source_export: "CONTENT_I18N_MESSAGES".to_string(),
|
.join("messages.json"),
|
||||||
locale_function: "defineContentI18nLocaleMessages".to_string(),
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
CatalogName::Email => (
|
CatalogName::Email => (
|
||||||
@@ -167,8 +172,9 @@ impl RuntimeConfig {
|
|||||||
.join("email")
|
.join("email")
|
||||||
.join("src")
|
.join("src")
|
||||||
.join("email_i18n")
|
.join("email_i18n")
|
||||||
|
.join("weblate")
|
||||||
.join("locales"),
|
.join("locales"),
|
||||||
CatalogLayout::StaticTs(StaticTsCatalogConfig {
|
CatalogLayout::StaticJson(StaticJsonCatalogConfig {
|
||||||
kind: StaticTsCatalogKind::EmailTemplates,
|
kind: StaticTsCatalogKind::EmailTemplates,
|
||||||
source_path: repo_root
|
source_path: repo_root
|
||||||
.join("fluxer_api")
|
.join("fluxer_api")
|
||||||
@@ -176,9 +182,8 @@ impl RuntimeConfig {
|
|||||||
.join("email")
|
.join("email")
|
||||||
.join("src")
|
.join("src")
|
||||||
.join("email_i18n")
|
.join("email_i18n")
|
||||||
.join("EmailI18nMessages.ts"),
|
.join("weblate")
|
||||||
source_export: "EMAIL_I18N_MESSAGES".to_string(),
|
.join("messages.json"),
|
||||||
locale_function: "defineEmailI18nLocaleMessages".to_string(),
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
@@ -426,6 +431,7 @@ pub fn run_translation(config: &RuntimeConfig, args: &TranslateArgs) -> Result<u
|
|||||||
CatalogLayout::NestedMessages => "nested messages.po",
|
CatalogLayout::NestedMessages => "nested messages.po",
|
||||||
CatalogLayout::FlatPo => "flat .po",
|
CatalogLayout::FlatPo => "flat .po",
|
||||||
CatalogLayout::StaticTs(_) => "static TypeScript locale map",
|
CatalogLayout::StaticTs(_) => "static TypeScript locale map",
|
||||||
|
CatalogLayout::StaticJson(_) => "weblate JSON locale catalog",
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
false,
|
false,
|
||||||
@@ -436,6 +442,12 @@ pub fn run_translation(config: &RuntimeConfig, args: &TranslateArgs) -> Result<u
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if let CatalogLayout::StaticJson(static_config) = &config.catalog_layout {
|
||||||
|
log(
|
||||||
|
&format!("Source catalog: {}", static_config.source_path.display()),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
log(&format!("Model: {}", client.model()), false);
|
log(&format!("Model: {}", client.model()), false);
|
||||||
log(&format!("LLM endpoint: {}", client.base_url()), false);
|
log(&format!("LLM endpoint: {}", client.base_url()), false);
|
||||||
log(
|
log(
|
||||||
@@ -589,9 +601,14 @@ fn available_locales(config: &RuntimeConfig) -> Result<Vec<String>> {
|
|||||||
};
|
};
|
||||||
stem.to_string()
|
stem.to_string()
|
||||||
}
|
}
|
||||||
CatalogLayout::StaticTs(_) => {
|
CatalogLayout::StaticTs(_) | CatalogLayout::StaticJson(_) => {
|
||||||
|
let extension = if matches!(&config.catalog_layout, CatalogLayout::StaticJson(_)) {
|
||||||
|
"json"
|
||||||
|
} else {
|
||||||
|
"ts"
|
||||||
|
};
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
if path.extension().and_then(|ext| ext.to_str()) != Some("ts") {
|
if path.extension().and_then(|ext| ext.to_str()) != Some(extension) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let Some(stem) = path.file_stem().and_then(|stem| stem.to_str()) else {
|
let Some(stem) = path.file_stem().and_then(|stem| stem.to_str()) else {
|
||||||
@@ -1155,7 +1172,10 @@ pub fn sync_source_locale(
|
|||||||
catalog_path: &Path,
|
catalog_path: &Path,
|
||||||
args: &TranslateArgs,
|
args: &TranslateArgs,
|
||||||
) -> Result<LocaleResult> {
|
) -> Result<LocaleResult> {
|
||||||
if matches!(&config.catalog_layout, CatalogLayout::StaticTs(_)) {
|
if matches!(
|
||||||
|
&config.catalog_layout,
|
||||||
|
CatalogLayout::StaticTs(_) | CatalogLayout::StaticJson(_)
|
||||||
|
) {
|
||||||
log(
|
log(
|
||||||
&format!(
|
&format!(
|
||||||
"[{SOURCE_LOCALE}] Source strings live in the static source catalog; nothing to sync"
|
"[{SOURCE_LOCALE}] Source strings live in the static source catalog; nothing to sync"
|
||||||
@@ -1223,9 +1243,13 @@ fn read_catalog_entries_from_content(
|
|||||||
parse_po(&content)
|
parse_po(&content)
|
||||||
}
|
}
|
||||||
CatalogLayout::StaticTs(static_config) => {
|
CatalogLayout::StaticTs(static_config) => {
|
||||||
let source_content = read_static_source_catalog(static_config)?;
|
let source_content = read_static_source_catalog(&static_config.source_path)?;
|
||||||
read_static_ts_entries(static_config, &source_content, content, reset)
|
read_static_ts_entries(static_config, &source_content, content, reset)
|
||||||
}
|
}
|
||||||
|
CatalogLayout::StaticJson(static_config) => {
|
||||||
|
let source_content = read_static_source_catalog(&static_config.source_path)?;
|
||||||
|
read_static_json_entries(static_config, &source_content, content, reset)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1235,6 +1259,9 @@ fn reset_catalog_translations(config: &RuntimeConfig, content: &str) -> Result<S
|
|||||||
CatalogLayout::StaticTs(static_config) => {
|
CatalogLayout::StaticTs(static_config) => {
|
||||||
reset_static_ts_translations(static_config, content)
|
reset_static_ts_translations(static_config, content)
|
||||||
}
|
}
|
||||||
|
CatalogLayout::StaticJson(static_config) => {
|
||||||
|
reset_static_json_translations(static_config, content)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1248,15 +1275,24 @@ fn rebuild_catalog_allow_replacing(
|
|||||||
rebuild_po_allow_replacing(content, translations)
|
rebuild_po_allow_replacing(content, translations)
|
||||||
}
|
}
|
||||||
CatalogLayout::StaticTs(static_config) => {
|
CatalogLayout::StaticTs(static_config) => {
|
||||||
let source_content = read_static_source_catalog(static_config)?;
|
let source_content = read_static_source_catalog(&static_config.source_path)?;
|
||||||
rebuild_static_ts_allow_replacing(static_config, &source_content, content, translations)
|
rebuild_static_ts_allow_replacing(static_config, &source_content, content, translations)
|
||||||
}
|
}
|
||||||
|
CatalogLayout::StaticJson(static_config) => {
|
||||||
|
let source_content = read_static_source_catalog(&static_config.source_path)?;
|
||||||
|
rebuild_static_json_allow_replacing(
|
||||||
|
static_config,
|
||||||
|
&source_content,
|
||||||
|
content,
|
||||||
|
translations,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_static_source_catalog(config: &StaticTsCatalogConfig) -> Result<String> {
|
fn read_static_source_catalog(source_path: &Path) -> Result<String> {
|
||||||
fs::read_to_string(&config.source_path)
|
fs::read_to_string(source_path)
|
||||||
.with_context(|| format!("failed to read {}", config.source_path.display()))
|
.with_context(|| format!("failed to read {}", source_path.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn catalog_path(config: &RuntimeConfig, locale: &str) -> PathBuf {
|
fn catalog_path(config: &RuntimeConfig, locale: &str) -> PathBuf {
|
||||||
@@ -1264,6 +1300,7 @@ fn catalog_path(config: &RuntimeConfig, locale: &str) -> PathBuf {
|
|||||||
CatalogLayout::NestedMessages => config.locales_dir.join(locale).join("messages.po"),
|
CatalogLayout::NestedMessages => config.locales_dir.join(locale).join("messages.po"),
|
||||||
CatalogLayout::FlatPo => config.locales_dir.join(format!("{locale}.po")),
|
CatalogLayout::FlatPo => config.locales_dir.join(format!("{locale}.po")),
|
||||||
CatalogLayout::StaticTs(_) => config.locales_dir.join(format!("{locale}.ts")),
|
CatalogLayout::StaticTs(_) => config.locales_dir.join(format!("{locale}.ts")),
|
||||||
|
CatalogLayout::StaticJson(_) => config.locales_dir.join(format!("{locale}.json")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user