
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
@docyrus/i18n
Advanced tools
Internationalization provider for Docyrus apps. Provides `DocyrusI18nProvider` + `t()` function with API-based and static translation support, cookie-based locale persistence, and `{{variable}}` interpolation.
Internationalization provider for Docyrus apps. Provides DocyrusI18nProvider + t() function with API-based and static translation support, cookie-based locale persistence, and {{variable}} interpolation.
Supports React, React Native, Vue, and SSR (Next.js Server Components) via separate entrypoints.
DocyrusI18nProvider and use t() anywhereGET /v1/tenant/translations) and/or provide static JSONdocyrus-locale), survives page reloadssetLocale('en') writes cookie + refetches translations automatically{{name}}, {name}, and {0} placeholder formats'use client' directive for Next.js; static translations available immediately (no loading flash)RestApiClient from @docyrus/api-client (auto Authorization header)tv('translate.Active') for API response values with "translate." prefixuseDocyrusI18n() (full context) and useTranslation() (lightweight)useI18n() with graceful fallback (works without provider), DocyI18nProvider with MMKV/AsyncStorage supportuseDocyrusI18n() and useTranslation() for Vue 3I18nManager class for custom integrationsCookieStorageAdapter (web), MemoryStorageAdapter (SSR/testing), custom adapters (MMKV, AsyncStorage)npm install @docyrus/i18n @docyrus/api-client
pnpm add @docyrus/i18n @docyrus/api-client
@docyrus/api-client >= 0.0.9 (optional — needed for API-based translations)react >= 19.2.0 (optional — required for React/Next.js/React Native entrypoints)vue >= 3.5.0 (optional — required for Vue entrypoint)| Import Path | Description | Framework |
|---|---|---|
@docyrus/i18n | React provider, hooks | React, Next.js |
@docyrus/i18n/rn | React Native provider, hooks (graceful fallback) | React Native (Expo) |
@docyrus/i18n/vue | Vue provider, composables | Vue 3 |
@docyrus/i18n/core | Pure TypeScript core (no framework dependency) | Any |
import { DocyrusI18nProvider, useTranslation, useDocyrusI18n } from '@docyrus/i18n';
import { useDocyrusClient } from '@docyrus/signin';
function App() {
const client = useDocyrusClient();
return (
<DocyrusI18nProvider
client={client}
staticTranslations={{ 'common.loading': 'YĂĽkleniyor...' }}
>
<Dashboard />
</DocyrusI18nProvider>
);
}
function Dashboard() {
const { t, isLoading } = useTranslation();
if (isLoading) return <div>{t('common.loading')}</div>;
return <h1>{t('dashboard.title')}</h1>;
}
// app/layout.tsx (Server Component)
import { I18nClientProvider } from './i18n-client-provider';
async function getTranslations() {
const res = await fetch(`${API_URL}/v1/tenant/translations`, {
headers: { Authorization: `Bearer ${serverToken}` }
});
return res.json();
}
export default async function Layout({ children }: { children: React.ReactNode }) {
const { data: translations } = await getTranslations();
return (
<html>
<body>
<I18nClientProvider staticTranslations={translations}>
{children}
</I18nClientProvider>
</body>
</html>
);
}
// components/i18n-client-provider.tsx (Client Component)
'use client';
import { DocyrusI18nProvider, type TranslationDictionary } from '@docyrus/i18n';
import { useDocyrusClient } from '@docyrus/signin';
export function I18nClientProvider({
children,
staticTranslations
}: {
children: React.ReactNode;
staticTranslations: TranslationDictionary;
}) {
const client = useDocyrusClient();
return (
<DocyrusI18nProvider
client={client}
staticTranslations={staticTranslations}
>
{children}
</DocyrusI18nProvider>
);
}
<!-- App.vue -->
<script setup lang="ts">
import { RouterView } from 'vue-router';
import { DocyrusI18nProvider } from '@docyrus/i18n/vue';
import { useDocyrusClient } from '@docyrus/signin/vue';
const client = useDocyrusClient();
</script>
<template>
<DocyrusI18nProvider :client="client">
<RouterView />
</DocyrusI18nProvider>
</template>
<!-- Dashboard.vue -->
<script setup lang="ts">
import { useDocyrusI18n } from '@docyrus/i18n/vue';
const { t, locale, setLocale, isLoading } = useDocyrusI18n();
</script>
<template>
<div v-if="isLoading">Loading...</div>
<div v-else>
<h1>{{ t('dashboard.title') }}</h1>
<select :value="locale" @change="setLocale(($event.target as HTMLSelectElement).value)">
<option value="tr">Turkce</option>
<option value="en">English</option>
</select>
</div>
</template>
import { DocyI18nProvider, useI18n, type SupportedLanguage } from '@docyrus/i18n/rn';
// Storage is optional — use any getString/setString compatible adapter:
// MMKV, AsyncStorage, expo-sqlite, or no storage at all (session-only)
import { MMKV } from 'react-native-mmkv';
const mmkv = new MMKV();
const storage = {
getString: (key: string) => mmkv.getString(key),
setString: (key: string, value: string) => mmkv.set(key, value)
};
// translations.json — nested format
const translations = {
en: { common: { save: 'Save', cancel: 'Cancel' } },
tr: { common: { save: 'Kaydet', cancel: 'İptal' } }
};
function App() {
return (
<DocyI18nProvider
defaultTranslations={translations}
defaultLanguage="tr"
storage={storage}
>
<Dashboard />
</DocyI18nProvider>
);
}
function Dashboard() {
const { t, tv, lang, setLanguage } = useI18n();
return (
<View>
<Text>{t('common.save', 'Save')}</Text>
<Text>{tv('translate.Active')}</Text>
<Button onPress={() => setLanguage('en')} title="English" />
</View>
);
}
Key differences from React/Web entrypoint:
useI18n() works without a provider (graceful fallback — returns key or fallback string)DocyI18nProvider accepts nested translations ({ en: { common: { save: "Save" } } })t('key', 'Fallback') — second argument is a fallback string (not params)lang / setLanguage aliases for locale / setLocalegetString/setString pattern — compatible with MMKV, AsyncStorage, expo-sqlite, or any custom adapterstorage prop is optional — if not provided, language preference won't persist across sessions| Prop | Type | Default | Description |
|---|---|---|---|
client | RestApiClient | null | null | Pre-configured API client with auth tokens |
getAccessToken | () => Promise<string | null> | undefined | Alternative auth callback (for SSR or custom setups) |
apiUrl | string | undefined | API base URL (required with getAccessToken) |
translationsEndpoint | string | 'v1/tenant/translations' | API endpoint path |
staticTranslations | Record<string, string> | {} | Pre-loaded translations (available immediately) |
cookieKey | string | 'docyrus-locale' | Cookie name for locale persistence |
mergeStrategy | 'api-first' | 'static-first' | 'api-first' | How to merge static and API translations |
fallback | 'key' | 'empty' | (key) => string | 'key' | What to return when a key is not found |
disableApiFetch | boolean | false | Skip API fetch entirely (use only static translations) |
userLanguageEndpoint | string | false | 'v1/users/me' | PATCH endpoint to persist user language. Set false to disable |
Full i18n context. Available in React (@docyrus/i18n) and Vue (@docyrus/i18n/vue). Throws if no provider.
const {
t, // (key, fallbackOrParams?) => string
tv, // (value) => string — translate "translate.Active" values
locale, // current locale string | null
setLocale, // (locale: string) => void — writes cookie + refetches
status, // 'loading' | 'ready' | 'error'
isLoading, // boolean
translations, // Record<string, string>
error, // Error | null
refetch // () => Promise<void>
} = useDocyrusI18n();
Full i18n context with graceful fallback. Works with or without provider. Use in shared library components.
import { useI18n } from '@docyrus/i18n/rn';
const {
t, // (key, fallbackOrParams?) => string
tv, // (value) => string
lang, // SupportedLanguage ('en' | 'tr')
locale, // alias for lang
setLanguage, // (lang: string) => void
setLocale, // alias for setLanguage
isLoading, // boolean
error, // Error | null
refetch, // () => Promise<void>
reloadTranslations // () => Promise<void>
} = useI18n();
Lightweight hook for components that only need t():
const { t, tv, isLoading } = useTranslation();
'api-first' (default): { ...static, ...api } — API translations override static. Use this when static translations are fallbacks.'static-first': { ...api, ...static } — Static translations override API. Use this for hardcoded overrides.Static translations are available immediately on mount (no loading flash). API translations are fetched asynchronously and merged in.
null.setLocale('en'): write to storage + PATCH users/me { language } + refetch translations.cookies() API.storage.getString(storageKey).userLanguage prop syncs with user profile language (e.g., "tr_TR" → "tr").setLanguage('en'): write to storage + call onLanguageChange callback.fetchRemote prop.The t() function accepts two call signatures:
// Basic lookup (returns key if not found)
t('common.save') // "Kaydet" or "common.save"
// With fallback string (returns fallback if key not found)
t('common.save', 'Save') // "Kaydet" or "Save"
// With interpolation params
t('greeting', { name: 'Ali' }) // "Merhaba, {{name}}!" → "Merhaba, Ali!"
Three placeholder formats are supported:
// Double-brace (static translations convention)
t('greeting', { name: 'Ali' }) // "Merhaba, {{name}}!" → "Merhaba, Ali!"
// Single-brace named (API convention)
t('ai.chat', { name: 'Docyrus' }) // "Talk to {name} AI" → "Talk to Docyrus AI"
// Single-brace positional (API convention)
t('add.new', { '0': 'User' }) // "Add New {0}" → "Add New User"
For API responses containing translatable values:
tv('translate.Active') // → t('common.Active') → "Aktif"
tv('Regular text') // → "Regular text" (unchanged)
The API endpoint (GET /v1/tenant/translations) must return:
{
"success": true,
"data": {
"Show Expand Detail Button": "Detayı Genişlet Butonunu Göster",
"Add New {0}": "Yeni {0} Ekle",
"Talk to {name} AI": "{name} AI ile KonuĹź"
}
}
Translation keys are flat strings (not dot-notation). Values may contain {0} or {name} placeholders.
import { I18nManager, interpolate, CookieStorageAdapter, MemoryStorageAdapter } from '@docyrus/i18n/core';
const manager = new I18nManager({
staticTranslations: { 'hello': 'Merhaba, {{name}}!' }
});
manager.subscribe((state) => console.log(state.status, state.translations));
await manager.initialize();
console.log(manager.t('hello', { name: 'Ali' })); // "Merhaba, Ali!"
pnpm install
pnpm dev # Watch mode
pnpm build # Build ESM + dts
pnpm lint # ESLint
pnpm typecheck # ESLint + tsc --noEmit
MIT
FAQs
Internationalization provider for Docyrus apps. Provides `DocyrusI18nProvider` + `t()` function with API-based and static translation support, cookie-based locale persistence, and `{{variable}}` interpolation.
We found that @docyrus/i18n demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 4 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
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.