@chain1/i18n
v1.0.1
Published
Lightweight internationalization (i18n) library for React Native with AsyncStorage support
Maintainers
Readme
@chain1/i18n
Lightweight internationalization (i18n) library for React Native applications with AsyncStorage persistence.
Features
- ✅ Simple and intuitive API
- ✅ Nested translation keys support
- ✅ Parameter interpolation
- ✅ Language persistence with AsyncStorage
- ✅ Fallback language support
- ✅ TypeScript support
- ✅ Dynamic language switching
- ✅ Translation validation
Installation
npm install @chain1/i18nPeer Dependencies
npm install @react-native-async-storage/async-storageUsage
Basic Setup
import { createI18n } from '@chain1/i18n';
const i18n = createI18n({
defaultLanguage: 'en',
fallbackLanguage: 'en',
storageKey: 'user-language',
translations: {
en: {
common: {
welcome: 'Welcome',
hello: 'Hello {name}',
goodbye: 'Goodbye',
},
buttons: {
submit: 'Submit',
cancel: 'Cancel',
},
},
ko: {
common: {
welcome: '환영합니다',
hello: '안녕하세요 {name}',
goodbye: '안녕히 가세요',
},
buttons: {
submit: '제출',
cancel: '취소',
},
},
},
});
// Initialize (load saved language)
await i18n.init();In React Component
import React, { useEffect, useState } from 'react';
import { View, Text, Button } from 'react-native';
import { i18n } from './i18n';
function App() {
const [, forceUpdate] = useState({});
useEffect(() => {
i18n.init();
}, []);
const changeLanguage = async (lang: string) => {
await i18n.changeLanguage(lang);
forceUpdate({}); // Re-render component
};
return (
<View>
<Text>{i18n.t('common.welcome')}</Text>
<Text>{i18n.t('common.hello', { name: 'John' })}</Text>
<Button
title={i18n.t('buttons.submit')}
onPress={() => {}}
/>
<Button
title="한국어"
onPress={() => changeLanguage('ko')}
/>
<Button
title="English"
onPress={() => changeLanguage('en')}
/>
</View>
);
}With React Context
import React, { createContext, useContext, useState, useEffect } from 'react';
import { createI18n } from '@chain1/i18n';
const i18n = createI18n({ /* config */ });
const I18nContext = createContext({
t: (key: string, params?: any) => key,
changeLanguage: async (lang: string) => {},
currentLanguage: 'en',
});
export function I18nProvider({ children }: { children: React.ReactNode }) {
const [language, setLanguage] = useState('en');
useEffect(() => {
i18n.init().then(() => {
setLanguage(i18n.getCurrentLanguage());
});
}, []);
const changeLanguage = async (lang: string) => {
await i18n.changeLanguage(lang);
setLanguage(lang);
};
return (
<I18nContext.Provider
value={{
t: i18n.t.bind(i18n),
changeLanguage,
currentLanguage: language,
}}
>
{children}
</I18nContext.Provider>
);
}
export const useI18n = () => useContext(I18nContext);
// Usage in component
function MyComponent() {
const { t, changeLanguage, currentLanguage } = useI18n();
return (
<View>
<Text>{t('common.welcome')}</Text>
<Text>{t('common.hello', { name: 'Jane' })}</Text>
</View>
);
}API Reference
createI18n(config: I18nConfig): I18n
Create an I18n instance.
Config:
defaultLanguage(string, optional): Default language code (default: 'en')fallbackLanguage(string, optional): Fallback language when translation missing (default: 'en')storageKey(string, optional): AsyncStorage key for persistence (default: 'user-language')translations(Translations, required): Translation object
i18n.init(): Promise<void>
Initialize I18n and load saved language from AsyncStorage.
await i18n.init();i18n.t(key: string, params?: Record<string, string | number>): string
Translate a key with optional parameters.
// Simple translation
i18n.t('common.welcome'); // "Welcome"
// Nested keys
i18n.t('buttons.submit'); // "Submit"
// With parameters
i18n.t('common.hello', { name: 'John' }); // "Hello John"
// Multiple parameters
i18n.t('message', { user: 'Alice', count: 5 }); // "Alice has 5 messages"i18n.changeLanguage(lang: string): Promise<void>
Change current language and save to AsyncStorage.
await i18n.changeLanguage('ko');i18n.getCurrentLanguage(): string
Get current language code.
const currentLang = i18n.getCurrentLanguage(); // "en"i18n.getAvailableLanguages(): string[]
Get all available language codes.
const languages = i18n.getAvailableLanguages(); // ["en", "ko", "vi"]i18n.addTranslations(locale: string, translations: TranslationObject): void
Add or update translations for a language.
i18n.addTranslations('en', {
newSection: {
newKey: 'New Translation',
},
});i18n.has(key: string): boolean
Check if a translation key exists.
if (i18n.has('common.welcome')) {
console.log('Translation exists');
}Translation File Structure
Recommended Structure
// translations/en.ts
export default {
common: {
welcome: 'Welcome',
loading: 'Loading...',
error: 'Error',
},
auth: {
login: 'Login',
logout: 'Logout',
username: 'Username',
password: 'Password',
},
buttons: {
submit: 'Submit',
cancel: 'Cancel',
save: 'Save',
},
};
// translations/ko.ts
export default {
common: {
welcome: '환영합니다',
loading: '로딩 중...',
error: '오류',
},
auth: {
login: '로그인',
logout: '로그아웃',
username: '사용자명',
password: '비밀번호',
},
buttons: {
submit: '제출',
cancel: '취소',
save: '저장',
},
};
// i18n.ts
import en from './translations/en';
import ko from './translations/ko';
export const i18n = createI18n({
defaultLanguage: 'ko',
fallbackLanguage: 'en',
translations: { en, ko },
});Advanced Usage
Parameter Interpolation
// Single parameter
const msg = i18n.t('welcome', { name: 'John' });
// Translation: "Welcome {name}" -> "Welcome John"
// Multiple parameters
const msg2 = i18n.t('orderInfo', {
product: 'iPhone',
price: 999,
quantity: 2
});
// Translation: "{quantity} {product} for ${price}"
// -> "2 iPhone for $999"Fallback Mechanism
// If key not found in current language,
// fallback to fallbackLanguage
i18n.changeLanguage('ko');
i18n.t('missing.key'); // Returns English version if existsDynamic Language Loading
// Load translations dynamically
async function loadLanguage(lang: string) {
const translations = await import(`./translations/${lang}.ts`);
i18n.addTranslations(lang, translations.default);
await i18n.changeLanguage(lang);
}Best Practices
Use nested keys for organization:
{ auth: { login: { title: '...' } } }Consistent naming: Use lowercase with dots
'auth.login.title' // ✅ 'Auth_Login_Title' // ❌Parameter names: Use descriptive names
'Hello {userName}' // ✅ 'Hello {x}' // ❌Fallback language: Always provide English as fallback
Keep translations in sync: Ensure all languages have same keys
TypeScript Support
Full TypeScript support with type safety.
import { I18n, createI18n, Translations } from '@chain1/i18n';
const translations: Translations = {
en: { /* ... */ },
ko: { /* ... */ },
};
const i18n: I18n = createI18n({
defaultLanguage: 'en',
translations,
});License
MIT
