diff --git a/README.md b/README.md
index 4d7c330..e083534 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-A Rust versions of [LinguiJS Macro](https://lingui.dev/ref/macro) [

](https://github.com/lingui/swc-plugin)
+A Rust version of [LinguiJS Macro](https://lingui.dev/ref/macro) [

](https://github.com/lingui/swc-plugin)
[](https://www.npmjs.com/package/@lingui/swc-plugin)
[](https://www.npmjs.com/package/@lingui/swc-plugin)
@@ -51,8 +51,17 @@ https://swc.rs/docs/configuration/swcrc
// "trans": ["@lingui/react", "Trans"]
// }
// Lingui strips non-essential fields in production builds for performance.
+ // Docs https://lingui.dev/guides/optimizing-bundle-size
// You can override the default behavior with:
// "stripNonEssentialFields": false/true
+
+ // Compatibility option allows to use v6.* SWC Plugin release channel with @lingui/cli@5.*
+ // Controls the BASE64 alphabet used for generating message IDs.
+ // - false (default): Uses URL-safe BASE64 alphabet (Lingui v6 behavior)
+ // - true: Uses standard BASE64 alphabet (Lingui v5 behavior for compatibility)
+ //
+ // IMPORTANT: This option is temporal and will be removed in the next major release.
+ // "useLinguiV5IdGeneration": true
},
],
],
@@ -127,6 +136,7 @@ To learn more about SWC Plugins compatibility check this issue https://github.co
- Version `0.1.0` ~ `0.*` compatible with `@lingui/core@3.*`
- Version `4.*` compatible with `@lingui/core@4.*`
- Version `5.*` compatible with `@lingui/core@5.*`
+- Version `6.*` compatible with `@lingui/core@5.*` with `useLinguiV5IdGeneration: true` and `@lingui/core@6.*`
## License
diff --git a/package.json b/package.json
index 25d9302..9425553 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,7 @@
},
"files": [],
"peerDependencies": {
- "@lingui/core": "6"
+ "@lingui/core": "5 || 6"
},
"peerDependenciesMeta": {
"@swc/core": {
diff --git a/src/generate_id.rs b/src/generate_id.rs
index 3bf82b7..cca67c5 100644
--- a/src/generate_id.rs
+++ b/src/generate_id.rs
@@ -1,15 +1,16 @@
-use data_encoding::BASE64URL;
+use data_encoding::{BASE64, BASE64URL};
use sha2::{Digest, Sha256};
const UNIT_SEPARATOR: &char = &'\u{001F}';
-pub fn generate_message_id(message: &str, context: &str) -> String {
+pub fn generate_message_id(message: &str, context: &str, use_lingui_v5: bool) -> String {
let mut hasher = Sha256::new();
hasher.update(format!("{message}{UNIT_SEPARATOR}{context}"));
let result = hasher.finalize();
- BASE64URL.encode(result.as_ref())[0..6].into()
+ let encoder = if use_lingui_v5 { BASE64 } else { BASE64URL };
+ encoder.encode(result.as_ref())[0..6].into()
}
#[cfg(test)]
@@ -18,20 +19,32 @@ mod tests {
#[test]
fn test_generate_message_id() {
- assert_eq!(generate_message_id("my message", ""), "vQhkQx")
+ assert_eq!(generate_message_id("my message", "", false), "vQhkQx")
}
#[test]
fn test_generate_message_id_with_context() {
assert_eq!(
- generate_message_id("my message", "custom context"),
+ generate_message_id("my message", "custom context", false),
"gGUeZH"
)
}
#[test]
- fn test_generate_message_id_url_save() {
+ fn test_generate_message_id_url_safe() {
// this normally should produce `SO/WB8` but with urlsafe result should be different
- assert_eq!(generate_message_id("Hello World", "my context"), "SO_WB8")
+ assert_eq!(
+ generate_message_id("Hello World", "my context", false),
+ "SO_WB8"
+ )
+ }
+
+ #[test]
+ fn test_generate_message_id_v5_compatibility() {
+ // When using v5 mode (non-url-safe), should produce BASE64 encoding with / and +
+ assert_eq!(
+ generate_message_id("Hello World", "my context", true),
+ "SO/WB8"
+ )
}
}
diff --git a/src/js_macro_folder.rs b/src/js_macro_folder.rs
index 4134edf..99f31dd 100644
--- a/src/js_macro_folder.rs
+++ b/src/js_macro_folder.rs
@@ -35,7 +35,12 @@ where
let mut props: Vec
= vec![create_key_value_prop(
"id",
- generate_message_id(&parsed.message_str, "").into(),
+ generate_message_id(
+ &parsed.message_str,
+ "",
+ self.ctx.options.use_lingui_v5_id_generation,
+ )
+ .into(),
)];
if !self.ctx.options.strip_non_essential_fields {
@@ -123,6 +128,7 @@ where
generate_message_id(
&parsed.message_str,
context_val.as_deref().unwrap_or_default(),
+ self.ctx.options.use_lingui_v5_id_generation,
)
.into(),
))
diff --git a/src/lib.rs b/src/lib.rs
index 68af411..cbc4fd9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -135,8 +135,12 @@ where
message_descriptor_props.push(create_key_value_prop(
"id",
- generate_message_id(&parsed.message_str, &context_attr_val.unwrap_or_default())
- .into(),
+ generate_message_id(
+ &parsed.message_str,
+ &context_attr_val.unwrap_or_default(),
+ self.ctx.options.use_lingui_v5_id_generation,
+ )
+ .into(),
));
}
diff --git a/src/options.rs b/src/options.rs
index a76743c..2c557ac 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -6,6 +6,8 @@ pub struct LinguiJsOptions {
runtime_modules: Option,
#[serde(default)]
strip_non_essential_fields: Option,
+ #[serde(default)]
+ use_lingui_v5_id_generation: Option,
}
#[derive(Deserialize, Debug, PartialEq)]
@@ -32,6 +34,7 @@ impl LinguiJsOptions {
strip_non_essential_fields: self
.strip_non_essential_fields
.unwrap_or(matches!(env_name, "production")),
+ use_lingui_v5_id_generation: self.use_lingui_v5_id_generation.unwrap_or(false),
runtime_modules: RuntimeModulesConfigMapNormalized {
i18n: (
self.runtime_modules
@@ -78,12 +81,14 @@ impl LinguiJsOptions {
pub struct LinguiOptions {
pub strip_non_essential_fields: bool,
pub runtime_modules: RuntimeModulesConfigMapNormalized,
+ pub use_lingui_v5_id_generation: bool,
}
impl Default for LinguiOptions {
fn default() -> LinguiOptions {
LinguiOptions {
strip_non_essential_fields: false,
+ use_lingui_v5_id_generation: false,
runtime_modules: RuntimeModulesConfigMapNormalized {
i18n: ("@lingui/core".into(), "i18n".into()),
trans: ("@lingui/react".into(), "Trans".into()),
@@ -128,6 +133,7 @@ mod lib_tests {
)),
}),
strip_non_essential_fields: None,
+ use_lingui_v5_id_generation: None,
}
)
}
@@ -152,6 +158,7 @@ mod lib_tests {
use_lingui: None,
}),
strip_non_essential_fields: None,
+ use_lingui_v5_id_generation: None,
}
)
}
@@ -203,4 +210,52 @@ mod lib_tests {
let options = config.into_options("production");
assert!(options.strip_non_essential_fields);
}
+
+ #[test]
+ fn test_use_lingui_v5_id_generation_config() {
+ let config = serde_json::from_str::(
+ r#"{
+ "useLinguiV5IdGeneration": true,
+ "runtimeModules": {}
+ }"#,
+ )
+ .unwrap();
+
+ let options = config.into_options("development");
+ assert!(options.use_lingui_v5_id_generation);
+
+ let config = serde_json::from_str::(
+ r#"{
+ "useLinguiV5IdGeneration": false,
+ "runtimeModules": {}
+ }"#,
+ )
+ .unwrap();
+
+ let options = config.into_options("production");
+ assert!(!options.use_lingui_v5_id_generation);
+ }
+
+ #[test]
+ fn test_use_lingui_v5_id_generation_default() {
+ let config = serde_json::from_str::(
+ r#"{
+ "runtimeModules": {}
+ }"#,
+ )
+ .unwrap();
+
+ let options = config.into_options("development");
+ assert!(!options.use_lingui_v5_id_generation);
+
+ let config = serde_json::from_str::(
+ r#"{
+ "runtimeModules": {}
+ }"#,
+ )
+ .unwrap();
+
+ let options = config.into_options("production");
+ assert!(!options.use_lingui_v5_id_generation);
+ }
}
diff --git a/tests/__swc_snapshots__/tests/js_t.rs/js_should_use_v5_generate_id_with_parameter.js b/tests/__swc_snapshots__/tests/js_t.rs/js_should_use_v5_generate_id_with_parameter.js
new file mode 100644
index 0000000..d80eea9
--- /dev/null
+++ b/tests/__swc_snapshots__/tests/js_t.rs/js_should_use_v5_generate_id_with_parameter.js
@@ -0,0 +1,6 @@
+import { i18n as $_i18n } from "@lingui/core";
+$_i18n._(/*i18n*/ {
+ id: "SO/WB8",
+ message: "Hello World",
+ context: "my context"
+});
diff --git a/tests/js_t.rs b/tests/js_t.rs
index 6816127..f6e799c 100644
--- a/tests/js_t.rs
+++ b/tests/js_t.rs
@@ -222,3 +222,18 @@ to!(
t({ message: 'Ola', context: `My Context`})
"#
);
+
+to!(
+ js_should_use_v5_generate_id_with_parameter,
+ LinguiOptions {
+ use_lingui_v5_id_generation: true,
+ ..Default::default()
+ },
+ r#"
+ import { t } from '@lingui/core/macro'
+ t({
+ message: "Hello World",
+ context: "my context"
+ });
+ "#
+);
diff --git a/yarn.lock b/yarn.lock
index 299f113..77c22ec 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -228,7 +228,7 @@ __metadata:
typescript: "npm:^5.9.3"
vitest: "npm:^4.0.18"
peerDependencies:
- "@lingui/core": 6
+ "@lingui/core": 5 || 6
peerDependenciesMeta:
"@swc/core":
optional: true