mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-07 09:54:42 +00:00
Bug 1613705 - [localization] part1: Introduce localization-ffi bindings for fluent-fallback. r=emilio,nika
Depends on D117349 Differential Revision: https://phabricator.services.mozilla.com/D104788
This commit is contained in:
parent
4dbd59d465
commit
05db766a87
23
Cargo.lock
generated
23
Cargo.lock
generated
@ -2013,6 +2013,7 @@ dependencies = [
|
||||
"l10nregistry",
|
||||
"l10nregistry-ffi",
|
||||
"lmdb-rkv-sys",
|
||||
"localization-ffi",
|
||||
"log",
|
||||
"mapped_hyph",
|
||||
"mdns_service",
|
||||
@ -2809,6 +2810,28 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "localization-ffi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"cstr",
|
||||
"fluent",
|
||||
"fluent-fallback",
|
||||
"fluent-ffi",
|
||||
"futures 0.3.15",
|
||||
"futures-channel",
|
||||
"l10nregistry",
|
||||
"l10nregistry-ffi",
|
||||
"libc",
|
||||
"moz_task",
|
||||
"nserror",
|
||||
"nsstring",
|
||||
"thin-vec",
|
||||
"unic-langid",
|
||||
"xpcom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.4"
|
||||
|
@ -188,35 +188,39 @@ bool extendJSArrayWithErrors(JSContext* aCx, JS::Handle<JSObject*> aErrors,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ConvertArgs(const L10nArgs& aArgs,
|
||||
nsTArray<ffi::L10nArg>& aRetVal) {
|
||||
for (const auto& entry : aArgs.Entries()) {
|
||||
if (!entry.mValue.IsNull()) {
|
||||
const auto& value = entry.mValue.Value();
|
||||
|
||||
if (value.IsUTF8String()) {
|
||||
aRetVal.AppendElement(ffi::L10nArg{
|
||||
&entry.mKey,
|
||||
ffi::FluentArgument::String(&value.GetAsUTF8String())});
|
||||
} else {
|
||||
aRetVal.AppendElement(ffi::L10nArg{
|
||||
&entry.mKey, ffi::FluentArgument::Double_(value.GetAsDouble())});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FluentBundle::FormatPattern(JSContext* aCx, const FluentPattern& aPattern,
|
||||
const Nullable<L10nArgs>& aArgs,
|
||||
const Optional<JS::Handle<JSObject*>>& aErrors,
|
||||
nsACString& aRetVal, ErrorResult& aRv) {
|
||||
nsTArray<nsCString> argIds;
|
||||
nsTArray<ffi::FluentArgument> argValues;
|
||||
nsTArray<ffi::L10nArg> l10nArgs;
|
||||
|
||||
if (!aArgs.IsNull()) {
|
||||
const L10nArgs& args = aArgs.Value();
|
||||
for (auto& entry : args.Entries()) {
|
||||
if (!entry.mValue.IsNull()) {
|
||||
argIds.AppendElement(entry.mKey);
|
||||
|
||||
auto& value = entry.mValue.Value();
|
||||
if (value.IsUTF8String()) {
|
||||
argValues.AppendElement(
|
||||
ffi::FluentArgument::String(&value.GetAsUTF8String()));
|
||||
} else {
|
||||
argValues.AppendElement(
|
||||
ffi::FluentArgument::Double_(value.GetAsDouble()));
|
||||
}
|
||||
}
|
||||
}
|
||||
ConvertArgs(args, l10nArgs);
|
||||
}
|
||||
|
||||
nsTArray<nsCString> errors;
|
||||
bool succeeded = fluent_bundle_format_pattern(mRaw.get(), &aPattern.mId,
|
||||
&aPattern.mAttrName, &argIds,
|
||||
&argValues, &aRetVal, &errors);
|
||||
&aPattern.mAttrName, &l10nArgs,
|
||||
&aRetVal, &errors);
|
||||
|
||||
if (!succeeded) {
|
||||
return aRv.ThrowInvalidStateError(
|
||||
|
26
intl/l10n/LocalizationBindings.h
Normal file
26
intl/l10n/LocalizationBindings.h
Normal file
@ -0,0 +1,26 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_intl_l10n_LocalizationBindings_h
|
||||
#define mozilla_intl_l10n_LocalizationBindings_h
|
||||
|
||||
#include "mozilla/intl/localization_ffi_generated.h"
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
template <>
|
||||
struct RefPtrTraits<intl::ffi::LocalizationRc> {
|
||||
static void AddRef(const intl::ffi::LocalizationRc* aPtr) {
|
||||
intl::ffi::localization_addref(aPtr);
|
||||
}
|
||||
static void Release(const intl::ffi::LocalizationRc* aPtr) {
|
||||
intl::ffi::localization_release(aPtr);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -11,6 +11,7 @@ EXPORTS.mozilla.intl += [
|
||||
"FluentResource.h",
|
||||
"L10nRegistry.h",
|
||||
"Localization.h",
|
||||
"LocalizationBindings.h",
|
||||
"RegistryBindings.h",
|
||||
]
|
||||
|
||||
@ -46,13 +47,19 @@ USE_LIBS += ["intlcomponents"]
|
||||
|
||||
if CONFIG["COMPILE_ENVIRONMENT"]:
|
||||
CbindgenHeader("fluent_ffi_generated.h", inputs=["/intl/l10n/rust/fluent-ffi"])
|
||||
|
||||
CbindgenHeader(
|
||||
"l10nregistry_ffi_generated.h", inputs=["/intl/l10n/rust/l10nregistry-ffi"]
|
||||
)
|
||||
|
||||
CbindgenHeader(
|
||||
"localization_ffi_generated.h", inputs=["/intl/l10n/rust/localization-ffi"]
|
||||
)
|
||||
|
||||
EXPORTS.mozilla.intl += [
|
||||
"!fluent_ffi_generated.h",
|
||||
"!l10nregistry_ffi_generated.h",
|
||||
"!localization_ffi_generated.h",
|
||||
]
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ["test/xpcshell.ini"]
|
||||
|
@ -20,9 +20,16 @@ pub type FluentBundleRc = FluentBundle<Rc<FluentResource>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C, u8)]
|
||||
pub enum FluentArgument {
|
||||
pub enum FluentArgument<'s> {
|
||||
Double_(f64),
|
||||
String(*const nsCString),
|
||||
String(&'s nsACString),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct L10nArg<'s> {
|
||||
pub id: &'s nsACString,
|
||||
pub value: FluentArgument<'s>,
|
||||
}
|
||||
|
||||
fn transform_accented(s: &str) -> Cow<str> {
|
||||
@ -149,14 +156,12 @@ pub fn adapt_bundle_for_gecko(bundle: &mut FluentBundleRc, pseudo_strategy: Opti
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fluent_bundle_new_single(
|
||||
pub extern "C" fn fluent_bundle_new_single(
|
||||
locale: &nsACString,
|
||||
use_isolating: bool,
|
||||
pseudo_strategy: &nsACString,
|
||||
) -> *mut FluentBundleRc {
|
||||
// We can use as_str_unchecked because this string comes from WebIDL and is
|
||||
// guaranteed utf-8.
|
||||
let id = match locale.as_str_unchecked().parse::<LanguageIdentifier>() {
|
||||
let id = match locale.to_utf8().parse::<LanguageIdentifier>() {
|
||||
Ok(id) => id,
|
||||
Err(..) => return std::ptr::null_mut(),
|
||||
};
|
||||
@ -178,7 +183,7 @@ pub unsafe extern "C" fn fluent_bundle_new(
|
||||
let mut langids = Vec::with_capacity(locale_count);
|
||||
let locales = std::slice::from_raw_parts(locales, locale_count);
|
||||
for locale in locales {
|
||||
let id = match locale.as_str_unchecked().parse::<LanguageIdentifier>() {
|
||||
let id = match locale.to_utf8().parse::<LanguageIdentifier>() {
|
||||
Ok(id) => id,
|
||||
Err(..) => return std::ptr::null_mut(),
|
||||
};
|
||||
@ -228,13 +233,13 @@ pub extern "C" fn fluent_bundle_has_message(bundle: &FluentBundleRc, id: &nsACSt
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fluent_bundle_get_message(
|
||||
pub extern "C" fn fluent_bundle_get_message(
|
||||
bundle: &FluentBundleRc,
|
||||
id: &nsACString,
|
||||
has_value: &mut bool,
|
||||
attrs: &mut ThinVec<nsCString>,
|
||||
) -> bool {
|
||||
match bundle.get_message(id.as_str_unchecked()) {
|
||||
match bundle.get_message(&id.to_utf8()) {
|
||||
Some(message) => {
|
||||
attrs.reserve(message.attributes().count());
|
||||
*has_value = message.value().is_some();
|
||||
@ -251,24 +256,23 @@ pub unsafe extern "C" fn fluent_bundle_get_message(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fluent_bundle_format_pattern(
|
||||
pub extern "C" fn fluent_bundle_format_pattern(
|
||||
bundle: &FluentBundleRc,
|
||||
id: &nsACString,
|
||||
attr: &nsACString,
|
||||
arg_ids: &ThinVec<nsCString>,
|
||||
arg_vals: &ThinVec<FluentArgument>,
|
||||
args: &ThinVec<L10nArg>,
|
||||
ret_val: &mut nsACString,
|
||||
ret_errors: &mut ThinVec<nsCString>,
|
||||
) -> bool {
|
||||
let args = convert_args(arg_ids, arg_vals);
|
||||
let args = convert_args(&args);
|
||||
|
||||
let message = match bundle.get_message(id.as_str_unchecked()) {
|
||||
let message = match bundle.get_message(&id.to_utf8()) {
|
||||
Some(message) => message,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let pattern = if !attr.is_empty() {
|
||||
match message.get_attribute(attr.as_str_unchecked()) {
|
||||
match message.get_attribute(&attr.to_utf8()) {
|
||||
Some(attr) => attr.value(),
|
||||
None => return false,
|
||||
}
|
||||
@ -304,25 +308,20 @@ pub unsafe extern "C" fn fluent_bundle_add_resource(
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_args<'a>(
|
||||
arg_ids: &'a [nsCString],
|
||||
arg_vals: &'a [FluentArgument],
|
||||
) -> Option<FluentArgs<'a>> {
|
||||
debug_assert_eq!(arg_ids.len(), arg_vals.len());
|
||||
|
||||
if arg_ids.is_empty() {
|
||||
pub fn convert_args<'s>(args: &[L10nArg<'s>]) -> Option<FluentArgs<'s>> {
|
||||
if args.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut args = FluentArgs::with_capacity(arg_ids.len());
|
||||
for (id, val) in arg_ids.iter().zip(arg_vals.iter()) {
|
||||
let val = match val {
|
||||
let mut result = FluentArgs::with_capacity(args.len());
|
||||
for arg in args {
|
||||
let val = match arg.value {
|
||||
FluentArgument::Double_(d) => FluentValue::from(d),
|
||||
FluentArgument::String(s) => FluentValue::from(unsafe { (**s).to_string() }),
|
||||
FluentArgument::String(s) => FluentValue::from(s.to_utf8()),
|
||||
};
|
||||
args.set(id.to_string(), val);
|
||||
result.set(arg.id.to_string(), val);
|
||||
}
|
||||
Some(args)
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn append_fluent_errors_to_ret_errors(ret_errors: &mut ThinVec<nsCString>, errors: &[FluentError]) {
|
||||
|
@ -3,6 +3,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use cstr::cstr;
|
||||
use fluent_fallback::env::LocalesProvider;
|
||||
use l10nregistry::{
|
||||
env::ErrorReporter,
|
||||
errors::{L10nRegistryError, L10nRegistrySetupError},
|
||||
@ -68,6 +69,13 @@ impl ErrorReporter for GeckoEnvironment {
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalesProvider for GeckoEnvironment {
|
||||
type Iter = std::vec::IntoIter<unic_langid::LanguageIdentifier>;
|
||||
fn locales(&self) -> Self::Iter {
|
||||
vec!["en-US".parse().unwrap()].into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
fn log_simple_console_error(
|
||||
error: &impl fmt::Display,
|
||||
category: &CStr,
|
||||
|
@ -2,9 +2,9 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
mod env;
|
||||
pub mod env;
|
||||
mod fetcher;
|
||||
pub mod load;
|
||||
mod registry;
|
||||
pub mod registry;
|
||||
mod source;
|
||||
mod xpcom_utils;
|
||||
|
23
intl/l10n/rust/localization-ffi/Cargo.toml
Normal file
23
intl/l10n/rust/localization-ffi/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "localization-ffi"
|
||||
version = "0.1.0"
|
||||
authors = ["nobody@mozilla.org"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
futures-channel = "0.3"
|
||||
futures = "0.3"
|
||||
libc = "0.2"
|
||||
nserror = { path = "../../../../xpcom/rust/nserror" }
|
||||
nsstring = { path = "../../../../xpcom/rust/nsstring" }
|
||||
l10nregistry = { git = "https://github.com/mozilla/l10nregistry-rs", rev = "55bf7f826d773303a67d8d7fdab099a04322d4fb" }
|
||||
fluent = { version = "0.16", features = ["fluent-pseudo"] }
|
||||
unic-langid = "0.9"
|
||||
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
|
||||
async-trait = "0.1"
|
||||
moz_task = { path = "../../../../xpcom/rust/moz_task" }
|
||||
fluent-ffi = { path = "../fluent-ffi" }
|
||||
fluent-fallback = "0.5"
|
||||
l10nregistry-ffi = { path = "../l10nregistry-ffi" }
|
||||
xpcom = { path = "../../../../xpcom/rust/xpcom" }
|
||||
cstr = "0.2"
|
25
intl/l10n/rust/localization-ffi/cbindgen.toml
Normal file
25
intl/l10n/rust/localization-ffi/cbindgen.toml
Normal file
@ -0,0 +1,25 @@
|
||||
header = """/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */"""
|
||||
autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. See RunCbindgen.py */
|
||||
#ifndef mozilla_intl_l10n_LocalizationBindings_h
|
||||
#error "Don't include this file directly, instead include LocalizationBindings.h"
|
||||
#endif
|
||||
"""
|
||||
include_version = true
|
||||
braces = "SameLine"
|
||||
line_length = 100
|
||||
tab_width = 2
|
||||
language = "C++"
|
||||
namespaces = ["mozilla", "intl", "ffi"]
|
||||
|
||||
[parse]
|
||||
parse_deps = true
|
||||
include = ["fluent-fallback"]
|
||||
|
||||
[enum]
|
||||
derive_helper_methods = true
|
||||
|
||||
[export.rename]
|
||||
"ThinVec" = "nsTArray"
|
||||
"Promise" = "dom::Promise"
|
508
intl/l10n/rust/localization-ffi/src/lib.rs
Normal file
508
intl/l10n/rust/localization-ffi/src/lib.rs
Normal file
@ -0,0 +1,508 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use fluent::FluentValue;
|
||||
use fluent_fallback::{
|
||||
types::{
|
||||
L10nAttribute as FluentL10nAttribute, L10nKey as FluentL10nKey,
|
||||
L10nMessage as FluentL10nMessage,
|
||||
},
|
||||
Localization,
|
||||
};
|
||||
use fluent_ffi::{convert_args, FluentArgs, FluentArgument, L10nArg};
|
||||
use l10nregistry_ffi::{
|
||||
env::GeckoEnvironment,
|
||||
registry::{get_l10n_registry, GeckoL10nRegistry},
|
||||
};
|
||||
use nserror::{nsresult, NS_OK};
|
||||
use nsstring::{nsACString, nsCString};
|
||||
use std::{borrow::Cow, cell::RefCell};
|
||||
use thin_vec::ThinVec;
|
||||
use xpcom::{interfaces::nsrefcnt, RefCounted, RefPtr, Refcnt};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct L10nKey<'s> {
|
||||
id: &'s nsACString,
|
||||
args: ThinVec<L10nArg<'s>>,
|
||||
}
|
||||
|
||||
impl<'s> From<&'s L10nKey<'s>> for FluentL10nKey<'static> {
|
||||
fn from(input: &'s L10nKey<'s>) -> Self {
|
||||
FluentL10nKey {
|
||||
id: input.id.to_utf8().to_string().into(),
|
||||
args: convert_args_to_owned(&input.args),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is a variant of `convert_args` from `fluent-ffi` with a 'static constrain
|
||||
// put on the resulting `FluentArgs` to make it acceptable into `spqwn_current_thread`.
|
||||
pub fn convert_args_to_owned(args: &[L10nArg]) -> Option<FluentArgs<'static>> {
|
||||
if args.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut result = FluentArgs::with_capacity(args.len());
|
||||
for arg in args {
|
||||
let val = match arg.value {
|
||||
FluentArgument::Double_(d) => FluentValue::from(d),
|
||||
// We need this to be owned because we pass the result into `spawn_current_thread`.
|
||||
FluentArgument::String(s) => FluentValue::from(Cow::Owned(s.to_utf8().to_string())),
|
||||
};
|
||||
result.set(arg.id.to_string(), val);
|
||||
}
|
||||
Some(result)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct L10nAttribute {
|
||||
name: nsCString,
|
||||
value: nsCString,
|
||||
}
|
||||
|
||||
impl From<FluentL10nAttribute<'_>> for L10nAttribute {
|
||||
fn from(attr: FluentL10nAttribute<'_>) -> Self {
|
||||
Self {
|
||||
name: nsCString::from(&*attr.name),
|
||||
value: nsCString::from(&*attr.value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct L10nMessage {
|
||||
value: nsCString,
|
||||
attributes: ThinVec<L10nAttribute>,
|
||||
}
|
||||
|
||||
impl std::default::Default for L10nMessage {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
value: nsCString::new(),
|
||||
attributes: ThinVec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct OptionalL10nMessage {
|
||||
is_present: bool,
|
||||
message: L10nMessage,
|
||||
}
|
||||
|
||||
impl From<FluentL10nMessage<'_>> for L10nMessage {
|
||||
fn from(input: FluentL10nMessage) -> Self {
|
||||
let value = if let Some(value) = input.value {
|
||||
value.to_string().into()
|
||||
} else {
|
||||
let mut s = nsCString::new();
|
||||
s.set_is_void(true);
|
||||
s
|
||||
};
|
||||
Self {
|
||||
value,
|
||||
attributes: input.attributes.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LocalizationRc {
|
||||
inner: RefCell<Localization<GeckoL10nRegistry, GeckoEnvironment>>,
|
||||
refcnt: Refcnt,
|
||||
}
|
||||
|
||||
// xpcom::RefPtr support
|
||||
unsafe impl RefCounted for LocalizationRc {
|
||||
unsafe fn addref(&self) {
|
||||
localization_addref(self);
|
||||
}
|
||||
unsafe fn release(&self) {
|
||||
localization_release(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalizationRc {
|
||||
pub fn new(reg: &GeckoL10nRegistry, res_ids: Vec<String>, is_sync: bool) -> RefPtr<Self> {
|
||||
let inner = Localization::with_env(res_ids, is_sync, GeckoEnvironment, reg.clone());
|
||||
|
||||
let loc = Box::new(LocalizationRc {
|
||||
inner: RefCell::new(inner),
|
||||
refcnt: unsafe { Refcnt::new() },
|
||||
});
|
||||
|
||||
unsafe {
|
||||
RefPtr::from_raw(Box::into_raw(loc))
|
||||
.expect("Failed to create RefPtr<LocalizationRc> from Box<LocalizationRc>")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_resource_id(&self, res_id: String) {
|
||||
self.inner.borrow_mut().add_resource_id(res_id);
|
||||
}
|
||||
|
||||
pub fn add_resource_ids(&self, res_ids: Vec<String>) {
|
||||
self.inner.borrow_mut().add_resource_ids(res_ids);
|
||||
}
|
||||
|
||||
pub fn remove_resource_id(&self, res_id: String) -> usize {
|
||||
self.inner.borrow_mut().remove_resource_id(res_id)
|
||||
}
|
||||
|
||||
pub fn remove_resource_ids(&self, res_ids: Vec<String>) -> usize {
|
||||
self.inner.borrow_mut().remove_resource_ids(res_ids)
|
||||
}
|
||||
|
||||
/// Upgrade synchronous mode to asynchronous.
|
||||
pub fn upgrade(&self) {
|
||||
if self.is_sync() {
|
||||
self.inner.borrow_mut().set_async();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_sync(&self) -> bool {
|
||||
self.inner.borrow().is_sync()
|
||||
}
|
||||
|
||||
pub fn format_value_sync(
|
||||
&self,
|
||||
id: &nsACString,
|
||||
args: &ThinVec<L10nArg>,
|
||||
ret_val: &mut nsACString,
|
||||
ret_err: &mut ThinVec<nsCString>,
|
||||
) -> bool {
|
||||
let mut errors = vec![];
|
||||
let args = convert_args(&args);
|
||||
if let Ok(value) = self.inner.borrow().bundles().format_value_sync(
|
||||
&id.to_utf8(),
|
||||
args.as_ref(),
|
||||
&mut errors,
|
||||
) {
|
||||
if let Some(value) = value {
|
||||
ret_val.assign(&value);
|
||||
} else {
|
||||
ret_val.set_is_void(true);
|
||||
}
|
||||
ret_err.extend(errors.into_iter().map(|err| err.to_string().into()));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_values_sync(
|
||||
&self,
|
||||
keys: &ThinVec<L10nKey>,
|
||||
ret_val: &mut ThinVec<nsCString>,
|
||||
ret_err: &mut ThinVec<nsCString>,
|
||||
) -> bool {
|
||||
ret_val.reserve(keys.len());
|
||||
let keys: Vec<FluentL10nKey> = keys.into_iter().map(|k| k.into()).collect();
|
||||
let mut errors = vec![];
|
||||
if let Ok(values) = self
|
||||
.inner
|
||||
.borrow()
|
||||
.bundles()
|
||||
.format_values_sync(&keys, &mut errors)
|
||||
{
|
||||
for value in values.iter() {
|
||||
if let Some(value) = value {
|
||||
ret_val.push(value.as_ref().into());
|
||||
} else {
|
||||
let mut void_string = nsCString::new();
|
||||
void_string.set_is_void(true);
|
||||
ret_val.push(void_string);
|
||||
}
|
||||
}
|
||||
ret_err.extend(errors.into_iter().map(|err| err.to_string().into()));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_messages_sync(
|
||||
&self,
|
||||
keys: &ThinVec<L10nKey>,
|
||||
ret_val: &mut ThinVec<OptionalL10nMessage>,
|
||||
ret_err: &mut ThinVec<nsCString>,
|
||||
) -> bool {
|
||||
ret_val.reserve(keys.len());
|
||||
let mut errors = vec![];
|
||||
let keys: Vec<FluentL10nKey> = keys.into_iter().map(|k| k.into()).collect();
|
||||
if let Ok(messages) = self
|
||||
.inner
|
||||
.borrow()
|
||||
.bundles()
|
||||
.format_messages_sync(&keys, &mut errors)
|
||||
{
|
||||
for msg in messages {
|
||||
ret_val.push(if let Some(msg) = msg {
|
||||
OptionalL10nMessage {
|
||||
is_present: true,
|
||||
message: msg.into(),
|
||||
}
|
||||
} else {
|
||||
OptionalL10nMessage {
|
||||
is_present: false,
|
||||
message: L10nMessage::default(),
|
||||
}
|
||||
});
|
||||
}
|
||||
assert_eq!(keys.len(), ret_val.len());
|
||||
ret_err.extend(errors.into_iter().map(|err| err.to_string().into()));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_value(
|
||||
&self,
|
||||
id: &nsACString,
|
||||
args: &ThinVec<L10nArg>,
|
||||
promise: &xpcom::Promise,
|
||||
callback: extern "C" fn(&xpcom::Promise, &nsACString, &ThinVec<nsCString>),
|
||||
) {
|
||||
let bundles = self.inner.borrow().bundles().clone();
|
||||
|
||||
let args = convert_args_to_owned(&args);
|
||||
|
||||
let id = nsCString::from(id);
|
||||
let strong_promise = RefPtr::new(promise);
|
||||
|
||||
moz_task::spawn_current_thread(async move {
|
||||
let mut errors = vec![];
|
||||
let value = if let Some(value) = bundles
|
||||
.format_value(&id.to_utf8(), args.as_ref(), &mut errors)
|
||||
.await
|
||||
{
|
||||
let v: nsCString = value.to_string().into();
|
||||
v
|
||||
} else {
|
||||
let mut v = nsCString::new();
|
||||
v.set_is_void(true);
|
||||
v
|
||||
};
|
||||
let errors = errors
|
||||
.into_iter()
|
||||
.map(|err| err.to_string().into())
|
||||
.collect();
|
||||
callback(&strong_promise, &value, &errors);
|
||||
})
|
||||
.expect("Failed to spawn future");
|
||||
}
|
||||
|
||||
pub fn format_values(
|
||||
&self,
|
||||
keys: &ThinVec<L10nKey>,
|
||||
promise: &xpcom::Promise,
|
||||
callback: extern "C" fn(&xpcom::Promise, Option<&ThinVec<nsCString>>),
|
||||
) {
|
||||
let bundles = self.inner.borrow().bundles().clone();
|
||||
|
||||
let keys: Vec<FluentL10nKey> = keys.into_iter().map(|k| k.into()).collect();
|
||||
|
||||
let strong_promise = RefPtr::new(promise);
|
||||
|
||||
moz_task::spawn_current_thread(async move {
|
||||
let mut errors = vec![];
|
||||
let ret_val = bundles
|
||||
.format_values(&keys, &mut errors)
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|value| {
|
||||
if let Some(value) = value {
|
||||
nsCString::from(value.as_ref())
|
||||
} else {
|
||||
let mut v = nsCString::new();
|
||||
v.set_is_void(true);
|
||||
v
|
||||
}
|
||||
})
|
||||
.collect::<ThinVec<_>>();
|
||||
|
||||
callback(&strong_promise, Some(&ret_val));
|
||||
})
|
||||
.expect("Failed to spawn future");
|
||||
}
|
||||
|
||||
pub fn format_messages(
|
||||
&self,
|
||||
keys: &ThinVec<L10nKey>,
|
||||
promise: &xpcom::Promise,
|
||||
callback: extern "C" fn(
|
||||
&xpcom::Promise,
|
||||
Option<&ThinVec<OptionalL10nMessage>>,
|
||||
&ThinVec<nsCString>,
|
||||
),
|
||||
) {
|
||||
let bundles = self.inner.borrow().bundles().clone();
|
||||
|
||||
let keys: Vec<FluentL10nKey> = keys.into_iter().map(|k| k.into()).collect();
|
||||
|
||||
let strong_promise = RefPtr::new(promise);
|
||||
|
||||
moz_task::spawn_current_thread(async move {
|
||||
let mut errors = vec![];
|
||||
let ret_val = bundles
|
||||
.format_messages(&keys, &mut errors)
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|msg| {
|
||||
if let Some(msg) = msg {
|
||||
OptionalL10nMessage {
|
||||
is_present: true,
|
||||
message: msg.into(),
|
||||
}
|
||||
} else {
|
||||
OptionalL10nMessage {
|
||||
is_present: false,
|
||||
message: L10nMessage::default(),
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<ThinVec<_>>();
|
||||
|
||||
assert_eq!(keys.len(), ret_val.len());
|
||||
|
||||
let errors = errors
|
||||
.into_iter()
|
||||
.map(|err| err.to_string().into())
|
||||
.collect();
|
||||
|
||||
callback(&strong_promise, Some(&ret_val), &errors);
|
||||
})
|
||||
.expect("Failed to spawn future");
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn localization_new(
|
||||
res_ids: &ThinVec<nsCString>,
|
||||
is_sync: bool,
|
||||
result: &mut *const LocalizationRc,
|
||||
) -> nsresult {
|
||||
*result = std::ptr::null();
|
||||
|
||||
let reg = get_l10n_registry();
|
||||
let res_ids: Vec<String> = res_ids.iter().map(|res| res.to_string()).collect();
|
||||
*result = RefPtr::forget_into_raw(LocalizationRc::new(®, res_ids, is_sync));
|
||||
NS_OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn localization_addref(loc: &LocalizationRc) -> nsrefcnt {
|
||||
loc.refcnt.inc()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn localization_release(loc: *const LocalizationRc) -> nsrefcnt {
|
||||
let rc = (*loc).refcnt.dec();
|
||||
if rc == 0 {
|
||||
Box::from_raw(loc as *const _ as *mut LocalizationRc);
|
||||
}
|
||||
rc
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn localization_add_res_id(loc: &LocalizationRc, res_id: &nsACString) {
|
||||
let res_id = res_id.to_string();
|
||||
loc.add_resource_id(res_id);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn localization_add_res_ids(loc: &LocalizationRc, res_ids: &ThinVec<nsCString>) {
|
||||
let res_ids = res_ids.iter().map(|s| s.to_string()).collect();
|
||||
loc.add_resource_ids(res_ids);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn localization_remove_res_id(loc: &LocalizationRc, res_id: &nsACString) -> usize {
|
||||
let res_id = res_id.to_string();
|
||||
loc.remove_resource_id(res_id)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn localization_remove_res_ids(
|
||||
loc: &LocalizationRc,
|
||||
res_ids: &ThinVec<nsCString>,
|
||||
) -> usize {
|
||||
let res_ids = res_ids.iter().map(|s| s.to_string()).collect();
|
||||
loc.remove_resource_ids(res_ids)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn localization_format_value_sync(
|
||||
loc: &LocalizationRc,
|
||||
id: &nsACString,
|
||||
args: &ThinVec<L10nArg>,
|
||||
ret_val: &mut nsACString,
|
||||
ret_err: &mut ThinVec<nsCString>,
|
||||
) -> bool {
|
||||
loc.format_value_sync(id, args, ret_val, ret_err)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn localization_format_values_sync(
|
||||
loc: &LocalizationRc,
|
||||
keys: &ThinVec<L10nKey>,
|
||||
ret_val: &mut ThinVec<nsCString>,
|
||||
ret_err: &mut ThinVec<nsCString>,
|
||||
) -> bool {
|
||||
loc.format_values_sync(keys, ret_val, ret_err)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn localization_format_messages_sync(
|
||||
loc: &LocalizationRc,
|
||||
keys: &ThinVec<L10nKey>,
|
||||
ret_val: &mut ThinVec<OptionalL10nMessage>,
|
||||
ret_err: &mut ThinVec<nsCString>,
|
||||
) -> bool {
|
||||
loc.format_messages_sync(keys, ret_val, ret_err)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn localization_format_value(
|
||||
loc: &LocalizationRc,
|
||||
id: &nsACString,
|
||||
args: &ThinVec<L10nArg>,
|
||||
promise: &xpcom::Promise,
|
||||
callback: extern "C" fn(&xpcom::Promise, &nsACString, &ThinVec<nsCString>),
|
||||
) {
|
||||
loc.format_value(id, args, promise, callback);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn localization_format_values(
|
||||
loc: &LocalizationRc,
|
||||
keys: &ThinVec<L10nKey>,
|
||||
promise: &xpcom::Promise,
|
||||
callback: extern "C" fn(&xpcom::Promise, Option<&ThinVec<nsCString>>),
|
||||
) {
|
||||
loc.format_values(keys, promise, callback);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn localization_format_messages(
|
||||
loc: &LocalizationRc,
|
||||
keys: &ThinVec<L10nKey>,
|
||||
promise: &xpcom::Promise,
|
||||
callback: extern "C" fn(
|
||||
&xpcom::Promise,
|
||||
Option<&ThinVec<OptionalL10nMessage>>,
|
||||
&ThinVec<nsCString>,
|
||||
),
|
||||
) {
|
||||
loc.format_messages(keys, promise, callback);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn localization_upgrade(loc: &LocalizationRc) {
|
||||
loc.upgrade();
|
||||
}
|
@ -67,6 +67,7 @@ fluent-ffi = { path = "../../../../intl/l10n/rust/fluent-ffi" }
|
||||
l10nregistry-ffi = { path = "../../../../intl/l10n/rust/l10nregistry-ffi" }
|
||||
l10nregistry = { git = "https://github.com/mozilla/l10nregistry-rs", rev = "55bf7f826d773303a67d8d7fdab099a04322d4fb" }
|
||||
fluent-fallback = "0.5"
|
||||
localization-ffi = { path = "../../../../intl/l10n/rust/localization-ffi" }
|
||||
|
||||
processtools = { path = "../../../components/processtools" }
|
||||
qcms = { path = "../../../../gfx/qcms", features = ["c_bindings", "neon"], default-features = false }
|
||||
|
@ -29,7 +29,6 @@ extern crate gkrust_utils;
|
||||
extern crate http_sfv;
|
||||
extern crate jsrust_shared;
|
||||
extern crate kvstore;
|
||||
extern crate l10nregistry_ffi;
|
||||
extern crate mapped_hyph;
|
||||
extern crate mozurl;
|
||||
extern crate mp4parse_capi;
|
||||
@ -75,7 +74,8 @@ extern crate fluent;
|
||||
extern crate fluent_ffi;
|
||||
|
||||
extern crate fluent_fallback;
|
||||
extern crate l10nregistry;
|
||||
extern crate l10nregistry_ffi;
|
||||
extern crate localization_ffi;
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
extern crate viaduct;
|
||||
|
Loading…
Reference in New Issue
Block a user