
Security News
/Research
Wallet-Draining npm Package Impersonates Nodemailer to Hijack Crypto Transactions
Malicious npm package impersonates Nodemailer and drains wallets by hijacking crypto transactions across multiple blockchains.
@dialpad/i18n
Advanced tools
This package contains all the logic needed to take care the i18n of a Dialpad application extending from the common logic existing in the base locale manager that you can find about on the i18n-services documentation, based on different bundle sources.
There are essentially two main concepts behind the scenes:
The LocaleManager extends the BaseLocaleManager to oversee the internationalization (i18n) in a Vue3.js application using the Fluent localization framework. Its responsibilities include:
.ftl
data, which
maps keys to text for each locale.$t
, $ta
) to
Vue
templates, enabling localized content within the application.The second key concept is the BundleSource interface, which abstracts the
loading and management of localization resources. This interface has two primary
implementations: RawBundleSource
and HTTPBundleSource
, each designed for
different use cases.
RawBundleSource
: This implementation handles localization resources defined
within the application. It organizes these resources defined directly in the
application, and imported directly at runtime (for example, using
import(./[locale].ftl
). This approach is ideal for applications with mostly
static content.
HTTPBundleSource
: This implementation fetches localization resources
dynamically from a server. It caches these resources locally, allowing for
efficient access while still being flexible enough to load translations on
demand.
Disclaimer: Both implementations are provisional and may change as we
better understand our needs. Currently, RawBundleSource
is the preferred
option for most applications.
While the dynamicResources method is used for asynchronously loading resources, the RawBundleSource class also provides a builtResources method for synchronous resource loading. This is useful when you have the localization resources available at compile time and prefer to import them directly without dealing with promises.
Instead of dynamically importing .ftl
files, you can import them directly and
use the
builtResources
method to process the resources. Here's an example:
import { RawBundleSource } from '@dialpad/i18n-services';
// Import your .ftl files directly
import enUSResource from './locales/en-US.ftl';
import esResource from './locales/es.ftl';
// Create an array of BuiltResource objects
const builtResourcesArray: BuiltResource[] = [
['en-US', 'namespace', enUSResource],
['es', 'namespace', esResource],
// ... add other locales and namespaces as needed
];
// Convert BuiltResource objects to Resource objects using builtResources
const resources: Resource[] =
RawBundleSource.builtResources(builtResourcesArray);
// Now, resources can be used to instantiate RawBundleSource or for any other purpose
const bundleSource = new RawBundleSource({ resources });
pnpm add @dialpad/i18n
When using LocaleManager
within a Vue application, (especially in the
context of a library, you should be aware that install needs to receive a
non-default namespace value) it's important to properly integrate it using the
install
method. This method registers LocaleManager
with the Vue instance, allowing
for global access to localization functionalities across the application.
After creating an instance of LocaleManager
, you must call the
install
method. This method accepts an optional namespace
parameter, which is
particularly useful when you are developing a library and need to avoid
conflicts with other potential instances of LocaleManager
in the consumer's
environment.
By using different namespaces, you can have multiple instances of
LocaleManager
available globally in Vue. This is ideal for libraries that may
be used in conjunction with other Vue plugins or in larger applications where
scoped localization management is required.
namespace
: (Optional) The namespace under which the LocaleManager
will be
registered. Defaults to 'default'
if no namespace is provided.import Vue from 'vue';
import { LocaleManager } from '@dialpad/i18n-vue2';
// Create a LocaleManager instance with your configuration options
const localeManager = new LocaleManager({
// ... configuration options, see on Dynamic resources configuration section
});
// Install the LocaleManager with a specific namespace to avoid conflicts
localeManager.install('my-namespace');
// Your library's users can now access the LocaleManager instance globally in Vue
new Vue({
// ... other options
}).$mount('#app');
We recommend you to include this in your own composable that handle every locale and i18n set up logic, i.e.:
//src/localization/i18n.ts
import type { App } from 'vue';
import { LocaleManager, RawBundleSource } from '@dialpad/i18n';
export async function hostI18NManager({
vueApp,
}: {
vueApp: App;
}): Promise<void> {
const bundleSource = new RawBundleSource({
resources: await RawBundleSource.dynamicResources([
['en-US', 'your-app', import('./en-US.ftl?raw')],
]),
});
const manager = new LocaleManager({
bundleSource,
preferredLocale: 'en-US', // optional
allowedLocales: ['en-US'], // optional
fallbackLocale: 'en-US',
namespaces: ['your-app'],
});
await manager.ready;
vueApp.use(manager);
}
Important note: let's say that you have more than one allowedLocales
, or
that your fallbackLocale
/preferredLocale
is different from what you're
specifying on the allowedLocales
prop to the LocaleManager
instance. In
those cases you need to verify that you have specified a Bundle resource for
each locale, in order to have them working properly, i.e.:
//src/localization/i18n.ts
import type { App } from 'vue';
import { LocaleManager, RawBundleSource } from '@dialpad/i18n';
export async function hostI18NManager({
vueApp,
}: {
vueApp: App;
}): Promise<void> {
const bundleSource = new RawBundleSource({
resources: await RawBundleSource.dynamicResources([
// You must **always** have one resource per locale.
['dp-DP', 'your-app', import('./dp-DP.ftl?raw')],
// This one is for the `fallbackLocale`
['en-US', 'your-app', import('./en-US.ftl?raw')],
]),
});
const manager = new LocaleManager({
bundleSource,
// I'm using Dialpadistan as my locale
preferredLocale: 'dp-DP',
// But my fallback is en-US (if it was part of the locales this is the same concept, **you need to add one resource per locale**)
fallbackLocale: 'en-US',
namespaces: ['your-app'],
});
await manager.ready;
vueApp.use(manager);
}
After this, you should plug this into your main.ts
. You will call it like:
// src/main.ts
import { createHost } from '@dialpad/host-entry';
import { hostI18NManager } from './localization/i18n';
import { hostEnv } from './constants';
await createHost(hostEnv)
// ... all your config
.vueCreated(hostI18NManager)
// ... some other config
.start();
You will find that this API is (and should always be) the same as the i18n-vue2 tool.
When instantiating the LocaleManager (or when calling setI18N), the preferredLocale and allowedLocales parameters are optional. BUT this is the following criteria to select it.
If preferredLocale param is provided, use that. If allowedLocales param is provided, use the first one. If localStorage contains the previously-used value, use that. If navigator.language is available, use that. Use the fallbackLocale.
First of all you need to understand he difference between $t
and $ta
, which
mainly lies in their outputs:
There are three main functions exported: $t
, $ta
, and setI18N()
, and two
exported values: currentLocale
and allowedLocales
.
(Keep in mind that you should have a fluent file (en-US.ftl in this case) following the Fluent rules.)
$t
Returns a translated string based on a key and optional variables. It’s used for simple text translations.
# src/localization/en-US.ftl
PARAGRAPH_KEY = Amazing paragraph, { $name }
// Component file
const greeting = $t('PARAGRAPH_KEY', { name: 'John' });
// Output: "Amazing paragraph, John"
$ta
Returns an object containing translated attributes to pass directly as props to components, it can contain aria-label, title, etc, rather than just a plain text. It’s useful for handling element attributes in the UI.
# src/localization/en-US.ftl
BUTTON_NAME =
.aria-label = { $title } now
.title = { $title }
// Component file
const buttonAttrs = $ta('BUTTON_NAME', { title: 'Submit' });
// Output: { ariaLabel: "Submit now", title: "Submit" }
// Usage:
<template>
<DtButton v-bind="$ta('BUTTON_NAME', { title: 'Submit' });" />
</template>;
In both cases, any valid fluent syntax is allowed, so the usage of it might include functions or any other feature provided by the framework. (You can find real world examples on this file)
Here's an example of how to use the LocaleManager
in a Vue
component.
<script>
/* `useI18N` exposes the `$t` and `$ta` localization methods, **for use in the setup method**, and these are both available by default in templates with no extra calls needed. */
import { useI18N } from '@dialpad/i18n';
const { $t, $ta } = useI18N();
</script>
<template>
<div>
<p>{{ $t('PARAGRAPH_KEY') }}</p>
<button
v-bind="
$ta('BUTTON_NAME', {
name,
})
"
/>
</div>
</template>
using the following Fluent file as example:
# src/localization/en-US.ftl
PARAGRAPH_KEY = Amazing paragraph, { $name }
BUTTON_NAME =
.aria-label = { $title } now
.title = { $title }
setI18N
This function receives a Partial<SetLocaleParams>
. This allows you to change
any of the set up parameters after the initialization of the LocaleManager
,
but the main use case is to change locales dynamically.
You probably want to use this in addition to:
currentLocale
, determined by the
criteria defined in the usage section.allowedLocales
, are all the allowed locale codes, sorted alphabetically,
defined during initialization of the LocaleManager
.<script setup lang="ts">
// your imports ...
const { setI18N, allowedLocales, currentLocale } = useI18N();
</script>
<template>
<DtDropdown placement="bottom-end">
<template #anchor="{ attrs }">
<DtButton v-bind="attrs" size="md" importance="clear">
{{ currentLocale }}
</DtButton>
</template>
<template #list="{ close }">
<template v-for="locale in allowedLocales" :key="locale">
<DtListItem
navigation-type="tab"
@click="
setI18N({ locales: locale });
close();
"
>
{{ locale }}
</DtListItem>
</template>
</template>
</DtDropdown>
</template>
The changeLocale method provides the functionality to dynamically change the current locale of your application. This method can target a specific LocaleManager by namespace or all LocaleManager instances globally.
To change the locale settings, call the changeLocale method on your LocaleManager instance. You can pass optional parameters to update the locale settings and optionally specify a namespace to target a specific LocaleManager. If no namespace is provided, the locale settings will be updated for all LocaleManager instances.
args
: (Optional) A Partial<SetLocaleParams>
object containing the new
locale settings to be applied.namespace
: (Optional) The namespace of the
LocaleManager
instance whose locale settings you want to change. If not provided, all
instances will be updated.// Assuming you have a LocaleManager instance
const manager = new LocaleManager(...);
// To change the locale settings for a specific namespace
manager.changeLocale({ preferredLocale: 'fr-FR' }, 'myNamespace');
// To change the locale settings for all LocaleManager instances
manager.changeLocale({ preferredLocale: 'fr-FR' });
### Adding Resources at Runtime with `addSources`
#### API Method: `addSources`
The `addSources` method allows you to add multiple translation resources to the `LocaleManager` at runtime. Once all resources are loaded, it triggers the `ready` promise indicating that the locale manager is fully initialized and ready to handle localization requests.
addSources
addSources
The addSources
method allows you to add multiple translation resources to the
LocaleManager
at runtime. Once all resources are loaded, it triggers the
ready
promise indicating that the locale manager is fully initialized and
ready to handle localization requests.
sources
: An array of BuiltResource
items where each item contains:
locale
: The locale code for the translation resource.namespace
: The namespace associated with the resource.source
: The actual source string or a promise that resolves to the source
string.// Assuming you have a LocaleManager instance
const manager = new LocaleManager(...);
// Define your resources
const resourcesToAdd: BuiltResource[] = [
// Array of [locale, namespace, source] tuples
['en-US', 'namespace1', sourceStringOrPromise],
['es-ES', 'namespace1', anotherSourceStringOrPromise],
// Add more resources as needed
];
// Add the resources to the LocaleManager
manager.addSources(resourcesToAdd);
Context screenshots are, as their name suggest, screenshots that provide context to recognize where each translation is used in each app.
Each screenshot should contain visual information of where the translation can be found in the app with enough context to be easily located. If other dev looks at the screenshot they should be able to locate it fairly easily in your app.
There should be one screenshot per each translation key on each .ftl
file. You
can reuse the same screenshot for multiple keys, but each file will correspond
to one key, so you must make one copy for each translation key. The screenshot
filename will be {translationKeyHere}.png
. We only support .png
images for
now.
There is a PR check which will fail if any translation key is missing its corresponding screenshot. There is an exception list for paths that should be ignored by this check. Avoid using it unless it's completely necessary. If you strictly need to use it, remember to mention in your commit message why you are using it.
These screenshots will be located in a directory called context-screenshots
sibling to each .ftl
file in your project. If you have an .ftl
file, it
should have a sibling screenshots directory. Each screenshot has a file size
limit of 20MB.
The screenshots are uploaded to smartling along with the CSV/FTL files through a github action when a PR to main is merged.
FAQs
Dialpad's internationalization library
The npm package @dialpad/i18n receives a total of 77 weekly downloads. As such, @dialpad/i18n popularity was classified as not popular.
We found that @dialpad/i18n demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
/Research
Malicious npm package impersonates Nodemailer and drains wallets by hijacking crypto transactions across multiple blockchains.
Security News
This episode explores the hard problem of reachability analysis, from static analysis limits to handling dynamic languages and massive dependency trees.
Security News
/Research
Malicious Nx npm versions stole secrets and wallet info using AI CLI tools; Socket’s AI scanner detected the supply chain attack and flagged the malware.